2 * This file is a part of hildon
4 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
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
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.
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
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
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.
40 #include "hildon-date-editor.h"
43 #include <gdk/gdkkeysyms.h>
48 #include "hildon-calendar-popup.h"
49 #include "hildon-defines.h"
50 #include "hildon-input-mode-hint.h"
51 #include "hildon-private.h"
52 #include "hildon-marshalers.h"
53 #include "hildon-enum-types.h"
54 #include "hildon-time-editor.h"
55 #include "hildon-banner.h"
57 #include "hildon-date-editor-private.h"
59 #define _(string) dgettext("hildon-libs", string)
61 #define c_(string) dgettext("hildon-common-strings", string)
63 #define ENTRY_BORDERS 11
65 #define DATE_EDITOR_HEIGHT 30
67 #define DAY_ENTRY_WIDTH 2
69 #define MONTH_ENTRY_WIDTH 2
71 #define YEAR_ENTRY_WIDTH 4
73 #define DEFAULT_MIN_YEAR 1970
75 #define DEFAULT_MAX_YEAR 2037
77 static GtkContainerClass* parent_class;
80 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
83 hildon_date_editor_init (HildonDateEditor *editor);
86 hildon_date_editor_icon_press (GtkWidget *widget,
90 hildon_date_editor_released (GtkWidget *widget,
94 hildon_date_editor_keypress (GtkWidget *widget,
99 hildon_date_editor_keyrelease (GtkWidget *widget,
103 hildon_date_editor_clicked (GtkWidget *widget,
107 hildon_date_editor_entry_validate (GtkWidget *widget,
111 hildon_date_editor_entry_changed (GtkEditable *widget,
115 hildon_date_editor_entry_focus_out (GtkWidget *widget,
116 GdkEventFocus *event,
120 hildon_date_editor_date_error (HildonDateEditor *editor,
121 HildonDateTimeError type);
124 hildon_date_editor_entry_focus_in (GtkWidget *widget,
125 GdkEventFocus *event,
129 hildon_date_editor_get_property (GObject *object,
135 hildon_date_editor_set_property (GObject *object,
140 hildon_child_forall (GtkContainer *container,
141 gboolean include_internals,
142 GtkCallback callback,
143 gpointer callback_data);
146 hildon_date_editor_destroy (GtkObject *self);
149 hildon_date_editor_size_allocate (GtkWidget *widget,
150 GtkAllocation *allocation);
153 hildon_date_editor_size_request (GtkWidget *widget,
154 GtkRequisition *requisition);
157 hildon_date_editor_entry_select_all (GtkWidget *widget);
159 /* Property indices */
176 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
179 hildon_date_editor_get_type (void)
181 static GType editor_type = 0;
184 static const GTypeInfo editor_info = {
185 sizeof (HildonDateEditorClass),
186 NULL, /* base_init */
187 NULL, /* base_finalize */
188 (GClassInitFunc) hildon_date_editor_class_init,
189 NULL, /* class_finalize */
190 NULL, /* class_data */
191 sizeof (HildonDateEditor),
193 (GInstanceInitFunc) hildon_date_editor_init,
195 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
204 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
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);
210 parent_class = g_type_class_peek_parent (editor_class);
212 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
214 gobject_class->set_property = hildon_date_editor_set_property;
215 gobject_class->get_property = hildon_date_editor_get_property;
216 widget_class->size_request = hildon_date_editor_size_request;
217 widget_class->size_allocate = hildon_date_editor_size_allocate;
218 widget_class->focus = hildon_private_composite_focus;
220 container_class->forall = hildon_child_forall;
221 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
223 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
225 date_editor_signals[DATE_ERROR] =
226 g_signal_new ("date-error",
227 G_OBJECT_CLASS_TYPE (gobject_class),
229 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
230 g_signal_accumulator_true_handled, NULL,
231 _hildon_marshal_BOOLEAN__ENUM,
232 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
235 * HildonDateEditor:year:
239 g_object_class_install_property (gobject_class, PROP_YEAR,
240 g_param_spec_uint ("year",
245 G_PARAM_READABLE | G_PARAM_WRITABLE));
248 * HildonDateEditor:month:
252 g_object_class_install_property (gobject_class, PROP_MONTH,
253 g_param_spec_uint ("month",
258 G_PARAM_READABLE | G_PARAM_WRITABLE));
261 * HildonDateEditor:day:
265 g_object_class_install_property (gobject_class, PROP_DAY,
266 g_param_spec_uint ("day",
271 G_PARAM_READABLE | G_PARAM_WRITABLE));
274 * HildonDateEditor:min-year:
276 * Minimum valid year.
278 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
279 g_param_spec_uint ("min-year",
280 "Minimum valid year",
281 "Minimum valid year",
287 * HildonDateEditor:max-year:
289 * Maximum valid year.
291 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
292 g_param_spec_uint ("max-year",
293 "Maximum valid year",
294 "Maximum valid year",
300 /* Forces setting of the icon to certain state. Used initially
301 and from the actual setter function */
303 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
308 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
309 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_WIDG);
311 priv->calendar_icon_pressed = pressed;
314 /* Sets the icon to given state (normal/pressed). Returns
315 info if the state actually changed. */
317 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
320 HildonDateEditorPrivate *priv;
322 g_assert (HILDON_IS_DATE_EDITOR (editor));
324 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
327 if (pressed != priv->calendar_icon_pressed) {
328 real_set_calendar_icon_state (priv, pressed);
335 /* Packing day, month and year entries depend on locale settings
336 We find out the order and all separators by converting a known
337 date to default format and inspecting the result string */
339 apply_locale_field_order (HildonDateEditorPrivate *priv)
341 GDate locale_test_date;
344 gchar *iter, *delim_text;
346 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
347 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
355 /* Try to convert the current location into number. */
356 value = strtoul (iter, &endp, 10);
358 /* If the conversion didn't progress or the detected value was
359 unknown (we used a fixed date, you remember), we treat
360 current position as a literal */
364 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
365 priv->d_entry, FALSE, FALSE, 0);
369 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
370 priv->m_entry, FALSE, FALSE, 0);
373 case 70: /* %x format uses only 2 numbers for some locales */
375 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
376 priv->y_entry, FALSE, FALSE, 0);
380 /* All non-number characters starting from current position
381 form the delimeter */
382 for (endp = iter; *endp; endp++)
383 if (g_ascii_isdigit (*endp))
386 /* Now endp points one place past the delimeter text */
387 delim_text = g_strndup (iter, endp - iter);
388 delim = gtk_label_new (delim_text);
389 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
390 delim, FALSE, FALSE, 0);
392 priv->delims = g_list_append (priv->delims, delim);
403 hildon_date_editor_init (HildonDateEditor *editor)
405 HildonDateEditorPrivate *priv;
408 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
411 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
413 gtk_widget_push_composite_child ();
415 /* initialize values */
416 g_date_clear (&cur_date, 1);
417 g_date_set_time (&cur_date, time (NULL));
419 priv->day = g_date_get_day (&cur_date);
420 priv->month = g_date_get_month (&cur_date);
421 priv->year = g_date_get_year (&cur_date);
422 priv->min_year = DEFAULT_MIN_YEAR;
423 priv->max_year = DEFAULT_MAX_YEAR;
426 priv->frame = gtk_frame_new (NULL);
427 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
429 priv->d_entry = gtk_entry_new ();
430 priv->m_entry = gtk_entry_new ();
431 priv->y_entry = gtk_entry_new ();
433 g_object_set (G_OBJECT(priv->d_entry), "input-mode",
434 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
435 g_object_set (G_OBJECT(priv->m_entry), "input-mode",
436 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
437 g_object_set (G_OBJECT(priv->y_entry), "input-mode",
438 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
441 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
442 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
443 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
445 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
446 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
447 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
449 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
450 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
451 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
453 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
454 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
455 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
457 priv->d_box_date = gtk_hbox_new (FALSE, 0);
459 priv->d_button_image = gtk_button_new ();
460 priv->calendar_icon = gtk_image_new ();
461 real_set_calendar_icon_state (priv, FALSE);
462 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
464 apply_locale_field_order (priv);
466 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
467 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
468 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
469 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
471 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
472 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
473 gtk_widget_show_all (priv->frame);
474 gtk_widget_show_all (priv->d_button_image);
476 /* image button signal connects */
477 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
478 G_CALLBACK (hildon_date_editor_icon_press), editor);
479 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
480 G_CALLBACK (hildon_date_editor_released), editor);
481 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
482 G_CALLBACK (hildon_date_editor_clicked), editor);
483 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
484 G_CALLBACK (hildon_date_editor_keypress), editor);
486 /* entry signal connects */
487 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
488 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
490 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
491 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
493 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
494 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
496 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
497 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
499 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
500 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
502 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
503 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
505 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
506 G_CALLBACK (hildon_date_editor_keypress), editor);
508 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
509 G_CALLBACK (hildon_date_editor_keypress), editor);
511 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
512 G_CALLBACK (hildon_date_editor_keypress), editor);
514 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
515 G_CALLBACK (hildon_date_editor_keyrelease), editor);
517 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
518 G_CALLBACK(hildon_date_editor_keyrelease), editor);
520 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
521 G_CALLBACK (hildon_date_editor_keyrelease), editor);
523 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
525 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
526 G_CALLBACK (hildon_date_editor_entry_changed), editor);
528 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
529 G_CALLBACK (hildon_date_editor_entry_changed), editor);
531 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
532 G_CALLBACK (hildon_date_editor_entry_changed), editor);
534 gtk_widget_pop_composite_child ();
538 hildon_date_editor_set_property (GObject *object,
543 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
544 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
552 hildon_date_editor_set_year (editor, g_value_get_uint (value));
556 hildon_date_editor_set_month (editor, g_value_get_uint (value));
560 hildon_date_editor_set_day (editor, g_value_get_uint (value));
564 val = g_value_get_uint (value);
565 g_return_if_fail (val > priv->max_year);
566 priv->min_year = val;
567 /* Clamp current year */
568 if (hildon_date_editor_get_year (editor) < priv->min_year)
569 hildon_date_editor_set_year (editor, priv->min_year);
573 val = g_value_get_uint (value);
574 g_return_if_fail (val < priv->min_year);
575 priv->max_year = val;
576 /* Clamp current year */
577 if (hildon_date_editor_get_year (editor) > priv->max_year)
578 hildon_date_editor_set_year (editor, priv->max_year);
582 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
588 hildon_date_editor_get_property (GObject *object,
593 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
594 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
599 g_value_set_uint (value, hildon_date_editor_get_year (editor));
603 g_value_set_uint (value, hildon_date_editor_get_month (editor));
607 g_value_set_uint (value, hildon_date_editor_get_day (editor));
611 g_value_set_uint (value, priv->min_year);
615 g_value_set_uint (value, priv->max_year);
619 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
624 static void hildon_child_forall (GtkContainer *container,
625 gboolean include_internals,
626 GtkCallback callback,
627 gpointer callback_data)
629 HildonDateEditor *editor;
630 HildonDateEditorPrivate *priv;
632 g_assert (HILDON_IS_DATE_EDITOR (container));
635 editor = HILDON_DATE_EDITOR (container);
636 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
639 if (include_internals) {
640 (*callback) (priv->frame, callback_data);
641 (*callback) (priv->d_button_image, callback_data);
646 hildon_date_editor_destroy (GtkObject *self)
648 HildonDateEditorPrivate *priv;
650 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
654 gtk_widget_unparent (priv->frame);
657 if (priv->d_button_image) {
658 gtk_widget_unparent (priv->d_button_image);
659 priv->d_button_image = NULL;
662 g_list_free (priv->delims);
666 if (GTK_OBJECT_CLASS (parent_class)->destroy)
667 GTK_OBJECT_CLASS (parent_class)->destroy (self);
671 * hildon_date_editor_new:
673 * Creates a new date editor. The current system date
674 * is shown in the editor.
676 * Returns: pointer to a new @HildonDateEditor widget.
679 hildon_date_editor_new (void)
681 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
685 * hildon_date_editor_set_date:
686 * @date: the @HildonDateEditor widget
691 * Sets the date shown in the editor.
694 hildon_date_editor_set_date (HildonDateEditor *editor,
699 HildonDateEditorPrivate *priv;
701 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
703 /* This function cannot be implemented by calling
704 component setters, since applying the individual
705 values one by one can make the date temporarily
706 invalid (depending on what the previous values were),
707 which in turn causes that the desired date
708 is not set (even though it's valid). We must set all the
709 components at one go and not try to do any validation etc
712 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
715 if (g_date_valid_dmy (day, month, year))
724 g_date_set_dmy (&date, day, month, year);
726 /* We apply the new values, but do not want automatic focus move
728 g_snprintf (buffer, sizeof (buffer), "%04d", year);
729 g_signal_handlers_block_by_func (priv->y_entry,
730 (gpointer) hildon_date_editor_entry_changed, editor);
731 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
732 g_signal_handlers_unblock_by_func (priv->y_entry,
733 (gpointer) hildon_date_editor_entry_changed, editor);
735 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
736 g_signal_handlers_block_by_func (priv->m_entry,
737 (gpointer) hildon_date_editor_entry_changed, editor);
738 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
739 g_signal_handlers_unblock_by_func (priv->m_entry,
740 (gpointer) hildon_date_editor_entry_changed, editor);
742 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
743 g_signal_handlers_block_by_func (priv->d_entry,
744 (gpointer) hildon_date_editor_entry_changed, editor);
745 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
746 g_signal_handlers_unblock_by_func (priv->d_entry,
747 (gpointer) hildon_date_editor_entry_changed, editor);
749 g_object_notify (G_OBJECT (editor), "year");
750 g_object_notify (G_OBJECT (editor), "month");
751 g_object_notify (G_OBJECT (editor), "day");
756 * hildon_date_editor_get_date:
757 * @date: the @HildonDateEditor widget
762 * Returns: the year, month, and day currently on the
763 * date editor. You can pass NULL to any of the pointers if
764 * you're not interested in obtaining it.
767 hildon_date_editor_get_date (HildonDateEditor *date,
772 HildonDateEditorPrivate *priv;
774 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
776 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
778 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
779 is unclear. They do not neccesarily match and still the texts are
780 used as return values and members for some internal validation!!
781 At least a partly reason is to allow empty text to become
782 0 return value, while members are restricted to valid ranges?!
783 However, if we change the current way, we are likely to break
784 some applications if they rely on some specific way how this
785 widget currently handles empty values and temporarily invalid values.
787 The key issue is this: What should the _get methods return while
788 user is editing a field and the result is incomplete. The
789 partial result? The last good result? If we return partial result
790 we also need a way to inform if the date is not valid. Current
791 implementation is some kind of hybrid of these two...
794 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
796 easily fails, since set_day tries to force validity while get_day
799 Proposal: Always return the same values that are shown in the
800 fields. We add a separate flag (Or use GDate) to
801 indicate if the current date is valid. This would allow
802 setters to make the date invalid as well. */
805 *year = /*priv->year;*/
806 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
808 *month = /*priv->month;*/
809 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
811 *day = /*priv->day;*/
812 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
815 /* icon button press event */
817 hildon_date_editor_icon_press (GtkWidget *widget,
820 g_assert (GTK_IS_WIDGET (widget));
821 g_assert (HILDON_IS_DATE_EDITOR (data));
823 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
829 hildon_date_editor_entry_focus_in (GtkWidget *widget,
830 GdkEventFocus *event,
833 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
840 popup_calendar_dialog (HildonDateEditor *ed)
842 guint y = 0, m = 0, d = 0;
848 hildon_date_editor_get_date (ed, &y, &m, &d);
850 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
851 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
853 g_value_init (&val, G_TYPE_INT);
854 /* Set max/min year in calendar popup to date editor values */
855 g_object_get_property (G_OBJECT (ed), "min-year", &val);
856 g_object_set_property (G_OBJECT (popup), "min-year", &val);
857 g_object_get_property (G_OBJECT (ed), "max-year", &val);
858 g_object_set_property (G_OBJECT (popup), "max-year", &val);
860 /* Pop up calendar */
861 result = gtk_dialog_run (GTK_DIALOG (popup));
863 case GTK_RESPONSE_OK:
864 case GTK_RESPONSE_ACCEPT:
865 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
867 hildon_date_editor_set_date (ed, y, m, d);
870 gtk_widget_destroy (popup);
873 /* button released */
875 hildon_date_editor_released (GtkWidget *widget,
878 HildonDateEditor *ed;
880 g_assert (GTK_IS_WIDGET (widget));
881 g_assert (HILDON_IS_DATE_EDITOR (data));
883 ed = HILDON_DATE_EDITOR (data);
885 /* restores the icon state. The clicked cycle raises the dialog */
886 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
891 /* button released */
893 hildon_date_editor_clicked (GtkWidget *widget,
896 HildonDateEditor *ed;
898 g_assert (GTK_IS_WIDGET (widget));
899 g_assert (HILDON_IS_DATE_EDITOR (data));
901 ed = HILDON_DATE_EDITOR (data);
903 /* restores the non-clicked button state and raises the dialog */
904 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
905 popup_calendar_dialog (ed);
910 /* This is called whenever some editor filed loses the focus and
911 when the all of the fields are filled.
912 Earlier this was called whenever an entry changed */
913 /* FIXME: Validation on focus_out is broken by concept */
915 hildon_date_editor_entry_validate (GtkWidget *widget,
918 HildonDateEditor *ed;
919 HildonDateEditorPrivate *priv;
920 gint d, m, y, max_days;
921 gboolean r; /* temp return values for signals */
923 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
925 g_assert (HILDON_IS_DATE_EDITOR (data));
926 g_assert (GTK_IS_ENTRY (widget));
928 ed = HILDON_DATE_EDITOR (data);
929 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
932 if (priv->skip_validation)
935 /*check if the calling entry is empty*/
936 text = gtk_entry_get_text (GTK_ENTRY (widget));
937 if(text == NULL || text[0] == 0)
939 if (widget == priv->d_entry)
940 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
941 else if(widget == priv->m_entry)
942 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
944 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
946 /* restore empty entry to safe value */
947 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
951 /* Ok, we now check validity. Some fields can be empty */
952 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
953 if (text == NULL || text[0] == 0) return error_code;
955 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
956 if (text == NULL || text[0] == 0) return error_code;
958 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
959 if (text == NULL || text[0] == 0) return error_code;
962 /* Did it actually change? */
963 if (d != priv->day || m != priv->month || y != priv->year)
965 /* We could/should use hildon_date_editor_set_year and such functions
966 * to set the date, instead of use gtk_entry_set_text, and then change
967 * the priv member but hildon_date_editor_set_year and such functions
968 * check if the date is valid, we do want to do date validation check
969 * here according to spec */
972 if (widget == priv->m_entry) {
974 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
978 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
984 if(widget == priv->y_entry) {
985 if (y < priv->min_year) {
986 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
989 else if (y > priv->max_year) {
990 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
995 /* Validate day. We have to do this in every case, since
996 changing month or year can make the day number to be invalid */
997 max_days = g_date_get_days_in_month (m,y);
999 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1002 else if (d > max_days) {
1004 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1007 else { /* the date does not exist (is invalid) */
1008 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1009 /* check what was changed and restore previous value */
1010 if (widget == priv->y_entry)
1012 else if (widget == priv->m_entry)
1019 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1021 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1023 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1027 /* Fix and reformat the date after error signal is processed.
1028 reformatting can be needed even in a such case that numerical
1029 values of the date components are the same as earlier. */
1030 hildon_date_editor_set_date (ed, y, m, d);
1034 /* When entry becomes full, we move the focus to the next field.
1035 If we are on the last field, the whole contents are validated. */
1037 hildon_date_editor_entry_changed (GtkEditable *ed,
1042 HildonDateEditorPrivate *priv;
1044 g_assert (GTK_IS_ENTRY (ed));
1045 g_assert (HILDON_IS_DATE_EDITOR (data));
1047 entry = GTK_ENTRY (ed);
1049 /* If day entry is full, move to next entry or validate */
1050 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1052 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1053 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1055 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1057 priv->skip_validation = TRUE;
1058 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1064 hildon_date_editor_keyrelease (GtkWidget *widget,
1068 HildonDateEditor *ed;
1069 HildonDateEditorPrivate *priv;
1071 g_return_val_if_fail (data, FALSE);
1072 g_return_val_if_fail (widget, FALSE);
1074 ed = HILDON_DATE_EDITOR (data);
1075 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1077 if (event->keyval == GDK_KP_Enter ||
1078 event->keyval == GDK_Return ||
1079 event->keyval == GDK_ISO_Enter) {
1080 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1082 popup_calendar_dialog (ed);
1085 } else if (event->keyval == GDK_Escape)
1086 priv->skip_validation = FALSE;
1091 /* keyboard handling */
1093 hildon_date_editor_keypress (GtkWidget *widget,
1097 HildonDateEditor *ed;
1098 HildonDateEditorPrivate *priv;
1102 g_assert (HILDON_IS_DATE_EDITOR (data));
1103 g_assert (GTK_IS_ENTRY (widget));
1105 ed = HILDON_DATE_EDITOR (data);
1106 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1107 pos = gtk_editable_get_position (GTK_EDITABLE (widget));
1110 /* Show error message in case the key pressed is not allowed
1111 (only digits and control characters are allowed )*/
1112 if (!g_unichar_isdigit (event->keyval) && ! (event->keyval & 0xF000)) {
1113 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1117 switch (event->keyval) {
1120 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_LEFT);
1126 if (pos >= g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (widget)), -1)) {
1127 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1133 /* Ignore return value, since we want to handle event at all times.
1134 otherwise vkb would popup when the keyrepeat starts. */
1135 (void) hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1139 priv->skip_validation = TRUE;
1149 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1150 GdkEventFocus *event,
1153 HildonDateEditor *ed;
1154 HildonDateEditorPrivate *priv;
1156 g_assert (HILDON_IS_DATE_EDITOR (data));
1158 ed = HILDON_DATE_EDITOR (data);
1159 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1162 hildon_date_editor_entry_validate (widget, data);
1163 priv->skip_validation = FALSE;
1169 hildon_date_editor_date_error (HildonDateEditor *editor,
1170 HildonDateTimeError type)
1172 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1177 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1178 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1181 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1182 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1185 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1186 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1189 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1190 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1191 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1194 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1195 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1198 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1199 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1202 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1203 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1206 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1207 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1208 priv->min_year, priv->max_year);
1211 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1212 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1215 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1216 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1220 /*default error message ?*/
1228 hildon_date_editor_size_request (GtkWidget *widget,
1229 GtkRequisition *requisition)
1231 HildonDateEditor *ed;
1232 HildonDateEditorPrivate *priv;
1233 GtkRequisition f_req, img_req;
1235 g_assert (GTK_IS_WIDGET (widget));
1236 g_assert (requisition != NULL);
1238 ed = HILDON_DATE_EDITOR (widget);
1239 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1242 /* Our own children affect our size */
1243 gtk_widget_size_request (priv->frame, &f_req);
1244 gtk_widget_size_request (priv->d_button_image, &img_req);
1246 /* calculate our size */
1247 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1249 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1250 doing so would break current pixel specifications, since
1251 the text entry by itself is already 30px tall + then frame takes
1253 requisition->height = DATE_EDITOR_HEIGHT;
1257 hildon_date_editor_size_allocate (GtkWidget *widget,
1258 GtkAllocation *allocation)
1260 HildonDateEditor *ed;
1261 HildonDateEditorPrivate *priv;
1262 GtkAllocation f_alloc, img_alloc;
1264 GtkRequisition max_req;
1267 g_assert (GTK_IS_WIDGET (widget));
1268 g_assert (allocation != NULL);
1270 ed = HILDON_DATE_EDITOR (widget);
1271 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1273 widget->allocation = *allocation;
1275 gtk_widget_get_child_requisition (widget, &max_req);
1277 /* Center vertically */
1278 f_alloc.y = img_alloc.y = allocation->y +
1279 MAX (allocation->height - max_req.height, 0) / 2;
1281 /* Center horizontally */
1282 f_alloc.x = img_alloc.x = allocation->x +
1283 MAX (allocation->width - max_req.width, 0) / 2;
1285 /* allocate frame */
1286 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1287 gtk_widget_get_child_requisition (priv->frame, &req);
1289 f_alloc.width = req.width;
1290 f_alloc.height = max_req.height;
1291 gtk_widget_size_allocate (priv->frame, &f_alloc);
1295 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1296 gtk_widget_get_child_requisition (priv->d_button_image,
1299 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1300 img_alloc.width = req.width;
1301 img_alloc.height = max_req.height;
1302 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1305 /* FIXME: We really should not alloc delimeters by hand (since they
1306 are not our own children, but we need to force to appear
1307 higher. This ugly hack is needed to compensate the forced
1308 height in size_request. */
1309 for (iter = priv->delims; iter; iter = iter->next)
1312 GtkAllocation alloc;
1314 delim = GTK_WIDGET (iter->data);
1315 alloc = delim->allocation;
1316 alloc.height = max_req.height;
1317 alloc.y = priv->d_entry->allocation.y - 2;
1319 gtk_widget_size_allocate (delim, &alloc);
1324 * hildon_date_editor_set_year:
1325 * @editor: the @HildonDateEditor widget
1328 * Sets the year shown in the editor.
1330 * Returns: TRUE if the year is valid
1333 hildon_date_editor_set_year (HildonDateEditor *editor,
1336 HildonDateEditorPrivate *priv;
1337 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1339 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1342 if (g_date_valid_dmy (priv->day, priv->month, year))
1347 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1349 /* We apply the new day, but do not want automatic focus move
1350 etc to take place */
1351 g_signal_handlers_block_by_func (priv->y_entry,
1352 (gpointer) hildon_date_editor_entry_changed, editor);
1353 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1354 g_signal_handlers_unblock_by_func (priv->y_entry,
1355 (gpointer) hildon_date_editor_entry_changed, editor);
1357 g_object_notify (G_OBJECT(editor), "year");
1365 * hildon_date_editor_set_month:
1366 * @editor: the @HildonDateEditor widget
1369 * Sets the month shown in the editor.
1371 * Returns: TRUE if the month is valid
1374 hildon_date_editor_set_month (HildonDateEditor *editor,
1377 HildonDateEditorPrivate *priv;
1378 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1379 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1382 if (g_date_valid_dmy (priv->day, month, priv->year))
1387 priv->month = month;
1388 g_date_set_dmy (&date, priv->day, month, priv->year);
1389 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1391 /* We apply the new day, but do not want automatic focus move
1392 etc to take place */
1393 g_signal_handlers_block_by_func (priv->m_entry,
1394 (gpointer) hildon_date_editor_entry_changed, editor);
1395 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1396 g_signal_handlers_unblock_by_func (priv->m_entry,
1397 (gpointer) hildon_date_editor_entry_changed, editor);
1399 g_object_notify (G_OBJECT (editor), "month");
1406 * hildon_date_editor_set_day:
1407 * @editor: the @HildonDateEditor widget
1410 * Sets the day shown in the editor.
1412 * Returns: TRUE if the day is valid
1415 hildon_date_editor_set_day (HildonDateEditor *editor,
1418 HildonDateEditorPrivate *priv;
1420 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1421 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1424 if (g_date_valid_dmy (day, priv->month, priv->year))
1430 g_date_set_dmy (&date, day, priv->month, priv->year);
1431 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1433 /* We apply the new day, but do not want automatic focus move
1434 etc to take place */
1435 g_signal_handlers_block_by_func (priv->d_entry,
1436 (gpointer) hildon_date_editor_entry_changed, editor);
1437 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1438 g_signal_handlers_unblock_by_func (priv->d_entry,
1439 (gpointer) hildon_date_editor_entry_changed, editor);
1441 g_object_notify (G_OBJECT(editor), "day");
1448 * hildon_date_editor_get_year:
1449 * @editor: the @HildonDateEditor widget
1451 * Returns: the current year shown in the editor.
1454 hildon_date_editor_get_year (HildonDateEditor *editor)
1456 HildonDateEditorPrivate *priv;
1457 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1459 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1462 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1466 * hildon_date_editor_get_month:
1467 * @editor: the @HildonDateEditor widget
1469 * Gets the month shown in the editor.
1471 * Returns: the current month shown in the editor.
1474 hildon_date_editor_get_month (HildonDateEditor *editor)
1476 HildonDateEditorPrivate *priv;
1477 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1479 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1482 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1486 * hildon_date_editor_get_day:
1487 * @editor: the @HildonDateEditor widget
1489 * Gets the day shown in the editor.
1491 * Returns: the current day shown in the editor
1494 hildon_date_editor_get_day (HildonDateEditor *editor)
1496 HildonDateEditorPrivate *priv;
1497 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1499 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1502 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
1507 hildon_date_editor_entry_select_all (GtkWidget *widget)
1509 GDK_THREADS_ENTER ();
1511 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1513 GDK_THREADS_LEAVE ();