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