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.
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 <gdk/gdkkeysyms.h>
60 #include "hildon-calendar-popup.h"
61 #include "hildon-defines.h"
62 #include "hildon-input-mode-hint.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 3000
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 ();
452 g_object_set (G_OBJECT(priv->d_entry), "input-mode",
453 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
454 g_object_set (G_OBJECT(priv->m_entry), "input-mode",
455 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
456 g_object_set (G_OBJECT(priv->y_entry), "input-mode",
457 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
460 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
461 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
462 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
464 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
465 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
466 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
468 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
469 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
470 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
472 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
473 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
474 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
476 priv->d_box_date = gtk_hbox_new (FALSE, 0);
478 priv->d_button_image = gtk_button_new ();
479 priv->calendar_icon = gtk_image_new ();
480 real_set_calendar_icon_state (priv, FALSE);
481 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
483 apply_locale_field_order (priv);
485 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
486 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
487 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
488 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
490 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
491 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
492 gtk_widget_show_all (priv->frame);
493 gtk_widget_show_all (priv->d_button_image);
495 /* image button signal connects */
496 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
497 G_CALLBACK (hildon_date_editor_icon_press), editor);
498 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
499 G_CALLBACK (hildon_date_editor_released), editor);
500 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
501 G_CALLBACK (hildon_date_editor_clicked), editor);
502 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
503 G_CALLBACK (hildon_date_editor_keypress), editor);
505 /* entry signal connects */
506 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
507 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
509 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
510 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
512 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
513 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
515 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
516 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
518 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
519 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
521 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
522 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
524 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
525 G_CALLBACK (hildon_date_editor_keypress), editor);
527 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
528 G_CALLBACK (hildon_date_editor_keypress), editor);
530 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
531 G_CALLBACK (hildon_date_editor_keypress), editor);
533 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
534 G_CALLBACK (hildon_date_editor_keyrelease), editor);
536 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
537 G_CALLBACK(hildon_date_editor_keyrelease), editor);
539 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
540 G_CALLBACK (hildon_date_editor_keyrelease), editor);
542 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
544 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
545 G_CALLBACK (hildon_date_editor_entry_changed), editor);
547 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
548 G_CALLBACK (hildon_date_editor_entry_changed), editor);
550 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
551 G_CALLBACK (hildon_date_editor_entry_changed), editor);
553 gtk_widget_pop_composite_child ();
557 hildon_date_editor_set_property (GObject *object,
562 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
563 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
571 hildon_date_editor_set_year (editor, g_value_get_uint (value));
575 hildon_date_editor_set_month (editor, g_value_get_uint (value));
579 hildon_date_editor_set_day (editor, g_value_get_uint (value));
583 val = g_value_get_uint (value);
584 g_return_if_fail (val > priv->max_year);
585 priv->min_year = val;
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 g_return_if_fail (val < priv->min_year);
594 priv->max_year = val;
595 /* Clamp current year */
596 if (hildon_date_editor_get_year (editor) > priv->max_year)
597 hildon_date_editor_set_year (editor, priv->max_year);
601 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
607 hildon_date_editor_get_property (GObject *object,
612 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
613 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
618 g_value_set_uint (value, hildon_date_editor_get_year (editor));
622 g_value_set_uint (value, hildon_date_editor_get_month (editor));
626 g_value_set_uint (value, hildon_date_editor_get_day (editor));
630 g_value_set_uint (value, priv->min_year);
634 g_value_set_uint (value, priv->max_year);
638 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
644 hildon_child_forall (GtkContainer *container,
645 gboolean include_internals,
646 GtkCallback callback,
647 gpointer callback_data)
649 HildonDateEditor *editor;
650 HildonDateEditorPrivate *priv;
652 g_assert (HILDON_IS_DATE_EDITOR (container));
655 editor = HILDON_DATE_EDITOR (container);
656 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
659 if (include_internals) {
660 (*callback) (priv->frame, callback_data);
661 (*callback) (priv->d_button_image, callback_data);
666 hildon_date_editor_destroy (GtkObject *self)
668 HildonDateEditorPrivate *priv;
670 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
674 gtk_widget_unparent (priv->frame);
677 if (priv->d_button_image) {
678 gtk_widget_unparent (priv->d_button_image);
679 priv->d_button_image = NULL;
682 g_list_free (priv->delims);
686 if (GTK_OBJECT_CLASS (parent_class)->destroy)
687 GTK_OBJECT_CLASS (parent_class)->destroy (self);
691 * hildon_date_editor_new:
693 * Creates a new date editor. The current system date
694 * is shown in the editor.
696 * Returns: pointer to a new @HildonDateEditor widget.
699 hildon_date_editor_new (void)
701 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
705 * hildon_date_editor_set_date:
706 * @date: the @HildonDateEditor widget
711 * Sets the date shown in the editor.
714 hildon_date_editor_set_date (HildonDateEditor *editor,
719 HildonDateEditorPrivate *priv;
721 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
723 /* This function cannot be implemented by calling
724 component setters, since applying the individual
725 values one by one can make the date temporarily
726 invalid (depending on what the previous values were),
727 which in turn causes that the desired date
728 is not set (even though it's valid). We must set all the
729 components at one go and not try to do any validation etc
732 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
735 if (g_date_valid_dmy (day, month, year))
744 g_date_set_dmy (&date, day, month, year);
746 /* We apply the new values, but do not want automatic focus move
748 g_snprintf (buffer, sizeof (buffer), "%04d", year);
749 g_signal_handlers_block_by_func (priv->y_entry,
750 (gpointer) hildon_date_editor_entry_changed, editor);
751 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
752 g_signal_handlers_unblock_by_func (priv->y_entry,
753 (gpointer) hildon_date_editor_entry_changed, editor);
755 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
756 g_signal_handlers_block_by_func (priv->m_entry,
757 (gpointer) hildon_date_editor_entry_changed, editor);
758 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
759 g_signal_handlers_unblock_by_func (priv->m_entry,
760 (gpointer) hildon_date_editor_entry_changed, editor);
762 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
763 g_signal_handlers_block_by_func (priv->d_entry,
764 (gpointer) hildon_date_editor_entry_changed, editor);
765 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
766 g_signal_handlers_unblock_by_func (priv->d_entry,
767 (gpointer) hildon_date_editor_entry_changed, editor);
769 g_object_notify (G_OBJECT (editor), "year");
770 g_object_notify (G_OBJECT (editor), "month");
771 g_object_notify (G_OBJECT (editor), "day");
776 * hildon_date_editor_get_date:
777 * @date: the @HildonDateEditor widget
782 * Gets the date represented by the date editor.
783 * You can pass NULL to any of the pointers if
784 * you're not interested in obtaining it.
788 hildon_date_editor_get_date (HildonDateEditor *date,
793 HildonDateEditorPrivate *priv;
795 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
797 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
799 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
800 is unclear. They do not neccesarily match and still the texts are
801 used as return values and members for some internal validation!!
802 At least a partly reason is to allow empty text to become
803 0 return value, while members are restricted to valid ranges?!
804 However, if we change the current way, we are likely to break
805 some applications if they rely on some specific way how this
806 widget currently handles empty values and temporarily invalid values.
808 The key issue is this: What should the _get methods return while
809 user is editing a field and the result is incomplete. The
810 partial result? The last good result? If we return partial result
811 we also need a way to inform if the date is not valid. Current
812 implementation is some kind of hybrid of these two...
815 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
817 easily fails, since set_day tries to force validity while get_day
820 Proposal: Always return the same values that are shown in the
821 fields. We add a separate flag (Or use GDate) to
822 indicate if the current date is valid. This would allow
823 setters to make the date invalid as well. */
826 *year = /*priv->year;*/
827 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
829 *month = /*priv->month;*/
830 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
832 *day = /*priv->day;*/
833 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
836 /* icon button press event */
838 hildon_date_editor_icon_press (GtkWidget *widget,
841 g_assert (GTK_IS_WIDGET (widget));
842 g_assert (HILDON_IS_DATE_EDITOR (data));
844 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
850 hildon_date_editor_entry_focus_in (GtkWidget *widget,
851 GdkEventFocus *event,
854 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
861 popup_calendar_dialog (HildonDateEditor *ed)
863 guint y = 0, m = 0, d = 0;
869 hildon_date_editor_get_date (ed, &y, &m, &d);
871 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
872 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
874 g_value_init (&val, G_TYPE_INT);
875 /* Set max/min year in calendar popup to date editor values */
876 g_object_get_property (G_OBJECT (ed), "min-year", &val);
877 g_object_set_property (G_OBJECT (popup), "min-year", &val);
878 g_object_get_property (G_OBJECT (ed), "max-year", &val);
879 g_object_set_property (G_OBJECT (popup), "max-year", &val);
881 /* Pop up calendar */
882 result = gtk_dialog_run (GTK_DIALOG (popup));
884 case GTK_RESPONSE_OK:
885 case GTK_RESPONSE_ACCEPT:
886 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
888 hildon_date_editor_set_date (ed, y, m, d);
891 gtk_widget_destroy (popup);
894 /* button released */
896 hildon_date_editor_released (GtkWidget *widget,
899 HildonDateEditor *ed;
901 g_assert (GTK_IS_WIDGET (widget));
902 g_assert (HILDON_IS_DATE_EDITOR (data));
904 ed = HILDON_DATE_EDITOR (data);
906 /* restores the icon state. The clicked cycle raises the dialog */
907 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
912 /* button released */
914 hildon_date_editor_clicked (GtkWidget *widget,
917 HildonDateEditor *ed;
919 g_assert (GTK_IS_WIDGET (widget));
920 g_assert (HILDON_IS_DATE_EDITOR (data));
922 ed = HILDON_DATE_EDITOR (data);
924 /* restores the non-clicked button state and raises the dialog */
925 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
926 popup_calendar_dialog (ed);
931 /* This is called whenever some editor filed loses the focus and
932 when the all of the fields are filled.
933 Earlier this was called whenever an entry changed */
934 /* FIXME: Validation on focus_out is broken by concept */
936 hildon_date_editor_entry_validate (GtkWidget *widget,
939 HildonDateEditor *ed;
940 HildonDateEditorPrivate *priv;
941 gint d, m, y, max_days;
942 gboolean r; /* temp return values for signals */
944 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
946 g_assert (HILDON_IS_DATE_EDITOR (data));
947 g_assert (GTK_IS_ENTRY (widget));
949 ed = HILDON_DATE_EDITOR (data);
950 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
953 if (priv->skip_validation)
956 /*check if the calling entry is empty*/
957 text = gtk_entry_get_text (GTK_ENTRY (widget));
958 if(text == NULL || text[0] == 0)
960 if (widget == priv->d_entry)
961 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
962 else if(widget == priv->m_entry)
963 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
965 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
967 /* restore empty entry to safe value */
968 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
972 /* Ok, we now check validity. Some fields can be empty */
973 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
974 if (text == NULL || text[0] == 0) return error_code;
976 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
977 if (text == NULL || text[0] == 0) return error_code;
979 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
980 if (text == NULL || text[0] == 0) return error_code;
983 /* Did it actually change? */
984 if (d != priv->day || m != priv->month || y != priv->year)
986 /* We could/should use hildon_date_editor_set_year and such functions
987 * to set the date, instead of use gtk_entry_set_text, and then change
988 * the priv member but hildon_date_editor_set_year and such functions
989 * check if the date is valid, we do want to do date validation check
990 * here according to spec */
993 if (widget == priv->m_entry) {
995 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
999 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1005 if(widget == priv->y_entry) {
1006 if (y < priv->min_year) {
1007 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1010 else if (y > priv->max_year) {
1011 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1016 /* Validate day. We have to do this in every case, since
1017 changing month or year can make the day number to be invalid */
1018 max_days = g_date_get_days_in_month (m,y);
1020 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1023 else if (d > max_days) {
1025 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1028 else { /* the date does not exist (is invalid) */
1029 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1030 /* check what was changed and restore previous value */
1031 if (widget == priv->y_entry)
1033 else if (widget == priv->m_entry)
1040 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1042 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1044 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1048 /* Fix and reformat the date after error signal is processed.
1049 reformatting can be needed even in a such case that numerical
1050 values of the date components are the same as earlier. */
1051 hildon_date_editor_set_date (ed, y, m, d);
1057 hildon_date_editor_entry_select_all (GtkWidget *widget)
1059 GDK_THREADS_ENTER ();
1061 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1063 GDK_THREADS_LEAVE ();
1068 /* When entry becomes full, we move the focus to the next field.
1069 If we are on the last field, the whole contents are validated. */
1071 hildon_date_editor_entry_changed (GtkEditable *ed,
1076 HildonDateEditorPrivate *priv;
1078 g_assert (GTK_IS_ENTRY (ed));
1079 g_assert (HILDON_IS_DATE_EDITOR (data));
1081 entry = GTK_ENTRY (ed);
1083 /* If day entry is full, move to next entry or validate */
1084 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1086 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1087 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1089 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1091 priv->skip_validation = TRUE;
1092 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1098 hildon_date_editor_keyrelease (GtkWidget *widget,
1102 HildonDateEditor *ed;
1103 HildonDateEditorPrivate *priv;
1105 g_return_val_if_fail (data, FALSE);
1106 g_return_val_if_fail (widget, FALSE);
1108 ed = HILDON_DATE_EDITOR (data);
1109 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1111 if (event->keyval == GDK_KP_Enter ||
1112 event->keyval == GDK_Return ||
1113 event->keyval == GDK_ISO_Enter) {
1114 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1116 popup_calendar_dialog (ed);
1119 } else if (event->keyval == GDK_Escape)
1120 priv->skip_validation = FALSE;
1125 /* keyboard handling */
1127 hildon_date_editor_keypress (GtkWidget *widget,
1131 HildonDateEditor *ed;
1132 HildonDateEditorPrivate *priv;
1136 g_assert (HILDON_IS_DATE_EDITOR (data));
1137 g_assert (GTK_IS_ENTRY (widget));
1139 ed = HILDON_DATE_EDITOR (data);
1140 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1141 pos = gtk_editable_get_position (GTK_EDITABLE (widget));
1144 /* Show error message in case the key pressed is not allowed
1145 (only digits and control characters are allowed )*/
1146 if (!g_unichar_isdigit (event->keyval) && ! (event->keyval & 0xF000)) {
1147 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1151 switch (event->keyval) {
1154 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_LEFT);
1160 if (pos >= g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (widget)), -1)) {
1161 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1167 /* Ignore return value, since we want to handle event at all times.
1168 otherwise vkb would popup when the keyrepeat starts. */
1169 (void) hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1173 priv->skip_validation = TRUE;
1183 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1184 GdkEventFocus *event,
1187 HildonDateEditor *ed;
1188 HildonDateEditorPrivate *priv;
1190 g_assert (HILDON_IS_DATE_EDITOR (data));
1192 ed = HILDON_DATE_EDITOR (data);
1193 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1196 hildon_date_editor_entry_validate (widget, data);
1197 priv->skip_validation = FALSE;
1203 hildon_date_editor_date_error (HildonDateEditor *editor,
1204 HildonDateTimeError type)
1206 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1211 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1212 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1215 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1216 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1219 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1220 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1223 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1224 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1225 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1228 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1229 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1232 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1233 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1236 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1237 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1240 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1241 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1242 priv->min_year, priv->max_year);
1245 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1246 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1249 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1250 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1254 /*default error message ?*/
1262 hildon_date_editor_size_request (GtkWidget *widget,
1263 GtkRequisition *requisition)
1265 HildonDateEditor *ed;
1266 HildonDateEditorPrivate *priv;
1267 GtkRequisition f_req, img_req;
1269 g_assert (GTK_IS_WIDGET (widget));
1270 g_assert (requisition != NULL);
1272 ed = HILDON_DATE_EDITOR (widget);
1273 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1276 /* Our own children affect our size */
1277 gtk_widget_size_request (priv->frame, &f_req);
1278 gtk_widget_size_request (priv->d_button_image, &img_req);
1280 /* calculate our size */
1281 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1283 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1284 doing so would break current pixel specifications, since
1285 the text entry by itself is already 30px tall + then frame takes
1287 requisition->height = DATE_EDITOR_HEIGHT;
1291 hildon_date_editor_size_allocate (GtkWidget *widget,
1292 GtkAllocation *allocation)
1294 HildonDateEditor *ed;
1295 HildonDateEditorPrivate *priv;
1296 GtkAllocation f_alloc, img_alloc;
1298 GtkRequisition max_req;
1301 g_assert (GTK_IS_WIDGET (widget));
1302 g_assert (allocation != NULL);
1304 ed = HILDON_DATE_EDITOR (widget);
1305 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1307 widget->allocation = *allocation;
1309 gtk_widget_get_child_requisition (widget, &max_req);
1311 /* Center vertically */
1312 f_alloc.y = img_alloc.y = allocation->y +
1313 MAX (allocation->height - max_req.height, 0) / 2;
1315 /* Center horizontally */
1316 f_alloc.x = img_alloc.x = allocation->x +
1317 MAX (allocation->width - max_req.width, 0) / 2;
1319 /* allocate frame */
1320 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1321 gtk_widget_get_child_requisition (priv->frame, &req);
1323 f_alloc.width = req.width;
1324 f_alloc.height = max_req.height;
1325 gtk_widget_size_allocate (priv->frame, &f_alloc);
1329 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1330 gtk_widget_get_child_requisition (priv->d_button_image,
1333 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1334 img_alloc.width = req.width;
1335 img_alloc.height = max_req.height;
1336 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1339 /* FIXME: We really should not alloc delimeters by hand (since they
1340 are not our own children, but we need to force to appear
1341 higher. This ugly hack is needed to compensate the forced
1342 height in size_request. */
1343 for (iter = priv->delims; iter; iter = iter->next)
1346 GtkAllocation alloc;
1348 delim = GTK_WIDGET (iter->data);
1349 alloc = delim->allocation;
1350 alloc.height = max_req.height;
1351 alloc.y = priv->d_entry->allocation.y - 2;
1353 gtk_widget_size_allocate (delim, &alloc);
1358 * hildon_date_editor_set_year:
1359 * @editor: the @HildonDateEditor widget
1362 * Sets the year shown in the editor.
1364 * Returns: TRUE if the year is valid and has been set.
1367 hildon_date_editor_set_year (HildonDateEditor *editor,
1370 HildonDateEditorPrivate *priv;
1371 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1373 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1376 if (g_date_valid_dmy (priv->day, priv->month, year))
1381 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1383 /* We apply the new day, but do not want automatic focus move
1384 etc to take place */
1385 g_signal_handlers_block_by_func (priv->y_entry,
1386 (gpointer) hildon_date_editor_entry_changed, editor);
1387 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1388 g_signal_handlers_unblock_by_func (priv->y_entry,
1389 (gpointer) hildon_date_editor_entry_changed, editor);
1391 g_object_notify (G_OBJECT(editor), "year");
1399 * hildon_date_editor_set_month:
1400 * @editor: the @HildonDateEditor widget
1403 * Sets the month shown in the editor.
1405 * Returns: TRUE if the month is valid and has been set.
1408 hildon_date_editor_set_month (HildonDateEditor *editor,
1411 HildonDateEditorPrivate *priv;
1412 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1413 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1416 if (g_date_valid_dmy (priv->day, month, priv->year))
1421 priv->month = month;
1422 g_date_set_dmy (&date, priv->day, month, priv->year);
1423 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1425 /* We apply the new day, but do not want automatic focus move
1426 etc to take place */
1427 g_signal_handlers_block_by_func (priv->m_entry,
1428 (gpointer) hildon_date_editor_entry_changed, editor);
1429 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1430 g_signal_handlers_unblock_by_func (priv->m_entry,
1431 (gpointer) hildon_date_editor_entry_changed, editor);
1433 g_object_notify (G_OBJECT (editor), "month");
1440 * hildon_date_editor_set_day:
1441 * @editor: the @HildonDateEditor widget
1444 * Sets the day shown in the editor.
1446 * Returns: TRUE if the day is valid and has been set.
1449 hildon_date_editor_set_day (HildonDateEditor *editor,
1452 HildonDateEditorPrivate *priv;
1454 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1455 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1458 if (g_date_valid_dmy (day, priv->month, priv->year))
1464 g_date_set_dmy (&date, day, priv->month, priv->year);
1465 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1467 /* We apply the new day, but do not want automatic focus move
1468 etc to take place */
1469 g_signal_handlers_block_by_func (priv->d_entry,
1470 (gpointer) hildon_date_editor_entry_changed, editor);
1471 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1472 g_signal_handlers_unblock_by_func (priv->d_entry,
1473 (gpointer) hildon_date_editor_entry_changed, editor);
1475 g_object_notify (G_OBJECT(editor), "day");
1482 * hildon_date_editor_get_year:
1483 * @editor: the @HildonDateEditor widget
1485 * Returns: the current year shown in the editor.
1488 hildon_date_editor_get_year (HildonDateEditor *editor)
1490 HildonDateEditorPrivate *priv;
1491 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1493 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1496 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1500 * hildon_date_editor_get_month:
1501 * @editor: the @HildonDateEditor widget
1503 * Gets the month shown in the editor.
1505 * Returns: the current month shown in the editor.
1508 hildon_date_editor_get_month (HildonDateEditor *editor)
1510 HildonDateEditorPrivate *priv;
1511 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1513 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1516 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1520 * hildon_date_editor_get_day:
1521 * @editor: the @HildonDateEditor widget
1523 * Gets the day shown in the editor.
1525 * Returns: the current day shown in the editor
1528 hildon_date_editor_get_day (HildonDateEditor *editor)
1530 HildonDateEditorPrivate *priv;
1531 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1533 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1536 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));