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 priv->min_year = val;
587 /* Clamp current year */
588 if (hildon_date_editor_get_year (editor) < priv->min_year)
589 hildon_date_editor_set_year (editor, priv->min_year);
593 val = g_value_get_uint (value);
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;
1134 g_assert (HILDON_IS_DATE_EDITOR (data));
1135 g_assert (GTK_IS_ENTRY (widget));
1137 ed = HILDON_DATE_EDITOR (data);
1139 switch (event->keyval) {
1142 /* Ignore return value, since we want to handle event at all times.
1143 otherwise vkb would popup when the keyrepeat starts. */
1144 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1147 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1148 priv->skip_validation = TRUE;
1158 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1159 GdkEventFocus *event,
1162 HildonDateEditor *ed;
1163 HildonDateEditorPrivate *priv;
1165 g_assert (HILDON_IS_DATE_EDITOR (data));
1167 ed = HILDON_DATE_EDITOR (data);
1168 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1171 hildon_date_editor_entry_validate (widget, data);
1172 priv->skip_validation = FALSE;
1178 hildon_date_editor_date_error (HildonDateEditor *editor,
1179 HildonDateTimeError type)
1181 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1186 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1187 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1190 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1191 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1194 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1195 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1198 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1199 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1200 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1203 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1204 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1207 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1208 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1211 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1212 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1215 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1216 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1217 priv->min_year, priv->max_year);
1220 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1221 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1224 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1225 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1229 /*default error message ?*/
1237 hildon_date_editor_size_request (GtkWidget *widget,
1238 GtkRequisition *requisition)
1240 HildonDateEditor *ed;
1241 HildonDateEditorPrivate *priv;
1242 GtkRequisition f_req, img_req;
1244 g_assert (GTK_IS_WIDGET (widget));
1245 g_assert (requisition != NULL);
1247 ed = HILDON_DATE_EDITOR (widget);
1248 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1251 /* Our own children affect our size */
1252 gtk_widget_size_request (priv->frame, &f_req);
1253 gtk_widget_size_request (priv->d_button_image, &img_req);
1255 /* calculate our size */
1256 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1258 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1259 doing so would break current pixel specifications, since
1260 the text entry by itself is already 30px tall + then frame takes
1262 requisition->height = DATE_EDITOR_HEIGHT;
1266 hildon_date_editor_size_allocate (GtkWidget *widget,
1267 GtkAllocation *allocation)
1269 HildonDateEditor *ed;
1270 HildonDateEditorPrivate *priv;
1271 GtkAllocation f_alloc, img_alloc;
1273 GtkRequisition max_req;
1276 g_assert (GTK_IS_WIDGET (widget));
1277 g_assert (allocation != NULL);
1279 ed = HILDON_DATE_EDITOR (widget);
1280 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1282 widget->allocation = *allocation;
1284 gtk_widget_get_child_requisition (widget, &max_req);
1286 /* Center vertically */
1287 f_alloc.y = img_alloc.y = allocation->y +
1288 MAX (allocation->height - max_req.height, 0) / 2;
1290 /* Center horizontally */
1291 f_alloc.x = img_alloc.x = allocation->x +
1292 MAX (allocation->width - max_req.width, 0) / 2;
1294 /* allocate frame */
1295 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1296 gtk_widget_get_child_requisition (priv->frame, &req);
1298 f_alloc.width = req.width;
1299 f_alloc.height = max_req.height;
1300 gtk_widget_size_allocate (priv->frame, &f_alloc);
1304 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1305 gtk_widget_get_child_requisition (priv->d_button_image,
1308 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1309 img_alloc.width = req.width;
1310 img_alloc.height = max_req.height;
1311 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1314 /* FIXME: We really should not alloc delimeters by hand (since they
1315 are not our own children, but we need to force to appear
1316 higher. This ugly hack is needed to compensate the forced
1317 height in size_request. */
1318 for (iter = priv->delims; iter; iter = iter->next)
1321 GtkAllocation alloc;
1323 delim = GTK_WIDGET (iter->data);
1324 alloc = delim->allocation;
1325 alloc.height = max_req.height;
1326 alloc.y = priv->d_entry->allocation.y - 2;
1328 gtk_widget_size_allocate (delim, &alloc);
1333 * hildon_date_editor_set_year:
1334 * @editor: the @HildonDateEditor widget
1337 * Sets the year shown in the editor.
1339 * Returns: TRUE if the year is valid and has been set.
1342 hildon_date_editor_set_year (HildonDateEditor *editor,
1345 HildonDateEditorPrivate *priv;
1346 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1348 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1351 if (g_date_valid_dmy (priv->day, priv->month, year))
1356 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1358 /* We apply the new day, but do not want automatic focus move
1359 etc to take place */
1360 g_signal_handlers_block_by_func (priv->y_entry,
1361 (gpointer) hildon_date_editor_entry_changed, editor);
1362 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1363 g_signal_handlers_unblock_by_func (priv->y_entry,
1364 (gpointer) hildon_date_editor_entry_changed, editor);
1366 g_object_notify (G_OBJECT(editor), "year");
1374 * hildon_date_editor_set_month:
1375 * @editor: the @HildonDateEditor widget
1378 * Sets the month shown in the editor.
1380 * Returns: TRUE if the month is valid and has been set.
1383 hildon_date_editor_set_month (HildonDateEditor *editor,
1386 HildonDateEditorPrivate *priv;
1387 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1388 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1391 if (g_date_valid_dmy (priv->day, month, priv->year))
1396 priv->month = month;
1397 g_date_set_dmy (&date, priv->day, month, priv->year);
1398 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1400 /* We apply the new day, but do not want automatic focus move
1401 etc to take place */
1402 g_signal_handlers_block_by_func (priv->m_entry,
1403 (gpointer) hildon_date_editor_entry_changed, editor);
1404 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1405 g_signal_handlers_unblock_by_func (priv->m_entry,
1406 (gpointer) hildon_date_editor_entry_changed, editor);
1408 g_object_notify (G_OBJECT (editor), "month");
1415 * hildon_date_editor_set_day:
1416 * @editor: the @HildonDateEditor widget
1419 * Sets the day shown in the editor.
1421 * Returns: TRUE if the day is valid and has been set.
1424 hildon_date_editor_set_day (HildonDateEditor *editor,
1427 HildonDateEditorPrivate *priv;
1429 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1430 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1433 if (g_date_valid_dmy (day, priv->month, priv->year))
1439 g_date_set_dmy (&date, day, priv->month, priv->year);
1440 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1442 /* We apply the new day, but do not want automatic focus move
1443 etc to take place */
1444 g_signal_handlers_block_by_func (priv->d_entry,
1445 (gpointer) hildon_date_editor_entry_changed, editor);
1446 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1447 g_signal_handlers_unblock_by_func (priv->d_entry,
1448 (gpointer) hildon_date_editor_entry_changed, editor);
1450 g_object_notify (G_OBJECT(editor), "day");
1457 * hildon_date_editor_get_year:
1458 * @editor: the @HildonDateEditor widget
1460 * Returns: the current year shown in the editor.
1463 hildon_date_editor_get_year (HildonDateEditor *editor)
1465 HildonDateEditorPrivate *priv;
1466 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1468 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1471 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1475 * hildon_date_editor_get_month:
1476 * @editor: the @HildonDateEditor widget
1478 * Gets the month shown in the editor.
1480 * Returns: the current month shown in the editor.
1483 hildon_date_editor_get_month (HildonDateEditor *editor)
1485 HildonDateEditorPrivate *priv;
1486 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1488 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1491 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1495 * hildon_date_editor_get_day:
1496 * @editor: the @HildonDateEditor widget
1498 * Gets the day shown in the editor.
1500 * Returns: the current day shown in the editor
1503 hildon_date_editor_get_day (HildonDateEditor *editor)
1505 HildonDateEditorPrivate *priv;
1506 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1508 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1511 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));