da6bde894f619c562101f1dd913ea0c49d6490e6
[hildon] / hildon-widgets / hildon-date-editor.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@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; either version 2.1 of
11  * the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful, but
14  * WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
21  * 02110-1301 USA
22  *
23  */
24
25 /* HILDON DOC
26  * @shortdesc: DateEditor is a widget for setting, getting and showing a
27  * date.
28  * @longdesc: The date editor consists of a GtkLabel that shows the current
29  * date in localized form and an icon. Clicking on either the label or the
30  * icon opens the #HildonCalendarPopup used for selecting the date.
31  * Similarly, if the editor has the focus, and space or enter key is
32  * pressed, the #HildonCalendarPopup will open. 
33  * 
34  * @seealso: #HildonTimeEditor, #HildonCalendarPopup
35  */
36
37 #include <glib.h>
38 #include <gtk/gtk.h>
39 #include <gdk/gdkkeysyms.h>
40 #include <time.h>
41 #include <stdlib.h>
42 #include <stdio.h>
43 #include <string.h>
44
45 #include <hildon-widgets/hildon-date-editor.h>
46 #include <hildon-widgets/hildon-calendar-popup.h>
47 #include <hildon-widgets/gtk-infoprint.h>
48 #include <hildon-widgets/hildon-defines.h>
49 #include <hildon-widgets/hildon-input-mode-hint.h>
50 #include "hildon-composite-widget.h"
51 #include "hildon-marshalers.h"
52
53 #ifdef HAVE_CONFIG_H
54 #include<config.h>
55 #endif
56
57 #include<libintl.h>
58 #define _(string) dgettext(PACKAGE, string)
59
60 #define ENTRY_BORDERS 11
61 #define DATE_EDITOR_HEIGHT 30
62
63 #define DAY_ENTRY_WIDTH 2
64 #define MONTH_ENTRY_WIDTH 2
65 #define YEAR_ENTRY_WIDTH 4
66
67 #define DEFAULT_MIN_YEAR 1970
68 #define DEFAULT_MAX_YEAR 2037
69
70 #define HILDON_DATE_EDITOR_GET_PRIVATE(obj) \
71         (G_TYPE_INSTANCE_GET_PRIVATE((obj),\
72         HILDON_DATE_EDITOR_TYPE, HildonDateEditorPrivate));
73
74 static GtkContainerClass *parent_class;
75
76 typedef struct _HildonDateEditorPrivate HildonDateEditorPrivate;
77
78 static void
79 hildon_date_editor_class_init(HildonDateEditorClass * editor_class);
80
81 static void hildon_date_editor_init(HildonDateEditor * editor);
82
83 static gboolean
84 hildon_date_editor_icon_press(GtkWidget * widget, GdkEventButton * event,
85                               gpointer data);
86
87 static gboolean
88 hildon_date_editor_entry_released(GtkWidget * widget,
89                                   GdkEventButton * event, gpointer data);
90 static gboolean
91 hildon_date_editor_released(GtkWidget * widget, GdkEventButton * event,
92                             gpointer data);
93
94 static gboolean
95 hildon_date_editor_keypress(GtkWidget * widget, GdkEventKey * event,
96                             gpointer data);
97
98 static gboolean
99 hildon_date_editor_keyrelease(GtkWidget * widget, GdkEventKey * event,
100                               gpointer data);
101 static void
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                                               HildonDateEditorErrorType 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_event_box_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__INT,
234                 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
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_event_box_image = gtk_event_box_new();
452     priv->calendar_icon = gtk_image_new();
453     real_set_calendar_icon_state(priv, FALSE);
454
455     apply_locale_field_order(priv);
456
457     gtk_container_add(GTK_CONTAINER(priv->frame), priv->d_box_date);
458     gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->calendar_icon);
459
460     gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
461     gtk_widget_set_parent(priv->d_event_box_image, GTK_WIDGET(editor));
462     gtk_widget_show_all(priv->frame);
463     gtk_widget_show_all(priv->d_event_box_image);
464     
465     /* image signal connects */
466     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "button_press_event",
467                      G_CALLBACK(hildon_date_editor_icon_press), editor);
468     g_signal_connect(GTK_OBJECT(priv->d_event_box_image),
469                      "button_release_event",
470                      G_CALLBACK(hildon_date_editor_released), editor);
471     g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "key-press-event",
472                      G_CALLBACK(hildon_date_editor_keypress), editor);
473     g_signal_connect(GTK_OBJECT(priv->calendar_icon), "key-press-event",
474                      G_CALLBACK(hildon_date_editor_keypress), editor);
475
476
477     /* entry signal connects */
478     g_signal_connect(GTK_OBJECT(priv->d_entry), "button_release_event",
479                      G_CALLBACK(hildon_date_editor_entry_released), editor);
480     
481     g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-in-event",
482                      G_CALLBACK(hildon_date_editor_entry_focusin), editor);
483
484     g_signal_connect(GTK_OBJECT(priv->m_entry), "button_release_event",
485                      G_CALLBACK(hildon_date_editor_entry_released), editor);
486
487     g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-in-event",
488                      G_CALLBACK(hildon_date_editor_entry_focusin), editor);
489
490     g_signal_connect(GTK_OBJECT(priv->y_entry), "button_release_event",
491                      G_CALLBACK(hildon_date_editor_entry_released), editor);
492
493     g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-in-event",
494                      G_CALLBACK(hildon_date_editor_entry_focusin), 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 hildon_date_editor_set_property (GObject *object, guint param_id,
538                                        const GValue *value, GParamSpec *pspec)
539 {
540   HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
541   HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
542   gint val;
543
544   switch (param_id)
545   {
546     case PROP_YEAR:
547       hildon_date_editor_set_year (editor, g_value_get_uint(value));
548       break;
549
550     case PROP_MONTH:
551       hildon_date_editor_set_month (editor, g_value_get_uint(value));
552       break;
553
554     case PROP_DAY:
555       hildon_date_editor_set_day (editor, g_value_get_uint(value));
556       break;
557
558     case PROP_MIN_YEAR:
559       val = g_value_get_uint(value);
560       if (val <= priv->max_year)
561         {
562           priv->min_year = val;
563           /* Clamp current year */
564           if (hildon_date_editor_get_year (editor) < priv->min_year)
565             hildon_date_editor_set_year (editor, priv->min_year);
566         }
567       else
568         g_warning("min-year cannot be greater than max-year");
569       break;
570
571     case PROP_MAX_YEAR:
572       val = g_value_get_uint(value);
573       if (val >= priv->min_year)
574         {
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         }
580       else
581         g_warning("max-year cannot be less than min-year");
582       break;
583
584     default:
585       G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
586       break;
587   }
588 }
589
590 static void hildon_date_editor_get_property( GObject *object, guint param_id,
591                                          GValue *value, 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
638     if (include_internals) {
639         (*callback) (priv->frame, callback_data);
640         (*callback) (priv->d_event_box_image, callback_data);
641     }
642 }
643
644 static void hildon_date_editor_destroy(GtkObject * self)
645 {
646     HildonDateEditorPrivate *priv;
647
648     priv = HILDON_DATE_EDITOR_GET_PRIVATE(self);
649
650     if (priv->frame) {
651         gtk_widget_unparent(priv->frame);
652         priv->frame = NULL;
653     }
654     if (priv->d_event_box_image) {
655         gtk_widget_unparent(priv->d_event_box_image);
656         priv->d_event_box_image = NULL;
657     }
658     if (priv->delims) {
659         g_list_free(priv->delims);
660         priv->delims = NULL;
661     }
662
663     if (GTK_OBJECT_CLASS(parent_class)->destroy)
664         GTK_OBJECT_CLASS(parent_class)->destroy(self);
665 }
666
667 /**
668  * hildon_date_editor_new:
669  *
670  * Creates a new time editor. The current system date
671  * is shown in the editor.
672  *
673  * Return value: Pointer to a new @HildonDateEditor widget.
674  **/
675 GtkWidget *hildon_date_editor_new(void)
676 {
677     return GTK_WIDGET(g_object_new(HILDON_DATE_EDITOR_TYPE, NULL));
678 }
679
680 /**
681  * hildon_date_editor_set_date:
682  * @date: the @HildonDateEditor widget
683  * @year: year
684  * @month: month
685  * @day: day
686  *
687  * Sets the date shown in the editor. 
688  **/
689 void hildon_date_editor_set_date(HildonDateEditor * editor,
690                                  guint year, guint month, guint day)
691 {
692     HildonDateEditorPrivate *priv;
693
694     g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
695
696     /* This function cannot be implemented by calling
697        component setters, since applying the individual
698        values one by one can make the date temporarily
699        invalid (depending on what the previous values were), 
700        which in turn causes that the desired date
701        is not set (even though it's valid). We must set all the
702        components at one go and not try to do any validation etc
703        there in between. */
704
705     g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
706     priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
707
708     if (g_date_valid_dmy(day, month, year))
709     {
710         GDate date;
711         gchar buffer[256];
712         
713         priv->year = year;
714         priv->month = month;
715         priv->day = day;
716         
717         g_date_set_dmy(&date, day, month, year);
718
719         /* We apply the new values, but do not want automatic focus move
720            etc to take place */
721         g_snprintf(buffer, sizeof(buffer), "%04d", year);
722         g_signal_handlers_block_by_func(priv->y_entry, 
723             (gpointer) hildon_date_editor_entry_changed, editor);
724         gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
725         g_signal_handlers_unblock_by_func(priv->y_entry, 
726             (gpointer) hildon_date_editor_entry_changed, editor);
727
728         g_date_strftime(buffer, sizeof(buffer), "%m", &date);
729         g_signal_handlers_block_by_func(priv->m_entry, 
730             (gpointer) hildon_date_editor_entry_changed, editor);
731         gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
732         g_signal_handlers_unblock_by_func(priv->m_entry, 
733             (gpointer) hildon_date_editor_entry_changed, editor);
734
735         g_date_strftime(buffer, sizeof(buffer), "%d", &date);
736         g_signal_handlers_block_by_func(priv->d_entry, 
737             (gpointer) hildon_date_editor_entry_changed, editor);
738         gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
739         g_signal_handlers_unblock_by_func(priv->d_entry, 
740             (gpointer) hildon_date_editor_entry_changed, editor);
741   
742         g_object_notify(G_OBJECT(editor), "year");
743         g_object_notify(G_OBJECT(editor), "month");
744         g_object_notify(G_OBJECT(editor), "day");
745     }
746 }
747
748 /**
749  * hildon_date_editor_get_date:
750  * @date: the @HildonDateEditor widget
751  * @year: year
752  * @month: month
753  * @day: day
754  *
755  * Returns the year, month, and day currently on the
756  * date editor.
757  **/
758 void hildon_date_editor_get_date(HildonDateEditor * date,
759                                  guint * year, guint * month, guint * day)
760 {
761     HildonDateEditorPrivate *priv;
762
763     g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
764     g_return_if_fail(year);
765     g_return_if_fail(month);
766     g_return_if_fail(day);
767
768     priv = HILDON_DATE_EDITOR_GET_PRIVATE(date);
769
770     /* FIXME: The role of priv->{day,month,year} members vs. entry contents
771               is unclear. They do not neccesarily match and still the texts are
772               used as return values and members for some internal validation!!
773               At least a partly reason is to allow empty text to become
774               0 return value, while members are restricted to valid ranges?!
775               However, if we change the current way, we are likely to break 
776               some applications  if they rely on some specific way how this 
777               widget currently handles empty values and temporarily invalid values.
778
779               The key issue is this: What should the _get methods return while
780               user is editing a field and the result is incomplete. The
781               partial result? The last good result? If we return partial result
782               we also need a way to inform if the date is not valid. Current
783               implementation is some kind of hybrid of these two...
784
785               for example:
786                  hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
787
788               easily fails, since set_day tries to force validity while get_day
789               doesn't do that.
790
791               Proposal: Always return the same values that are shown in the
792                         fields. We add a separate flag (Or use GDate) to 
793                         indicate if the current date is valid. This would allow 
794                         setters to make the date invalid as well.
795     */
796     *year = /*priv->year;*/
797       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
798     *month = /*priv->month;*/
799       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
800     *day = /*priv->day;*/ 
801       (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
802 }
803
804 static gboolean hildon_date_editor_icon_press(GtkWidget * widget,
805                                               GdkEventButton * event,
806                                               gpointer data)
807 {
808     g_assert(GTK_IS_WIDGET(widget));
809     g_assert(event != NULL);
810     g_assert(HILDON_IS_DATE_EDITOR(data));
811
812     if (event->button == 1)
813        hildon_date_editor_set_calendar_icon_state(HILDON_DATE_EDITOR(data), TRUE);
814
815     return FALSE;
816 }
817
818 static gboolean hildon_date_editor_entry_released(GtkWidget * widget,
819                                                   GdkEventButton * event,
820                                                   gpointer data)
821 {
822     HildonDateEditor *ed;
823     HildonDateEditorPrivate *priv;
824
825     ed = HILDON_DATE_EDITOR(data);
826     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
827
828     if (event->button == 1) {
829         /* We might not get focus because of invalid values in entries */
830         if (GTK_WIDGET_HAS_FOCUS(widget))
831                         g_idle_add((GSourceFunc)
832                                         _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
833     }
834
835     return FALSE;
836 }
837
838 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
839                                                  GdkEventFocus * event,
840                                                  gpointer data)
841 {
842     if (!GTK_ENTRY(widget)->button)
843     {
844                 g_idle_add((GSourceFunc)
845                                 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
846     }
847
848     return FALSE;
849 }
850
851
852 static void popup_calendar_dialog(HildonDateEditor *ed)
853 {
854     guint y = 0, m = 0, d = 0;
855     GtkWidget *popup;
856     GtkWidget *parent;
857     guint result;
858     GValue val = {0, };
859
860     hildon_date_editor_get_date(ed, &y, &m, &d);
861
862     parent = gtk_widget_get_ancestor(GTK_WIDGET(ed), GTK_TYPE_WINDOW);
863     popup = hildon_calendar_popup_new(GTK_WINDOW(parent), y, m, d);
864
865     g_value_init(&val, G_TYPE_INT);
866     /* Set max/min year in calendar popup to date editor values */
867     g_object_get_property(G_OBJECT(ed), "min-year", &val);
868     g_object_set_property(G_OBJECT(popup), "min-year", &val);
869     g_object_get_property(G_OBJECT(ed), "max-year", &val);
870     g_object_set_property(G_OBJECT(popup), "max-year", &val);
871
872     /* Pop up calendar */
873     result = gtk_dialog_run(GTK_DIALOG(popup));
874     switch (result) {
875     case GTK_RESPONSE_OK:
876     case GTK_RESPONSE_ACCEPT:
877         hildon_calendar_popup_get_date(HILDON_CALENDAR_POPUP(popup), &y,
878                                        &m, &d);
879         hildon_date_editor_set_date(ed, y, m, d);
880     }
881
882     gtk_widget_destroy(popup);
883 }
884
885 /* button released */
886 static gboolean hildon_date_editor_released(GtkWidget * widget,
887                                             GdkEventButton * event,
888                                             gpointer data)
889 {
890     HildonDateEditor *ed;
891
892     g_assert(GTK_IS_WIDGET(widget));
893     g_assert(event != NULL);
894     g_assert(HILDON_IS_DATE_EDITOR(data));
895
896     ed = HILDON_DATE_EDITOR(data);
897
898     if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
899         popup_calendar_dialog(ed);
900
901     return FALSE;
902 }
903
904 /* This is called whenever some editor filed loses the focus and
905    when the all of the fields are filled. 
906    Earlier this was called whenever an entry changed */
907 /* FIXME: Validation on focus_out is broken by concept */
908 static void
909 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data)
910 {
911     HildonDateEditor *ed;
912     HildonDateEditorPrivate *priv;
913     gint d, m, y, max_days;
914     gboolean r;  /* temp return values for signals */
915     const gchar *text;        
916     gint error_code = NO_ERROR;
917
918     g_assert(HILDON_IS_DATE_EDITOR(data));
919     g_assert(GTK_IS_ENTRY(widget));
920
921     ed = HILDON_DATE_EDITOR(data);
922     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
923
924     if (priv->skip_validation)
925        return;
926
927     /*check if the calling entry is empty*/
928     text = gtk_entry_get_text(GTK_ENTRY(widget));
929     if(text == NULL || text[0] == 0)
930     {
931       if (widget == priv->d_entry)
932          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_DAY, &r);
933       else if(widget == priv->m_entry)
934          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_MONTH, &r);
935       else
936          g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_YEAR, &r);
937
938       return;
939     }
940
941     /* Ok, we now check validity. Some fields can be empty */
942     text = gtk_entry_get_text(GTK_ENTRY(priv->d_entry));
943     if (text == NULL || text[0] == 0) return;
944     d = atoi(text);
945     text = gtk_entry_get_text(GTK_ENTRY(priv->m_entry));
946     if (text == NULL || text[0] == 0) return;
947     m = atoi(text);
948     text = gtk_entry_get_text(GTK_ENTRY(priv->y_entry));
949     if (text == NULL || text[0] == 0) return;
950     y = atoi(text);
951
952     /* Did it actually change? */
953     if (d != priv->day || m != priv->month || y != priv->year)
954     {
955         /* We could/should use hildon_date_editor_set_year and such functions
956          * to set the date, instead of use gtk_entry_set_text, and then change
957          * the priv member but hildon_date_editor_set_year and such functions
958          * check if the date is valid, we do want to do date validation check
959          * here according to spec */
960
961         /* Validate month */
962         if(widget == priv->m_entry) {
963             if(m < 1) {
964                 error_code = MIN_MONTH;
965                 m = 1;
966             }
967             else if (m > 12) {
968                 error_code = MAX_MONTH;
969                 m = 12;
970             }
971         }
972
973         /* Validate year */
974         if(widget == priv->y_entry) {
975             if (y < priv->min_year) {
976                 error_code = MIN_YEAR;
977                 y = priv->min_year;
978             }
979             else if (y > priv->max_year) {
980                 error_code = MAX_YEAR;
981                 y = priv->max_year;
982             }
983         }
984
985         /* Validate day. We have to do this in every case, since
986            changing month or year can make the day number to be invalid */
987         max_days = g_date_get_days_in_month(m,y);
988         if(d < 1) {
989            error_code = MIN_DAY;
990            d = 1;
991         }
992         else if (d > max_days) {
993            /* Report range error or invalid date */
994            error_code = d > 31 ? MAX_DAY : INVALID_DATE;
995            d = max_days;
996            widget = priv->d_entry; /* Note! Change param to point day entry */
997         }
998
999         if (error_code != NO_ERROR)
1000         {
1001             g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1002
1003             g_idle_add ((GSourceFunc) 
1004                     _hildon_date_editor_entry_select_all, 
1005                     widget);
1006
1007             /* Set focus back to invalid entry */
1008             gtk_widget_grab_focus(widget);
1009         }
1010     }
1011
1012     /* Fix and reformat the date after error signal is processed. 
1013        reformatting can be needed even in a such case that numerical
1014        values of the date components are the same as earlier. */
1015     hildon_date_editor_set_date(ed, y, m, d);
1016 }
1017
1018 /* When entry becomes full, we move the focus to the next field.
1019    If we are on the last field, the whole contents are validated. */
1020 static void
1021 hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
1022 {
1023   GtkEntry *entry;
1024
1025   g_assert(GTK_IS_ENTRY(ed));
1026   g_assert(HILDON_IS_DATE_EDITOR(data));
1027
1028   entry = GTK_ENTRY(ed);
1029
1030   /* If day entry is full, move to next entry or validate */
1031   if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
1032     {
1033       if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
1034         hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
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
1073     g_assert(HILDON_IS_DATE_EDITOR(data));
1074     g_assert(GTK_IS_ENTRY(widget));
1075
1076     ed = HILDON_DATE_EDITOR(data);
1077     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1078     pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1079         
1080     switch (event->keyval) {
1081     case GDK_Left:
1082         (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
1083         return TRUE;
1084         break;
1085     case GDK_Right:
1086         (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
1087         return TRUE;
1088         break;
1089     case GDK_Return:
1090     case GDK_ISO_Enter:
1091         /* Ignore return value, since we want to handle event at all times.
1092            otherwise vkb would popup when the keyrepeat starts. */
1093         (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
1094         return TRUE;
1095
1096     case GDK_Escape:
1097         priv->skip_validation = TRUE;
1098         break;
1099     default:
1100         break;
1101     }
1102
1103     return FALSE;
1104 }
1105
1106 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
1107                                              GdkEventFocus * event,
1108                                              gpointer data)
1109 {
1110   hildon_date_editor_entry_validate(widget, data);
1111   return FALSE;
1112 }
1113
1114 static gboolean 
1115 hildon_date_editor_date_error(HildonDateEditor *editor,
1116                               HildonDateEditorErrorType type)
1117 {
1118   HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1119
1120   switch(type)
1121     {
1122     case MAX_DAY:
1123       gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 31);
1124       break;
1125     case MAX_MONTH:
1126       gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 12);
1127       break;
1128     case MAX_YEAR:
1129       gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), priv->max_year);
1130       break;
1131     case MIN_DAY:
1132     case MIN_MONTH:
1133       gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), 1);
1134       break;
1135     case MIN_YEAR:
1136       gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), priv->min_year);
1137       break;
1138     case EMPTY_DAY:
1139       gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 31);
1140       break;
1141     case EMPTY_MONTH:
1142       gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 12);
1143       break;
1144     case EMPTY_YEAR:
1145       gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"),
1146                      priv->min_year, priv->max_year);
1147       break;
1148     case INVALID_DATE:
1149       gtk_infoprint(NULL, _("Ckct_ib_date_does_not_exist"));
1150       break;
1151    default:
1152       /*default error message ?*/
1153       break;
1154     }
1155   return TRUE;
1156 }
1157
1158 static void hildon_date_editor_size_request(GtkWidget * widget,
1159                                             GtkRequisition * requisition)
1160 {
1161     HildonDateEditor *ed;
1162     HildonDateEditorPrivate *priv;
1163     GtkRequisition f_req, img_req;
1164
1165     g_assert(GTK_IS_WIDGET(widget));
1166     g_assert(requisition != NULL);
1167
1168     ed = HILDON_DATE_EDITOR(widget);
1169     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1170
1171     /* Our own children affect our size */
1172     gtk_widget_size_request(priv->frame, &f_req);
1173     gtk_widget_size_request(priv->d_event_box_image, &img_req);
1174
1175     /* calculate our size */
1176     requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1177
1178     /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1179               doing so would break current pixel specifications, since
1180               the text entry by itself is already 30px tall + then frame takes
1181               something */
1182     requisition->height = DATE_EDITOR_HEIGHT;
1183 }
1184
1185 static void hildon_date_editor_size_allocate(GtkWidget * widget,
1186                                              GtkAllocation * allocation)
1187 {
1188     HildonDateEditor *ed;
1189     HildonDateEditorPrivate *priv;
1190     GtkAllocation f_alloc, img_alloc;
1191     GtkRequisition req;
1192     GtkRequisition max_req;
1193     GList *iter;
1194
1195     g_assert(GTK_IS_WIDGET(widget));
1196     g_assert(allocation != NULL);
1197
1198     ed = HILDON_DATE_EDITOR(widget);
1199     priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1200
1201     widget->allocation = *allocation;
1202
1203     gtk_widget_get_child_requisition(widget, &max_req);
1204
1205     /* Center vertically */
1206     f_alloc.y = img_alloc.y = allocation->y +
1207             MAX(allocation->height - max_req.height, 0) / 2;
1208
1209     /* Center horizontally */
1210     f_alloc.x = img_alloc.x = allocation->x +
1211             MAX(allocation->width - max_req.width, 0) / 2;
1212
1213     /* allocate frame */
1214         if (GTK_WIDGET_VISIBLE(priv->frame)) {
1215             gtk_widget_get_child_requisition(priv->frame, &req);
1216
1217             f_alloc.width = req.width;
1218             f_alloc.height = max_req.height;
1219             gtk_widget_size_allocate(priv->frame, &f_alloc);
1220         }
1221
1222     /* allocate icon */
1223         if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
1224             gtk_widget_get_child_requisition(priv->d_event_box_image,
1225                                              &req);
1226
1227             img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1228             img_alloc.width = req.width;
1229             img_alloc.height = max_req.height;
1230             gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
1231         }
1232
1233     /* FIXME: We really should not alloc delimeters by hand (since they
1234               are not our own children, but we need to force to appear 
1235               higher. This ugly hack is needed to compensate the forced
1236               height in size_request. */
1237     for (iter = priv->delims; iter; iter = iter->next)
1238     {
1239         GtkWidget *delim;
1240         GtkAllocation alloc;
1241
1242         delim = GTK_WIDGET(iter->data);
1243         alloc = delim->allocation;
1244         alloc.height = max_req.height; 
1245         alloc.y = priv->d_entry->allocation.y - 2;
1246
1247         gtk_widget_size_allocate(delim, &alloc);
1248     }
1249 }
1250
1251 /**
1252  * hildon_date_editor_set_year:
1253  * @editor: the @HildonDateEditor widget
1254  * @year: year
1255  *
1256  * Sets the year shown in the editor. 
1257  *
1258  * Return: Returns TRUE if the year is valid.
1259  **/
1260 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
1261 {
1262   HildonDateEditorPrivate *priv;
1263   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1264   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1265
1266   if (g_date_valid_dmy(priv->day, priv->month, year))
1267   {
1268     gchar buffer[256];
1269     priv->year = year;
1270
1271     g_snprintf(buffer, sizeof(buffer), "%04d", year);
1272
1273     /* We apply the new day, but do not want automatic focus move
1274        etc to take place */
1275     g_signal_handlers_block_by_func(priv->y_entry, 
1276         (gpointer) hildon_date_editor_entry_changed, editor);
1277     gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
1278     g_signal_handlers_unblock_by_func(priv->y_entry, 
1279         (gpointer) hildon_date_editor_entry_changed, editor);
1280
1281     g_object_notify(G_OBJECT(editor), "year");
1282     return TRUE;
1283   }
1284
1285   return FALSE;
1286 }
1287
1288 /**
1289  * hildon_date_editor_set_month:
1290  * @editor: the @HildonDateEditor widget
1291  * @month: month
1292  *
1293  * Sets the month shown in the editor. 
1294  *
1295  * Return: Returns TRUE if the month is valid.
1296  **/
1297 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
1298 {
1299   HildonDateEditorPrivate *priv;
1300   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1301   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1302
1303   if (g_date_valid_dmy(priv->day, month, priv->year))
1304   {
1305     GDate date;
1306     gchar buffer[256];
1307
1308     priv->month = month;
1309     g_date_set_dmy(&date, priv->day, month, priv->year);
1310     g_date_strftime(buffer, sizeof(buffer), "%m", &date);
1311
1312     /* We apply the new day, but do not want automatic focus move
1313        etc to take place */
1314     g_signal_handlers_block_by_func(priv->m_entry, 
1315         (gpointer) hildon_date_editor_entry_changed, editor);
1316     gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
1317     g_signal_handlers_unblock_by_func(priv->m_entry, 
1318         (gpointer) hildon_date_editor_entry_changed, editor);
1319
1320     g_object_notify(G_OBJECT(editor), "month");
1321     return TRUE;
1322   }
1323   return FALSE;
1324 }
1325
1326 /**
1327  * hildon_date_editor_set_day:
1328  * @editor: the @HildonDateEditor widget
1329  * @day: day
1330  *
1331  * Sets the day shown in the editor. 
1332  *
1333  * Return: Returns TRUE if the day is valid.
1334  **/
1335 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
1336 {
1337   HildonDateEditorPrivate *priv;
1338
1339   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1340   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1341
1342   if (g_date_valid_dmy(day, priv->month, priv->year))
1343   {
1344     GDate date;
1345     gchar buffer[256];
1346
1347     priv->day = day;
1348     g_date_set_dmy(&date, day, priv->month, priv->year);
1349     g_date_strftime(buffer, sizeof(buffer), "%d", &date);
1350
1351     /* We apply the new day, but do not want automatic focus move
1352        etc to take place */
1353     g_signal_handlers_block_by_func(priv->d_entry, 
1354         (gpointer) hildon_date_editor_entry_changed, editor);
1355     gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
1356     g_signal_handlers_unblock_by_func(priv->d_entry, 
1357         (gpointer) hildon_date_editor_entry_changed, editor);
1358
1359     g_object_notify(G_OBJECT(editor), "day");
1360     return TRUE;
1361   }
1362   return FALSE;
1363 }
1364
1365 /**
1366  * hildon_date_editor_get_year:
1367  * @editor: the @HildonDateEditor widget
1368  *
1369  * Gets the year shown in the editor. 
1370  *
1371  * Return: Returns the current year shown in the editor.
1372  **/
1373 guint hildon_date_editor_get_year(HildonDateEditor *editor)
1374 {
1375   HildonDateEditorPrivate *priv;
1376   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1377   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1378   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
1379 }
1380
1381 /**
1382  * hildon_date_editor_get_month:
1383  * @editor: the @HildonDateEditor widget
1384  *
1385  * Gets the month shown in the editor. 
1386  *
1387  * Return: Returns the current month shown in the editor.
1388  **/
1389
1390 guint hildon_date_editor_get_month(HildonDateEditor *editor)
1391 {
1392   HildonDateEditorPrivate *priv;
1393   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1394   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1395   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
1396 }
1397
1398 /**
1399  * hildon_date_editor_get_day:
1400  * @editor: the @HildonDateEditor widget
1401  *
1402  * Gets the day shown in the editor. 
1403  *
1404  * Return: Returns the current day shown in the editor.
1405  **/
1406
1407 guint hildon_date_editor_get_day(HildonDateEditor *editor)
1408 {
1409   HildonDateEditorPrivate *priv;
1410   g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1411   priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1412   return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
1413 }
1414
1415 /* Idle callback */
1416 static gboolean
1417 _hildon_date_editor_entry_select_all (GtkWidget *widget)
1418 {
1419         gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1420         return FALSE;
1421 }