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
11 * the License, or (at your option) any later version.
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.
39 * date_editor = hildon_date_editor_new ();
41 * hildon_date_editor_get_date(date_editor, &y, &m, &d);
52 #include "hildon-date-editor.h"
55 #include <gtk/gtkenums.h>
56 #include <gdk/gdkkeysyms.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"
69 #include "hildon-date-editor-private.h"
71 #define _(string) dgettext("hildon-libs", string)
73 #define c_(string) dgettext("hildon-common-strings", string)
75 #define ENTRY_BORDERS 11
77 #define DATE_EDITOR_HEIGHT 30
79 #define DAY_ENTRY_WIDTH 2
81 #define MONTH_ENTRY_WIDTH 2
83 #define YEAR_ENTRY_WIDTH 4
85 #define DEFAULT_MIN_YEAR 1970
87 #define DEFAULT_MAX_YEAR 2037
89 static GtkContainerClass* parent_class;
92 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
95 hildon_date_editor_init (HildonDateEditor *editor);
98 hildon_date_editor_icon_press (GtkWidget *widget,
102 hildon_date_editor_released (GtkWidget *widget,
106 hildon_date_editor_keypress (GtkWidget *widget,
111 hildon_date_editor_keyrelease (GtkWidget *widget,
115 hildon_date_editor_clicked (GtkWidget *widget,
119 hildon_date_editor_entry_validate (GtkWidget *widget,
123 hildon_date_editor_entry_changed (GtkEditable *widget,
127 hildon_date_editor_entry_focus_out (GtkWidget *widget,
128 GdkEventFocus *event,
132 hildon_date_editor_date_error (HildonDateEditor *editor,
133 HildonDateTimeError type);
136 hildon_date_editor_entry_focus_in (GtkWidget *widget,
137 GdkEventFocus *event,
141 hildon_date_editor_get_property (GObject *object,
147 hildon_date_editor_set_property (GObject *object,
152 hildon_child_forall (GtkContainer *container,
153 gboolean include_internals,
154 GtkCallback callback,
155 gpointer callback_data);
158 hildon_date_editor_destroy (GtkObject *self);
161 hildon_date_editor_size_allocate (GtkWidget *widget,
162 GtkAllocation *allocation);
165 hildon_date_editor_size_request (GtkWidget *widget,
166 GtkRequisition *requisition);
169 hildon_date_editor_entry_select_all (GtkWidget *widget);
171 /* Property indices */
188 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
191 * hildon_date_editor_get_type:
193 * Initializes and returns the type of a hildon date editor.
195 * @Returns: GType of #HildonDateEditor
198 hildon_date_editor_get_type (void)
200 static GType editor_type = 0;
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),
212 (GInstanceInitFunc) hildon_date_editor_init,
214 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
223 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
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);
229 parent_class = g_type_class_peek_parent (editor_class);
231 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
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;
239 container_class->forall = hildon_child_forall;
240 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
242 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
244 date_editor_signals[DATE_ERROR] =
245 g_signal_new ("date-error",
246 G_OBJECT_CLASS_TYPE (gobject_class),
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);
254 * HildonDateEditor:year:
258 g_object_class_install_property (gobject_class, PROP_YEAR,
259 g_param_spec_uint ("year",
264 G_PARAM_READABLE | G_PARAM_WRITABLE));
267 * HildonDateEditor:month:
271 g_object_class_install_property (gobject_class, PROP_MONTH,
272 g_param_spec_uint ("month",
277 G_PARAM_READABLE | G_PARAM_WRITABLE));
280 * HildonDateEditor:day:
284 g_object_class_install_property (gobject_class, PROP_DAY,
285 g_param_spec_uint ("day",
290 G_PARAM_READABLE | G_PARAM_WRITABLE));
293 * HildonDateEditor:min-year:
295 * Minimum valid year.
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",
306 * HildonDateEditor:max-year:
308 * Maximum valid year.
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",
319 /* Forces setting of the icon to certain state. Used initially
320 and from the actual setter function */
322 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
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);
330 priv->calendar_icon_pressed = pressed;
333 /* Sets the icon to given state (normal/pressed). Returns
334 info if the state actually changed. */
336 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
339 HildonDateEditorPrivate *priv;
341 g_assert (HILDON_IS_DATE_EDITOR (editor));
343 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
346 if (pressed != priv->calendar_icon_pressed) {
347 real_set_calendar_icon_state (priv, pressed);
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 */
358 apply_locale_field_order (HildonDateEditorPrivate *priv)
360 GDate locale_test_date;
363 gchar *iter, *delim_text;
365 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
366 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
374 /* Try to convert the current location into number. */
375 value = strtoul (iter, &endp, 10);
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 */
383 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
384 priv->d_entry, FALSE, FALSE, 0);
388 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
389 priv->m_entry, FALSE, FALSE, 0);
392 case 70: /* %x format uses only 2 numbers for some locales */
394 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
395 priv->y_entry, FALSE, FALSE, 0);
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))
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);
411 priv->delims = g_list_append (priv->delims, delim);
422 hildon_date_editor_init (HildonDateEditor *editor)
424 HildonDateEditorPrivate *priv;
427 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
430 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
432 gtk_widget_push_composite_child ();
434 /* initialize values */
435 g_date_clear (&cur_date, 1);
436 g_date_set_time (&cur_date, time (NULL));
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;
445 priv->frame = gtk_frame_new (NULL);
446 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
448 priv->d_entry = gtk_entry_new ();
449 priv->m_entry = gtk_entry_new ();
450 priv->y_entry = gtk_entry_new ();
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);
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);
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);
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);
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");
478 priv->d_box_date = gtk_hbox_new (FALSE, 0);
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);
485 apply_locale_field_order (priv);
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);
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);
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);
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);
511 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
512 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
514 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
515 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
517 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
518 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
520 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
521 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
523 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
524 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
526 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
527 G_CALLBACK (hildon_date_editor_keypress), editor);
529 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
530 G_CALLBACK (hildon_date_editor_keypress), editor);
532 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
533 G_CALLBACK (hildon_date_editor_keypress), editor);
535 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
536 G_CALLBACK (hildon_date_editor_keyrelease), editor);
538 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
539 G_CALLBACK(hildon_date_editor_keyrelease), editor);
541 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
542 G_CALLBACK (hildon_date_editor_keyrelease), editor);
544 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
546 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
547 G_CALLBACK (hildon_date_editor_entry_changed), editor);
549 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
550 G_CALLBACK (hildon_date_editor_entry_changed), editor);
552 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
553 G_CALLBACK (hildon_date_editor_entry_changed), editor);
555 gtk_widget_pop_composite_child ();
559 hildon_date_editor_set_property (GObject *object,
564 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
565 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
573 hildon_date_editor_set_year (editor, g_value_get_uint (value));
577 hildon_date_editor_set_month (editor, g_value_get_uint (value));
581 hildon_date_editor_set_day (editor, g_value_get_uint (value));
585 val = g_value_get_uint (value);
586 /* Clamp current year */
587 if (hildon_date_editor_get_year (editor) < priv->min_year)
588 hildon_date_editor_set_year (editor, priv->min_year);
592 val = g_value_get_uint (value);
593 priv->max_year = val;
594 /* Clamp current year */
595 if (hildon_date_editor_get_year (editor) > priv->max_year)
596 hildon_date_editor_set_year (editor, priv->max_year);
600 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
606 hildon_date_editor_get_property (GObject *object,
611 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
612 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
617 g_value_set_uint (value, hildon_date_editor_get_year (editor));
621 g_value_set_uint (value, hildon_date_editor_get_month (editor));
625 g_value_set_uint (value, hildon_date_editor_get_day (editor));
629 g_value_set_uint (value, priv->min_year);
633 g_value_set_uint (value, priv->max_year);
637 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
643 hildon_child_forall (GtkContainer *container,
644 gboolean include_internals,
645 GtkCallback callback,
646 gpointer callback_data)
648 HildonDateEditor *editor;
649 HildonDateEditorPrivate *priv;
651 g_assert (HILDON_IS_DATE_EDITOR (container));
654 editor = HILDON_DATE_EDITOR (container);
655 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
658 if (include_internals) {
659 (*callback) (priv->frame, callback_data);
660 (*callback) (priv->d_button_image, callback_data);
665 hildon_date_editor_destroy (GtkObject *self)
667 HildonDateEditorPrivate *priv;
669 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
673 gtk_widget_unparent (priv->frame);
676 if (priv->d_button_image) {
677 gtk_widget_unparent (priv->d_button_image);
678 priv->d_button_image = NULL;
681 g_list_free (priv->delims);
685 if (GTK_OBJECT_CLASS (parent_class)->destroy)
686 GTK_OBJECT_CLASS (parent_class)->destroy (self);
690 * hildon_date_editor_new:
692 * Creates a new date editor. The current system date
693 * is shown in the editor.
695 * Returns: pointer to a new @HildonDateEditor widget.
698 hildon_date_editor_new (void)
700 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
704 * hildon_date_editor_set_date:
705 * @date: the @HildonDateEditor widget
710 * Sets the date shown in the editor.
713 hildon_date_editor_set_date (HildonDateEditor *editor,
718 HildonDateEditorPrivate *priv;
720 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
722 /* This function cannot be implemented by calling
723 component setters, since applying the individual
724 values one by one can make the date temporarily
725 invalid (depending on what the previous values were),
726 which in turn causes that the desired date
727 is not set (even though it's valid). We must set all the
728 components at one go and not try to do any validation etc
731 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
734 if (g_date_valid_dmy (day, month, year))
743 g_date_set_dmy (&date, day, month, year);
745 /* We apply the new values, but do not want automatic focus move
747 g_snprintf (buffer, sizeof (buffer), "%04d", year);
748 g_signal_handlers_block_by_func (priv->y_entry,
749 (gpointer) hildon_date_editor_entry_changed, editor);
750 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
751 g_signal_handlers_unblock_by_func (priv->y_entry,
752 (gpointer) hildon_date_editor_entry_changed, editor);
754 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
755 g_signal_handlers_block_by_func (priv->m_entry,
756 (gpointer) hildon_date_editor_entry_changed, editor);
757 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
758 g_signal_handlers_unblock_by_func (priv->m_entry,
759 (gpointer) hildon_date_editor_entry_changed, editor);
761 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
762 g_signal_handlers_block_by_func (priv->d_entry,
763 (gpointer) hildon_date_editor_entry_changed, editor);
764 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
765 g_signal_handlers_unblock_by_func (priv->d_entry,
766 (gpointer) hildon_date_editor_entry_changed, editor);
768 g_object_notify (G_OBJECT (editor), "year");
769 g_object_notify (G_OBJECT (editor), "month");
770 g_object_notify (G_OBJECT (editor), "day");
775 * hildon_date_editor_get_date:
776 * @date: the @HildonDateEditor widget
781 * Gets the date represented by the date editor.
782 * You can pass NULL to any of the pointers if
783 * you're not interested in obtaining it.
787 hildon_date_editor_get_date (HildonDateEditor *date,
792 HildonDateEditorPrivate *priv;
794 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
796 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
798 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
799 is unclear. They do not neccesarily match and still the texts are
800 used as return values and members for some internal validation!!
801 At least a partly reason is to allow empty text to become
802 0 return value, while members are restricted to valid ranges?!
803 However, if we change the current way, we are likely to break
804 some applications if they rely on some specific way how this
805 widget currently handles empty values and temporarily invalid values.
807 The key issue is this: What should the _get methods return while
808 user is editing a field and the result is incomplete. The
809 partial result? The last good result? If we return partial result
810 we also need a way to inform if the date is not valid. Current
811 implementation is some kind of hybrid of these two...
814 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
816 easily fails, since set_day tries to force validity while get_day
819 Proposal: Always return the same values that are shown in the
820 fields. We add a separate flag (Or use GDate) to
821 indicate if the current date is valid. This would allow
822 setters to make the date invalid as well. */
825 *year = /*priv->year;*/
826 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
828 *month = /*priv->month;*/
829 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
831 *day = /*priv->day;*/
832 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
835 /* icon button press event */
837 hildon_date_editor_icon_press (GtkWidget *widget,
840 g_assert (GTK_IS_WIDGET (widget));
841 g_assert (HILDON_IS_DATE_EDITOR (data));
843 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
849 hildon_date_editor_entry_focus_in (GtkWidget *widget,
850 GdkEventFocus *event,
853 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
860 popup_calendar_dialog (HildonDateEditor *ed)
862 guint y = 0, m = 0, d = 0;
868 hildon_date_editor_get_date (ed, &y, &m, &d);
870 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
871 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
873 g_value_init (&val, G_TYPE_INT);
874 /* Set max/min year in calendar popup to date editor values */
875 g_object_get_property (G_OBJECT (ed), "min-year", &val);
876 g_object_set_property (G_OBJECT (popup), "min-year", &val);
877 g_object_get_property (G_OBJECT (ed), "max-year", &val);
878 g_object_set_property (G_OBJECT (popup), "max-year", &val);
880 /* Pop up calendar */
881 result = gtk_dialog_run (GTK_DIALOG (popup));
883 case GTK_RESPONSE_OK:
884 case GTK_RESPONSE_ACCEPT:
885 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
887 hildon_date_editor_set_date (ed, y, m, d);
890 gtk_widget_destroy (popup);
893 /* button released */
895 hildon_date_editor_released (GtkWidget *widget,
898 HildonDateEditor *ed;
900 g_assert (GTK_IS_WIDGET (widget));
901 g_assert (HILDON_IS_DATE_EDITOR (data));
903 ed = HILDON_DATE_EDITOR (data);
905 /* restores the icon state. The clicked cycle raises the dialog */
906 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
911 /* button released */
913 hildon_date_editor_clicked (GtkWidget *widget,
916 HildonDateEditor *ed;
918 g_assert (GTK_IS_WIDGET (widget));
919 g_assert (HILDON_IS_DATE_EDITOR (data));
921 ed = HILDON_DATE_EDITOR (data);
923 /* restores the non-clicked button state and raises the dialog */
924 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
925 popup_calendar_dialog (ed);
930 /* This is called whenever some editor filed loses the focus and
931 when the all of the fields are filled.
932 Earlier this was called whenever an entry changed */
933 /* FIXME: Validation on focus_out is broken by concept */
935 hildon_date_editor_entry_validate (GtkWidget *widget,
938 HildonDateEditor *ed;
939 HildonDateEditorPrivate *priv;
940 gint d, m, y, max_days;
941 gboolean r; /* temp return values for signals */
943 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
945 g_assert (HILDON_IS_DATE_EDITOR (data));
946 g_assert (GTK_IS_ENTRY (widget));
948 ed = HILDON_DATE_EDITOR (data);
949 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
952 if (priv->skip_validation)
955 /*check if the calling entry is empty*/
956 text = gtk_entry_get_text (GTK_ENTRY (widget));
957 if(text == NULL || text[0] == 0)
959 if (widget == priv->d_entry)
960 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
961 else if(widget == priv->m_entry)
962 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
964 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
966 /* restore empty entry to safe value */
967 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
971 /* Ok, we now check validity. Some fields can be empty */
972 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
973 if (text == NULL || text[0] == 0) return error_code;
975 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
976 if (text == NULL || text[0] == 0) return error_code;
978 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
979 if (text == NULL || text[0] == 0) return error_code;
982 /* Did it actually change? */
983 if (d != priv->day || m != priv->month || y != priv->year)
985 /* We could/should use hildon_date_editor_set_year and such functions
986 * to set the date, instead of use gtk_entry_set_text, and then change
987 * the priv member but hildon_date_editor_set_year and such functions
988 * check if the date is valid, we do want to do date validation check
989 * here according to spec */
992 if (widget == priv->m_entry) {
994 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
998 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1004 if(widget == priv->y_entry) {
1005 if (y < priv->min_year) {
1006 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1009 else if (y > priv->max_year) {
1010 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1015 /* Validate day. We have to do this in every case, since
1016 changing month or year can make the day number to be invalid */
1017 max_days = g_date_get_days_in_month (m,y);
1019 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1022 else if (d > max_days) {
1024 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1027 else { /* the date does not exist (is invalid) */
1028 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1029 /* check what was changed and restore previous value */
1030 if (widget == priv->y_entry)
1032 else if (widget == priv->m_entry)
1039 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1041 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1043 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1047 /* Fix and reformat the date after error signal is processed.
1048 reformatting can be needed even in a such case that numerical
1049 values of the date components are the same as earlier. */
1050 hildon_date_editor_set_date (ed, y, m, d);
1056 hildon_date_editor_entry_select_all (GtkWidget *widget)
1058 GDK_THREADS_ENTER ();
1060 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1062 GDK_THREADS_LEAVE ();
1067 /* When entry becomes full, we move the focus to the next field.
1068 If we are on the last field, the whole contents are validated. */
1070 hildon_date_editor_entry_changed (GtkEditable *ed,
1075 HildonDateEditorPrivate *priv;
1077 g_assert (GTK_IS_ENTRY (ed));
1078 g_assert (HILDON_IS_DATE_EDITOR (data));
1080 entry = GTK_ENTRY (ed);
1082 /* If day entry is full, move to next entry or validate */
1083 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1085 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1086 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1088 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1090 priv->skip_validation = TRUE;
1091 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1097 hildon_date_editor_keyrelease (GtkWidget *widget,
1101 HildonDateEditor *ed;
1102 HildonDateEditorPrivate *priv;
1104 g_return_val_if_fail (data, FALSE);
1105 g_return_val_if_fail (widget, FALSE);
1107 ed = HILDON_DATE_EDITOR (data);
1108 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1110 if (event->keyval == GDK_KP_Enter ||
1111 event->keyval == GDK_Return ||
1112 event->keyval == GDK_ISO_Enter) {
1113 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1115 popup_calendar_dialog (ed);
1118 } else if (event->keyval == GDK_Escape)
1119 priv->skip_validation = FALSE;
1124 /* keyboard handling */
1126 hildon_date_editor_keypress (GtkWidget *widget,
1130 HildonDateEditor *ed;
1131 HildonDateEditorPrivate *priv;
1135 g_assert (HILDON_IS_DATE_EDITOR (data));
1136 g_assert (GTK_IS_ENTRY (widget));
1138 ed = HILDON_DATE_EDITOR (data);
1139 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1140 pos = gtk_editable_get_position (GTK_EDITABLE (widget));
1143 /* Show error message in case the key pressed is not allowed
1144 (only digits and control characters are allowed )*/
1145 if (!g_unichar_isdigit (event->keyval) && ! (event->keyval & 0xF000)) {
1146 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1150 switch (event->keyval) {
1153 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_LEFT);
1159 if (pos >= g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (widget)), -1)) {
1160 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1166 /* Ignore return value, since we want to handle event at all times.
1167 otherwise vkb would popup when the keyrepeat starts. */
1168 (void) hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1172 priv->skip_validation = TRUE;
1182 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1183 GdkEventFocus *event,
1186 HildonDateEditor *ed;
1187 HildonDateEditorPrivate *priv;
1189 g_assert (HILDON_IS_DATE_EDITOR (data));
1191 ed = HILDON_DATE_EDITOR (data);
1192 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1195 hildon_date_editor_entry_validate (widget, data);
1196 priv->skip_validation = FALSE;
1202 hildon_date_editor_date_error (HildonDateEditor *editor,
1203 HildonDateTimeError type)
1205 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1210 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1211 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1214 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1215 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1218 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1219 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1222 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1223 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1224 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1227 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1228 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1231 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1232 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1235 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1236 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1239 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1240 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1241 priv->min_year, priv->max_year);
1244 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1245 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1248 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1249 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1253 /*default error message ?*/
1261 hildon_date_editor_size_request (GtkWidget *widget,
1262 GtkRequisition *requisition)
1264 HildonDateEditor *ed;
1265 HildonDateEditorPrivate *priv;
1266 GtkRequisition f_req, img_req;
1268 g_assert (GTK_IS_WIDGET (widget));
1269 g_assert (requisition != NULL);
1271 ed = HILDON_DATE_EDITOR (widget);
1272 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1275 /* Our own children affect our size */
1276 gtk_widget_size_request (priv->frame, &f_req);
1277 gtk_widget_size_request (priv->d_button_image, &img_req);
1279 /* calculate our size */
1280 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1282 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1283 doing so would break current pixel specifications, since
1284 the text entry by itself is already 30px tall + then frame takes
1286 requisition->height = DATE_EDITOR_HEIGHT;
1290 hildon_date_editor_size_allocate (GtkWidget *widget,
1291 GtkAllocation *allocation)
1293 HildonDateEditor *ed;
1294 HildonDateEditorPrivate *priv;
1295 GtkAllocation f_alloc, img_alloc;
1297 GtkRequisition max_req;
1300 g_assert (GTK_IS_WIDGET (widget));
1301 g_assert (allocation != NULL);
1303 ed = HILDON_DATE_EDITOR (widget);
1304 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1306 widget->allocation = *allocation;
1308 gtk_widget_get_child_requisition (widget, &max_req);
1310 /* Center vertically */
1311 f_alloc.y = img_alloc.y = allocation->y +
1312 MAX (allocation->height - max_req.height, 0) / 2;
1314 /* Center horizontally */
1315 f_alloc.x = img_alloc.x = allocation->x +
1316 MAX (allocation->width - max_req.width, 0) / 2;
1318 /* allocate frame */
1319 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1320 gtk_widget_get_child_requisition (priv->frame, &req);
1322 f_alloc.width = req.width;
1323 f_alloc.height = max_req.height;
1324 gtk_widget_size_allocate (priv->frame, &f_alloc);
1328 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1329 gtk_widget_get_child_requisition (priv->d_button_image,
1332 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1333 img_alloc.width = req.width;
1334 img_alloc.height = max_req.height;
1335 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1338 /* FIXME: We really should not alloc delimeters by hand (since they
1339 are not our own children, but we need to force to appear
1340 higher. This ugly hack is needed to compensate the forced
1341 height in size_request. */
1342 for (iter = priv->delims; iter; iter = iter->next)
1345 GtkAllocation alloc;
1347 delim = GTK_WIDGET (iter->data);
1348 alloc = delim->allocation;
1349 alloc.height = max_req.height;
1350 alloc.y = priv->d_entry->allocation.y - 2;
1352 gtk_widget_size_allocate (delim, &alloc);
1357 * hildon_date_editor_set_year:
1358 * @editor: the @HildonDateEditor widget
1361 * Sets the year shown in the editor.
1363 * Returns: TRUE if the year is valid and has been set.
1366 hildon_date_editor_set_year (HildonDateEditor *editor,
1369 HildonDateEditorPrivate *priv;
1370 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1372 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1375 if (g_date_valid_dmy (priv->day, priv->month, year))
1380 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1382 /* We apply the new day, but do not want automatic focus move
1383 etc to take place */
1384 g_signal_handlers_block_by_func (priv->y_entry,
1385 (gpointer) hildon_date_editor_entry_changed, editor);
1386 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1387 g_signal_handlers_unblock_by_func (priv->y_entry,
1388 (gpointer) hildon_date_editor_entry_changed, editor);
1390 g_object_notify (G_OBJECT(editor), "year");
1398 * hildon_date_editor_set_month:
1399 * @editor: the @HildonDateEditor widget
1402 * Sets the month shown in the editor.
1404 * Returns: TRUE if the month is valid and has been set.
1407 hildon_date_editor_set_month (HildonDateEditor *editor,
1410 HildonDateEditorPrivate *priv;
1411 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1412 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1415 if (g_date_valid_dmy (priv->day, month, priv->year))
1420 priv->month = month;
1421 g_date_set_dmy (&date, priv->day, month, priv->year);
1422 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1424 /* We apply the new day, but do not want automatic focus move
1425 etc to take place */
1426 g_signal_handlers_block_by_func (priv->m_entry,
1427 (gpointer) hildon_date_editor_entry_changed, editor);
1428 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1429 g_signal_handlers_unblock_by_func (priv->m_entry,
1430 (gpointer) hildon_date_editor_entry_changed, editor);
1432 g_object_notify (G_OBJECT (editor), "month");
1439 * hildon_date_editor_set_day:
1440 * @editor: the @HildonDateEditor widget
1443 * Sets the day shown in the editor.
1445 * Returns: TRUE if the day is valid and has been set.
1448 hildon_date_editor_set_day (HildonDateEditor *editor,
1451 HildonDateEditorPrivate *priv;
1453 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1454 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1457 if (g_date_valid_dmy (day, priv->month, priv->year))
1463 g_date_set_dmy (&date, day, priv->month, priv->year);
1464 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1466 /* We apply the new day, but do not want automatic focus move
1467 etc to take place */
1468 g_signal_handlers_block_by_func (priv->d_entry,
1469 (gpointer) hildon_date_editor_entry_changed, editor);
1470 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1471 g_signal_handlers_unblock_by_func (priv->d_entry,
1472 (gpointer) hildon_date_editor_entry_changed, editor);
1474 g_object_notify (G_OBJECT(editor), "day");
1481 * hildon_date_editor_get_year:
1482 * @editor: the @HildonDateEditor widget
1484 * Returns: the current year shown in the editor.
1487 hildon_date_editor_get_year (HildonDateEditor *editor)
1489 HildonDateEditorPrivate *priv;
1490 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1492 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1495 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1499 * hildon_date_editor_get_month:
1500 * @editor: the @HildonDateEditor widget
1502 * Gets the month shown in the editor.
1504 * Returns: the current month shown in the editor.
1507 hildon_date_editor_get_month (HildonDateEditor *editor)
1509 HildonDateEditorPrivate *priv;
1510 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1512 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1515 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1519 * hildon_date_editor_get_day:
1520 * @editor: the @HildonDateEditor widget
1522 * Gets the day shown in the editor.
1524 * Returns: the current day shown in the editor
1527 hildon_date_editor_get_day (HildonDateEditor *editor)
1529 HildonDateEditorPrivate *priv;
1530 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1532 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1535 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));