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(PACKAGE, string)
61 #define ENTRY_BORDERS 11
63 #define DATE_EDITOR_HEIGHT 30
65 #define DAY_ENTRY_WIDTH 2
67 #define MONTH_ENTRY_WIDTH 2
69 #define YEAR_ENTRY_WIDTH 4
71 #define DEFAULT_MIN_YEAR 1970
73 #define DEFAULT_MAX_YEAR 2037
75 static GtkContainerClass* parent_class;
78 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
81 hildon_date_editor_init (HildonDateEditor *editor);
84 hildon_date_editor_icon_press (GtkWidget *widget,
88 hildon_date_editor_released (GtkWidget *widget,
92 hildon_date_editor_keypress (GtkWidget *widget,
97 hildon_date_editor_keyrelease (GtkWidget *widget,
101 hildon_date_editor_clicked (GtkWidget *widget,
105 hildon_date_editor_entry_validate (GtkWidget *widget,
109 hildon_date_editor_entry_changed (GtkEditable *widget,
113 hildon_date_editor_entry_focus_out (GtkWidget *widget,
114 GdkEventFocus *event,
118 hildon_date_editor_date_error (HildonDateEditor *editor,
119 HildonDateTimeError type);
122 hildon_date_editor_entry_focus_in (GtkWidget *widget,
123 GdkEventFocus *event,
127 hildon_date_editor_get_property (GObject *object,
133 hildon_date_editor_set_property (GObject *object,
138 hildon_child_forall (GtkContainer *container,
139 gboolean include_internals,
140 GtkCallback callback,
141 gpointer callback_data);
144 hildon_date_editor_destroy (GtkObject *self);
147 hildon_date_editor_size_allocate (GtkWidget *widget,
148 GtkAllocation *allocation);
151 hildon_date_editor_size_request (GtkWidget *widget,
152 GtkRequisition *requisition);
155 hildon_date_editor_entry_select_all (GtkWidget *widget);
157 /* Property indices */
174 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
177 hildon_date_editor_get_type (void)
179 static GType editor_type = 0;
182 static const GTypeInfo editor_info = {
183 sizeof (HildonDateEditorClass),
184 NULL, /* base_init */
185 NULL, /* base_finalize */
186 (GClassInitFunc) hildon_date_editor_class_init,
187 NULL, /* class_finalize */
188 NULL, /* class_data */
189 sizeof (HildonDateEditor),
191 (GInstanceInitFunc) hildon_date_editor_init,
193 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
202 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
204 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
205 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
206 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
208 parent_class = g_type_class_peek_parent (editor_class);
210 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
212 gobject_class->set_property = hildon_date_editor_set_property;
213 gobject_class->get_property = hildon_date_editor_get_property;
214 widget_class->size_request = hildon_date_editor_size_request;
215 widget_class->size_allocate = hildon_date_editor_size_allocate;
216 widget_class->focus = hildon_private_composite_focus;
218 container_class->forall = hildon_child_forall;
219 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
221 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
223 date_editor_signals[DATE_ERROR] =
224 g_signal_new ("date-error",
225 G_OBJECT_CLASS_TYPE (gobject_class),
227 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
228 g_signal_accumulator_true_handled, NULL,
229 _hildon_marshal_BOOLEAN__ENUM,
230 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
233 * HildonDateEditor:year:
237 g_object_class_install_property (gobject_class, PROP_YEAR,
238 g_param_spec_uint ("year",
243 G_PARAM_READABLE | G_PARAM_WRITABLE));
246 * HildonDateEditor:month:
250 g_object_class_install_property (gobject_class, PROP_MONTH,
251 g_param_spec_uint ("month",
256 G_PARAM_READABLE | G_PARAM_WRITABLE));
259 * HildonDateEditor:day:
263 g_object_class_install_property (gobject_class, PROP_DAY,
264 g_param_spec_uint ("day",
269 G_PARAM_READABLE | G_PARAM_WRITABLE));
272 * HildonDateEditor:min-year:
274 * Minimum valid year.
276 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
277 g_param_spec_uint ("min-year",
278 "Minimum valid year",
279 "Minimum valid year",
285 * HildonDateEditor:max-year:
287 * Maximum valid year.
289 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
290 g_param_spec_uint ("max-year",
291 "Maximum valid year",
292 "Maximum valid year",
298 /* Forces setting of the icon to certain state. Used initially
299 and from the actual setter function */
301 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
306 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
307 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_WIDG);
309 priv->calendar_icon_pressed = pressed;
312 /* Sets the icon to given state (normal/pressed). Returns
313 info if the state actually changed. */
315 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
318 HildonDateEditorPrivate *priv;
320 g_assert (HILDON_IS_DATE_EDITOR (editor));
322 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
325 if (pressed != priv->calendar_icon_pressed) {
326 real_set_calendar_icon_state (priv, pressed);
333 /* Packing day, month and year entries depend on locale settings
334 We find out the order and all separators by converting a known
335 date to default format and inspecting the result string */
337 apply_locale_field_order (HildonDateEditorPrivate *priv)
339 GDate locale_test_date;
342 gchar *iter, *delim_text;
344 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
345 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
353 /* Try to convert the current location into number. */
354 value = strtoul (iter, &endp, 10);
356 /* If the conversion didn't progress or the detected value was
357 unknown (we used a fixed date, you remember), we treat
358 current position as a literal */
362 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
363 priv->d_entry, FALSE, FALSE, 0);
367 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
368 priv->m_entry, FALSE, FALSE, 0);
371 case 70: /* %x format uses only 2 numbers for some locales */
373 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
374 priv->y_entry, FALSE, FALSE, 0);
378 /* All non-number characters starting from current position
379 form the delimeter */
380 for (endp = iter; *endp; endp++)
381 if (g_ascii_isdigit (*endp))
384 /* Now endp points one place past the delimeter text */
385 delim_text = g_strndup (iter, endp - iter);
386 delim = gtk_label_new (delim_text);
387 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
388 delim, FALSE, FALSE, 0);
390 priv->delims = g_list_append (priv->delims, delim);
401 hildon_date_editor_init (HildonDateEditor *editor)
403 HildonDateEditorPrivate *priv;
406 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
409 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
411 gtk_widget_push_composite_child ();
413 /* initialize values */
414 g_date_clear (&cur_date, 1);
415 g_date_set_time (&cur_date, time (NULL));
417 priv->day = g_date_get_day (&cur_date);
418 priv->month = g_date_get_month (&cur_date);
419 priv->year = g_date_get_year (&cur_date);
420 priv->min_year = DEFAULT_MIN_YEAR;
421 priv->max_year = DEFAULT_MAX_YEAR;
424 priv->frame = gtk_frame_new (NULL);
425 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
427 priv->d_entry = gtk_entry_new ();
428 priv->m_entry = gtk_entry_new ();
429 priv->y_entry = gtk_entry_new ();
431 g_object_set (G_OBJECT(priv->d_entry), "input-mode",
432 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
433 g_object_set (G_OBJECT(priv->m_entry), "input-mode",
434 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
435 g_object_set (G_OBJECT(priv->y_entry), "input-mode",
436 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
439 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
440 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
441 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
443 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
444 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
445 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
447 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
448 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
449 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
451 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
452 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
453 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
455 priv->d_box_date = gtk_hbox_new (FALSE, 0);
457 priv->d_button_image = gtk_button_new ();
458 priv->calendar_icon = gtk_image_new ();
459 real_set_calendar_icon_state (priv, FALSE);
460 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
462 apply_locale_field_order (priv);
464 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
465 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
466 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
467 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
469 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
470 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
471 gtk_widget_show_all (priv->frame);
472 gtk_widget_show_all (priv->d_button_image);
474 /* image button signal connects */
475 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
476 G_CALLBACK (hildon_date_editor_icon_press), editor);
477 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
478 G_CALLBACK (hildon_date_editor_released), editor);
479 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
480 G_CALLBACK (hildon_date_editor_clicked), editor);
481 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
482 G_CALLBACK (hildon_date_editor_keypress), editor);
484 /* entry signal connects */
485 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
486 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
488 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
489 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
491 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
492 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
494 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
495 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
497 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
498 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
500 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
501 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
503 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
504 G_CALLBACK (hildon_date_editor_keypress), editor);
506 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
507 G_CALLBACK (hildon_date_editor_keypress), editor);
509 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
510 G_CALLBACK (hildon_date_editor_keypress), editor);
512 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
513 G_CALLBACK (hildon_date_editor_keyrelease), editor);
515 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
516 G_CALLBACK(hildon_date_editor_keyrelease), editor);
518 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
519 G_CALLBACK (hildon_date_editor_keyrelease), editor);
521 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
523 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
524 G_CALLBACK (hildon_date_editor_entry_changed), editor);
526 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
527 G_CALLBACK (hildon_date_editor_entry_changed), editor);
529 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
530 G_CALLBACK (hildon_date_editor_entry_changed), editor);
532 gtk_widget_pop_composite_child ();
536 hildon_date_editor_set_property (GObject *object,
541 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
542 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
550 hildon_date_editor_set_year (editor, g_value_get_uint (value));
554 hildon_date_editor_set_month (editor, g_value_get_uint (value));
558 hildon_date_editor_set_day (editor, g_value_get_uint (value));
562 val = g_value_get_uint (value);
563 g_return_if_fail (val > priv->max_year);
564 priv->min_year = val;
565 /* Clamp current year */
566 if (hildon_date_editor_get_year (editor) < priv->min_year)
567 hildon_date_editor_set_year (editor, priv->min_year);
571 val = g_value_get_uint (value);
572 g_return_if_fail (val < priv->min_year);
573 priv->max_year = val;
574 /* Clamp current year */
575 if (hildon_date_editor_get_year (editor) > priv->max_year)
576 hildon_date_editor_set_year (editor, priv->max_year);
580 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
586 hildon_date_editor_get_property (GObject *object,
591 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
592 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
597 g_value_set_uint (value, hildon_date_editor_get_year (editor));
601 g_value_set_uint (value, hildon_date_editor_get_month (editor));
605 g_value_set_uint (value, hildon_date_editor_get_day (editor));
609 g_value_set_uint (value, priv->min_year);
613 g_value_set_uint (value, priv->max_year);
617 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
622 static void hildon_child_forall (GtkContainer *container,
623 gboolean include_internals,
624 GtkCallback callback,
625 gpointer callback_data)
627 HildonDateEditor *editor;
628 HildonDateEditorPrivate *priv;
630 g_assert (HILDON_IS_DATE_EDITOR (container));
633 editor = HILDON_DATE_EDITOR (container);
634 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
637 if (include_internals) {
638 (*callback) (priv->frame, callback_data);
639 (*callback) (priv->d_button_image, callback_data);
644 hildon_date_editor_destroy (GtkObject *self)
646 HildonDateEditorPrivate *priv;
648 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
652 gtk_widget_unparent (priv->frame);
655 if (priv->d_button_image) {
656 gtk_widget_unparent (priv->d_button_image);
657 priv->d_button_image = NULL;
660 g_list_free (priv->delims);
664 if (GTK_OBJECT_CLASS (parent_class)->destroy)
665 GTK_OBJECT_CLASS (parent_class)->destroy (self);
669 * hildon_date_editor_new:
671 * Creates a new date editor. The current system date
672 * is shown in the editor.
674 * Returns: pointer to a new @HildonDateEditor widget.
677 hildon_date_editor_new (void)
679 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
683 * hildon_date_editor_set_date:
684 * @date: the @HildonDateEditor widget
689 * Sets the date shown in the editor.
692 hildon_date_editor_set_date (HildonDateEditor *editor,
697 HildonDateEditorPrivate *priv;
699 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
701 /* This function cannot be implemented by calling
702 component setters, since applying the individual
703 values one by one can make the date temporarily
704 invalid (depending on what the previous values were),
705 which in turn causes that the desired date
706 is not set (even though it's valid). We must set all the
707 components at one go and not try to do any validation etc
710 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
713 if (g_date_valid_dmy (day, month, year))
722 g_date_set_dmy (&date, day, month, year);
724 /* We apply the new values, but do not want automatic focus move
726 g_snprintf (buffer, sizeof (buffer), "%04d", year);
727 g_signal_handlers_block_by_func (priv->y_entry,
728 (gpointer) hildon_date_editor_entry_changed, editor);
729 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
730 g_signal_handlers_unblock_by_func (priv->y_entry,
731 (gpointer) hildon_date_editor_entry_changed, editor);
733 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
734 g_signal_handlers_block_by_func (priv->m_entry,
735 (gpointer) hildon_date_editor_entry_changed, editor);
736 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
737 g_signal_handlers_unblock_by_func (priv->m_entry,
738 (gpointer) hildon_date_editor_entry_changed, editor);
740 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
741 g_signal_handlers_block_by_func (priv->d_entry,
742 (gpointer) hildon_date_editor_entry_changed, editor);
743 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
744 g_signal_handlers_unblock_by_func (priv->d_entry,
745 (gpointer) hildon_date_editor_entry_changed, editor);
747 g_object_notify (G_OBJECT (editor), "year");
748 g_object_notify (G_OBJECT (editor), "month");
749 g_object_notify (G_OBJECT (editor), "day");
754 * hildon_date_editor_get_date:
755 * @date: the @HildonDateEditor widget
760 * Returns: the year, month, and day currently on the
761 * date editor. You can pass NULL to any of the pointers if
762 * you're not interested in obtaining it.
765 hildon_date_editor_get_date (HildonDateEditor *date,
770 HildonDateEditorPrivate *priv;
772 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
774 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
776 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
777 is unclear. They do not neccesarily match and still the texts are
778 used as return values and members for some internal validation!!
779 At least a partly reason is to allow empty text to become
780 0 return value, while members are restricted to valid ranges?!
781 However, if we change the current way, we are likely to break
782 some applications if they rely on some specific way how this
783 widget currently handles empty values and temporarily invalid values.
785 The key issue is this: What should the _get methods return while
786 user is editing a field and the result is incomplete. The
787 partial result? The last good result? If we return partial result
788 we also need a way to inform if the date is not valid. Current
789 implementation is some kind of hybrid of these two...
792 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
794 easily fails, since set_day tries to force validity while get_day
797 Proposal: Always return the same values that are shown in the
798 fields. We add a separate flag (Or use GDate) to
799 indicate if the current date is valid. This would allow
800 setters to make the date invalid as well. */
803 *year = /*priv->year;*/
804 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
806 *month = /*priv->month;*/
807 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
809 *day = /*priv->day;*/
810 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
813 /* icon button press event */
815 hildon_date_editor_icon_press (GtkWidget *widget,
818 g_assert (GTK_IS_WIDGET (widget));
819 g_assert (HILDON_IS_DATE_EDITOR (data));
821 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
827 hildon_date_editor_entry_focus_in (GtkWidget *widget,
828 GdkEventFocus *event,
831 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
838 popup_calendar_dialog (HildonDateEditor *ed)
840 guint y = 0, m = 0, d = 0;
846 hildon_date_editor_get_date (ed, &y, &m, &d);
848 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
849 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
851 g_value_init (&val, G_TYPE_INT);
852 /* Set max/min year in calendar popup to date editor values */
853 g_object_get_property (G_OBJECT (ed), "min-year", &val);
854 g_object_set_property (G_OBJECT (popup), "min-year", &val);
855 g_object_get_property (G_OBJECT (ed), "max-year", &val);
856 g_object_set_property (G_OBJECT (popup), "max-year", &val);
858 /* Pop up calendar */
859 result = gtk_dialog_run (GTK_DIALOG (popup));
861 case GTK_RESPONSE_OK:
862 case GTK_RESPONSE_ACCEPT:
863 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
865 hildon_date_editor_set_date (ed, y, m, d);
868 gtk_widget_destroy (popup);
871 /* button released */
873 hildon_date_editor_released (GtkWidget *widget,
876 HildonDateEditor *ed;
878 g_assert (GTK_IS_WIDGET (widget));
879 g_assert (HILDON_IS_DATE_EDITOR (data));
881 ed = HILDON_DATE_EDITOR (data);
883 /* restores the icon state. The clicked cycle raises the dialog */
884 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
889 /* button released */
891 hildon_date_editor_clicked (GtkWidget *widget,
894 HildonDateEditor *ed;
896 g_assert (GTK_IS_WIDGET (widget));
897 g_assert (HILDON_IS_DATE_EDITOR (data));
899 ed = HILDON_DATE_EDITOR (data);
901 /* restores the non-clicked button state and raises the dialog */
902 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
903 popup_calendar_dialog (ed);
908 /* This is called whenever some editor filed loses the focus and
909 when the all of the fields are filled.
910 Earlier this was called whenever an entry changed */
911 /* FIXME: Validation on focus_out is broken by concept */
913 hildon_date_editor_entry_validate (GtkWidget *widget,
916 HildonDateEditor *ed;
917 HildonDateEditorPrivate *priv;
918 gint d, m, y, max_days;
919 gboolean r; /* temp return values for signals */
921 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
923 g_assert (HILDON_IS_DATE_EDITOR (data));
924 g_assert (GTK_IS_ENTRY (widget));
926 ed = HILDON_DATE_EDITOR (data);
927 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
930 if (priv->skip_validation)
933 /*check if the calling entry is empty*/
934 text = gtk_entry_get_text (GTK_ENTRY (widget));
935 if(text == NULL || text[0] == 0)
937 if (widget == priv->d_entry)
938 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
939 else if(widget == priv->m_entry)
940 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
942 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
944 /* restore empty entry to safe value */
945 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
949 /* Ok, we now check validity. Some fields can be empty */
950 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
951 if (text == NULL || text[0] == 0) return error_code;
953 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
954 if (text == NULL || text[0] == 0) return error_code;
956 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
957 if (text == NULL || text[0] == 0) return error_code;
960 /* Did it actually change? */
961 if (d != priv->day || m != priv->month || y != priv->year)
963 /* We could/should use hildon_date_editor_set_year and such functions
964 * to set the date, instead of use gtk_entry_set_text, and then change
965 * the priv member but hildon_date_editor_set_year and such functions
966 * check if the date is valid, we do want to do date validation check
967 * here according to spec */
970 if (widget == priv->m_entry) {
972 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
976 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
982 if(widget == priv->y_entry) {
983 if (y < priv->min_year) {
984 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
987 else if (y > priv->max_year) {
988 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
993 /* Validate day. We have to do this in every case, since
994 changing month or year can make the day number to be invalid */
995 max_days = g_date_get_days_in_month (m,y);
997 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1000 else if (d > max_days) {
1002 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1005 else { /* the date does not exist (is invalid) */
1006 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1007 /* check what was changed and restore previous value */
1008 if (widget == priv->y_entry)
1010 else if (widget == priv->m_entry)
1017 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1019 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1021 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1025 /* Fix and reformat the date after error signal is processed.
1026 reformatting can be needed even in a such case that numerical
1027 values of the date components are the same as earlier. */
1028 hildon_date_editor_set_date (ed, y, m, d);
1032 /* When entry becomes full, we move the focus to the next field.
1033 If we are on the last field, the whole contents are validated. */
1035 hildon_date_editor_entry_changed (GtkEditable *ed,
1040 HildonDateEditorPrivate *priv;
1042 g_assert (GTK_IS_ENTRY (ed));
1043 g_assert (HILDON_IS_DATE_EDITOR (data));
1045 entry = GTK_ENTRY (ed);
1047 /* If day entry is full, move to next entry or validate */
1048 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1050 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1051 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1053 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1055 priv->skip_validation = TRUE;
1056 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1062 hildon_date_editor_keyrelease (GtkWidget *widget,
1066 HildonDateEditor *ed;
1067 HildonDateEditorPrivate *priv;
1069 g_return_val_if_fail (data, FALSE);
1070 g_return_val_if_fail (widget, FALSE);
1072 ed = HILDON_DATE_EDITOR (data);
1073 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1075 if (event->keyval == GDK_KP_Enter ||
1076 event->keyval == GDK_Return ||
1077 event->keyval == GDK_ISO_Enter) {
1078 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1080 popup_calendar_dialog (ed);
1083 } else if (event->keyval == GDK_Escape)
1084 priv->skip_validation = FALSE;
1089 /* keyboard handling */
1091 hildon_date_editor_keypress (GtkWidget *widget,
1095 HildonDateEditor *ed;
1096 HildonDateEditorPrivate *priv;
1100 g_assert (HILDON_IS_DATE_EDITOR (data));
1101 g_assert (GTK_IS_ENTRY (widget));
1103 ed = HILDON_DATE_EDITOR (data);
1104 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1105 pos = gtk_editable_get_position (GTK_EDITABLE (widget));
1108 /* Show error message in case the key pressed is not allowed
1109 (only digits and control characters are allowed )*/
1110 if (!g_unichar_isdigit (event->keyval) && ! (event->keyval & 0xF000)) {
1111 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1115 switch (event->keyval) {
1118 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_LEFT);
1124 if (pos >= g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (widget)), -1)) {
1125 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1131 /* Ignore return value, since we want to handle event at all times.
1132 otherwise vkb would popup when the keyrepeat starts. */
1133 (void) hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1137 priv->skip_validation = TRUE;
1147 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1148 GdkEventFocus *event,
1151 HildonDateEditor *ed;
1152 HildonDateEditorPrivate *priv;
1154 g_assert (HILDON_IS_DATE_EDITOR (data));
1156 ed = HILDON_DATE_EDITOR (data);
1157 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1160 hildon_date_editor_entry_validate (widget, data);
1161 priv->skip_validation = FALSE;
1167 hildon_date_editor_date_error (HildonDateEditor *editor,
1168 HildonDateTimeError type)
1170 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1175 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1176 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1179 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1180 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1183 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1184 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1187 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1188 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1189 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1192 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1193 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1196 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1197 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1200 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1201 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1204 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1205 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1206 priv->min_year, priv->max_year);
1209 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1210 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_illegal_character"));
1213 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1214 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1218 /*default error message ?*/
1226 hildon_date_editor_size_request (GtkWidget *widget,
1227 GtkRequisition *requisition)
1229 HildonDateEditor *ed;
1230 HildonDateEditorPrivate *priv;
1231 GtkRequisition f_req, img_req;
1233 g_assert (GTK_IS_WIDGET (widget));
1234 g_assert (requisition != NULL);
1236 ed = HILDON_DATE_EDITOR (widget);
1237 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1240 /* Our own children affect our size */
1241 gtk_widget_size_request (priv->frame, &f_req);
1242 gtk_widget_size_request (priv->d_button_image, &img_req);
1244 /* calculate our size */
1245 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1247 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1248 doing so would break current pixel specifications, since
1249 the text entry by itself is already 30px tall + then frame takes
1251 requisition->height = DATE_EDITOR_HEIGHT;
1255 hildon_date_editor_size_allocate (GtkWidget *widget,
1256 GtkAllocation *allocation)
1258 HildonDateEditor *ed;
1259 HildonDateEditorPrivate *priv;
1260 GtkAllocation f_alloc, img_alloc;
1262 GtkRequisition max_req;
1265 g_assert (GTK_IS_WIDGET (widget));
1266 g_assert (allocation != NULL);
1268 ed = HILDON_DATE_EDITOR (widget);
1269 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1271 widget->allocation = *allocation;
1273 gtk_widget_get_child_requisition (widget, &max_req);
1275 /* Center vertically */
1276 f_alloc.y = img_alloc.y = allocation->y +
1277 MAX (allocation->height - max_req.height, 0) / 2;
1279 /* Center horizontally */
1280 f_alloc.x = img_alloc.x = allocation->x +
1281 MAX (allocation->width - max_req.width, 0) / 2;
1283 /* allocate frame */
1284 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1285 gtk_widget_get_child_requisition (priv->frame, &req);
1287 f_alloc.width = req.width;
1288 f_alloc.height = max_req.height;
1289 gtk_widget_size_allocate (priv->frame, &f_alloc);
1293 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1294 gtk_widget_get_child_requisition (priv->d_button_image,
1297 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1298 img_alloc.width = req.width;
1299 img_alloc.height = max_req.height;
1300 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1303 /* FIXME: We really should not alloc delimeters by hand (since they
1304 are not our own children, but we need to force to appear
1305 higher. This ugly hack is needed to compensate the forced
1306 height in size_request. */
1307 for (iter = priv->delims; iter; iter = iter->next)
1310 GtkAllocation alloc;
1312 delim = GTK_WIDGET (iter->data);
1313 alloc = delim->allocation;
1314 alloc.height = max_req.height;
1315 alloc.y = priv->d_entry->allocation.y - 2;
1317 gtk_widget_size_allocate (delim, &alloc);
1322 * hildon_date_editor_set_year:
1323 * @editor: the @HildonDateEditor widget
1326 * Sets the year shown in the editor.
1328 * Returns: TRUE if the year is valid
1331 hildon_date_editor_set_year (HildonDateEditor *editor,
1334 HildonDateEditorPrivate *priv;
1335 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1337 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1340 if (g_date_valid_dmy (priv->day, priv->month, year))
1345 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1347 /* We apply the new day, but do not want automatic focus move
1348 etc to take place */
1349 g_signal_handlers_block_by_func (priv->y_entry,
1350 (gpointer) hildon_date_editor_entry_changed, editor);
1351 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1352 g_signal_handlers_unblock_by_func (priv->y_entry,
1353 (gpointer) hildon_date_editor_entry_changed, editor);
1355 g_object_notify (G_OBJECT(editor), "year");
1363 * hildon_date_editor_set_month:
1364 * @editor: the @HildonDateEditor widget
1367 * Sets the month shown in the editor.
1369 * Returns: TRUE if the month is valid
1372 hildon_date_editor_set_month (HildonDateEditor *editor,
1375 HildonDateEditorPrivate *priv;
1376 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1377 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1380 if (g_date_valid_dmy (priv->day, month, priv->year))
1385 priv->month = month;
1386 g_date_set_dmy (&date, priv->day, month, priv->year);
1387 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1389 /* We apply the new day, but do not want automatic focus move
1390 etc to take place */
1391 g_signal_handlers_block_by_func (priv->m_entry,
1392 (gpointer) hildon_date_editor_entry_changed, editor);
1393 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1394 g_signal_handlers_unblock_by_func (priv->m_entry,
1395 (gpointer) hildon_date_editor_entry_changed, editor);
1397 g_object_notify (G_OBJECT (editor), "month");
1404 * hildon_date_editor_set_day:
1405 * @editor: the @HildonDateEditor widget
1408 * Sets the day shown in the editor.
1410 * Returns: TRUE if the day is valid
1413 hildon_date_editor_set_day (HildonDateEditor *editor,
1416 HildonDateEditorPrivate *priv;
1418 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1419 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1422 if (g_date_valid_dmy (day, priv->month, priv->year))
1428 g_date_set_dmy (&date, day, priv->month, priv->year);
1429 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1431 /* We apply the new day, but do not want automatic focus move
1432 etc to take place */
1433 g_signal_handlers_block_by_func (priv->d_entry,
1434 (gpointer) hildon_date_editor_entry_changed, editor);
1435 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1436 g_signal_handlers_unblock_by_func (priv->d_entry,
1437 (gpointer) hildon_date_editor_entry_changed, editor);
1439 g_object_notify (G_OBJECT(editor), "day");
1446 * hildon_date_editor_get_year:
1447 * @editor: the @HildonDateEditor widget
1449 * Returns: the current year shown in the editor.
1452 hildon_date_editor_get_year (HildonDateEditor *editor)
1454 HildonDateEditorPrivate *priv;
1455 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1457 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1460 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1464 * hildon_date_editor_get_month:
1465 * @editor: the @HildonDateEditor widget
1467 * Gets the month shown in the editor.
1469 * Returns: the current month shown in the editor.
1472 hildon_date_editor_get_month (HildonDateEditor *editor)
1474 HildonDateEditorPrivate *priv;
1475 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1477 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1480 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1484 * hildon_date_editor_get_day:
1485 * @editor: the @HildonDateEditor widget
1487 * Gets the day shown in the editor.
1489 * Returns: the current day shown in the editor
1492 hildon_date_editor_get_day (HildonDateEditor *editor)
1494 HildonDateEditorPrivate *priv;
1495 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1497 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1500 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
1505 hildon_date_editor_entry_select_all (GtkWidget *widget)
1507 GDK_THREADS_ENTER ();
1509 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1511 GDK_THREADS_LEAVE ();