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