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