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 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 ();
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 g_return_if_fail (val > priv->max_year);
587 priv->min_year = val;
588 /* Clamp current year */
589 if (hildon_date_editor_get_year (editor) < priv->min_year)
590 hildon_date_editor_set_year (editor, priv->min_year);
594 val = g_value_get_uint (value);
595 g_return_if_fail (val < priv->min_year);
596 priv->max_year = val;
597 /* Clamp current year */
598 if (hildon_date_editor_get_year (editor) > priv->max_year)
599 hildon_date_editor_set_year (editor, priv->max_year);
603 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
609 hildon_date_editor_get_property (GObject *object,
614 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
615 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
620 g_value_set_uint (value, hildon_date_editor_get_year (editor));
624 g_value_set_uint (value, hildon_date_editor_get_month (editor));
628 g_value_set_uint (value, hildon_date_editor_get_day (editor));
632 g_value_set_uint (value, priv->min_year);
636 g_value_set_uint (value, priv->max_year);
640 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
646 hildon_child_forall (GtkContainer *container,
647 gboolean include_internals,
648 GtkCallback callback,
649 gpointer callback_data)
651 HildonDateEditor *editor;
652 HildonDateEditorPrivate *priv;
654 g_assert (HILDON_IS_DATE_EDITOR (container));
657 editor = HILDON_DATE_EDITOR (container);
658 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
661 if (include_internals) {
662 (*callback) (priv->frame, callback_data);
663 (*callback) (priv->d_button_image, callback_data);
668 hildon_date_editor_destroy (GtkObject *self)
670 HildonDateEditorPrivate *priv;
672 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
676 gtk_widget_unparent (priv->frame);
679 if (priv->d_button_image) {
680 gtk_widget_unparent (priv->d_button_image);
681 priv->d_button_image = NULL;
684 g_list_free (priv->delims);
688 if (GTK_OBJECT_CLASS (parent_class)->destroy)
689 GTK_OBJECT_CLASS (parent_class)->destroy (self);
693 * hildon_date_editor_new:
695 * Creates a new date editor. The current system date
696 * is shown in the editor.
698 * Returns: pointer to a new @HildonDateEditor widget.
701 hildon_date_editor_new (void)
703 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
707 * hildon_date_editor_set_date:
708 * @date: the @HildonDateEditor widget
713 * Sets the date shown in the editor.
716 hildon_date_editor_set_date (HildonDateEditor *editor,
721 HildonDateEditorPrivate *priv;
723 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
725 /* This function cannot be implemented by calling
726 component setters, since applying the individual
727 values one by one can make the date temporarily
728 invalid (depending on what the previous values were),
729 which in turn causes that the desired date
730 is not set (even though it's valid). We must set all the
731 components at one go and not try to do any validation etc
734 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
737 if (g_date_valid_dmy (day, month, year))
746 g_date_set_dmy (&date, day, month, year);
748 /* We apply the new values, but do not want automatic focus move
750 g_snprintf (buffer, sizeof (buffer), "%04d", year);
751 g_signal_handlers_block_by_func (priv->y_entry,
752 (gpointer) hildon_date_editor_entry_changed, editor);
753 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
754 g_signal_handlers_unblock_by_func (priv->y_entry,
755 (gpointer) hildon_date_editor_entry_changed, editor);
757 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
758 g_signal_handlers_block_by_func (priv->m_entry,
759 (gpointer) hildon_date_editor_entry_changed, editor);
760 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
761 g_signal_handlers_unblock_by_func (priv->m_entry,
762 (gpointer) hildon_date_editor_entry_changed, editor);
764 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
765 g_signal_handlers_block_by_func (priv->d_entry,
766 (gpointer) hildon_date_editor_entry_changed, editor);
767 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
768 g_signal_handlers_unblock_by_func (priv->d_entry,
769 (gpointer) hildon_date_editor_entry_changed, editor);
771 g_object_notify (G_OBJECT (editor), "year");
772 g_object_notify (G_OBJECT (editor), "month");
773 g_object_notify (G_OBJECT (editor), "day");
778 * hildon_date_editor_get_date:
779 * @date: the @HildonDateEditor widget
784 * Gets the date represented by the date editor.
785 * You can pass NULL to any of the pointers if
786 * you're not interested in obtaining it.
790 hildon_date_editor_get_date (HildonDateEditor *date,
795 HildonDateEditorPrivate *priv;
797 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
799 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
801 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
802 is unclear. They do not neccesarily match and still the texts are
803 used as return values and members for some internal validation!!
804 At least a partly reason is to allow empty text to become
805 0 return value, while members are restricted to valid ranges?!
806 However, if we change the current way, we are likely to break
807 some applications if they rely on some specific way how this
808 widget currently handles empty values and temporarily invalid values.
810 The key issue is this: What should the _get methods return while
811 user is editing a field and the result is incomplete. The
812 partial result? The last good result? If we return partial result
813 we also need a way to inform if the date is not valid. Current
814 implementation is some kind of hybrid of these two...
817 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
819 easily fails, since set_day tries to force validity while get_day
822 Proposal: Always return the same values that are shown in the
823 fields. We add a separate flag (Or use GDate) to
824 indicate if the current date is valid. This would allow
825 setters to make the date invalid as well. */
828 *year = /*priv->year;*/
829 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
831 *month = /*priv->month;*/
832 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
834 *day = /*priv->day;*/
835 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
838 /* icon button press event */
840 hildon_date_editor_icon_press (GtkWidget *widget,
843 g_assert (GTK_IS_WIDGET (widget));
844 g_assert (HILDON_IS_DATE_EDITOR (data));
846 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
852 hildon_date_editor_entry_focus_in (GtkWidget *widget,
853 GdkEventFocus *event,
856 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
863 popup_calendar_dialog (HildonDateEditor *ed)
865 guint y = 0, m = 0, d = 0;
871 hildon_date_editor_get_date (ed, &y, &m, &d);
873 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
874 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
876 g_value_init (&val, G_TYPE_INT);
877 /* Set max/min year in calendar popup to date editor values */
878 g_object_get_property (G_OBJECT (ed), "min-year", &val);
879 g_object_set_property (G_OBJECT (popup), "min-year", &val);
880 g_object_get_property (G_OBJECT (ed), "max-year", &val);
881 g_object_set_property (G_OBJECT (popup), "max-year", &val);
883 /* Pop up calendar */
884 result = gtk_dialog_run (GTK_DIALOG (popup));
886 case GTK_RESPONSE_OK:
887 case GTK_RESPONSE_ACCEPT:
888 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
890 hildon_date_editor_set_date (ed, y, m, d);
893 gtk_widget_destroy (popup);
896 /* button released */
898 hildon_date_editor_released (GtkWidget *widget,
901 HildonDateEditor *ed;
903 g_assert (GTK_IS_WIDGET (widget));
904 g_assert (HILDON_IS_DATE_EDITOR (data));
906 ed = HILDON_DATE_EDITOR (data);
908 /* restores the icon state. The clicked cycle raises the dialog */
909 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
914 /* button released */
916 hildon_date_editor_clicked (GtkWidget *widget,
919 HildonDateEditor *ed;
921 g_assert (GTK_IS_WIDGET (widget));
922 g_assert (HILDON_IS_DATE_EDITOR (data));
924 ed = HILDON_DATE_EDITOR (data);
926 /* restores the non-clicked button state and raises the dialog */
927 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
928 popup_calendar_dialog (ed);
933 /* This is called whenever some editor filed loses the focus and
934 when the all of the fields are filled.
935 Earlier this was called whenever an entry changed */
936 /* FIXME: Validation on focus_out is broken by concept */
938 hildon_date_editor_entry_validate (GtkWidget *widget,
941 HildonDateEditor *ed;
942 HildonDateEditorPrivate *priv;
943 gint d, m, y, max_days;
944 gboolean r; /* temp return values for signals */
946 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
948 g_assert (HILDON_IS_DATE_EDITOR (data));
949 g_assert (GTK_IS_ENTRY (widget));
951 ed = HILDON_DATE_EDITOR (data);
952 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
955 if (priv->skip_validation)
958 /*check if the calling entry is empty*/
959 text = gtk_entry_get_text (GTK_ENTRY (widget));
960 if(text == NULL || text[0] == 0)
962 if (widget == priv->d_entry)
963 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
964 else if(widget == priv->m_entry)
965 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
967 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
969 /* restore empty entry to safe value */
970 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
974 /* Ok, we now check validity. Some fields can be empty */
975 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
976 if (text == NULL || text[0] == 0) return error_code;
978 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
979 if (text == NULL || text[0] == 0) return error_code;
981 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
982 if (text == NULL || text[0] == 0) return error_code;
985 /* Did it actually change? */
986 if (d != priv->day || m != priv->month || y != priv->year)
988 /* We could/should use hildon_date_editor_set_year and such functions
989 * to set the date, instead of use gtk_entry_set_text, and then change
990 * the priv member but hildon_date_editor_set_year and such functions
991 * check if the date is valid, we do want to do date validation check
992 * here according to spec */
995 if (widget == priv->m_entry) {
997 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1001 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1007 if(widget == priv->y_entry) {
1008 if (y < priv->min_year) {
1009 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1012 else if (y > priv->max_year) {
1013 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1018 /* Validate day. We have to do this in every case, since
1019 changing month or year can make the day number to be invalid */
1020 max_days = g_date_get_days_in_month (m,y);
1022 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1025 else if (d > max_days) {
1027 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1030 else { /* the date does not exist (is invalid) */
1031 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1032 /* check what was changed and restore previous value */
1033 if (widget == priv->y_entry)
1035 else if (widget == priv->m_entry)
1042 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1044 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1046 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1050 /* Fix and reformat the date after error signal is processed.
1051 reformatting can be needed even in a such case that numerical
1052 values of the date components are the same as earlier. */
1053 hildon_date_editor_set_date (ed, y, m, d);
1059 hildon_date_editor_entry_select_all (GtkWidget *widget)
1061 GDK_THREADS_ENTER ();
1063 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1065 GDK_THREADS_LEAVE ();
1070 /* When entry becomes full, we move the focus to the next field.
1071 If we are on the last field, the whole contents are validated. */
1073 hildon_date_editor_entry_changed (GtkEditable *ed,
1078 HildonDateEditorPrivate *priv;
1080 g_assert (GTK_IS_ENTRY (ed));
1081 g_assert (HILDON_IS_DATE_EDITOR (data));
1083 entry = GTK_ENTRY (ed);
1085 /* If day entry is full, move to next entry or validate */
1086 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1088 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1089 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1091 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1093 priv->skip_validation = TRUE;
1094 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1100 hildon_date_editor_keyrelease (GtkWidget *widget,
1104 HildonDateEditor *ed;
1105 HildonDateEditorPrivate *priv;
1107 g_return_val_if_fail (data, FALSE);
1108 g_return_val_if_fail (widget, FALSE);
1110 ed = HILDON_DATE_EDITOR (data);
1111 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1113 if (event->keyval == GDK_KP_Enter ||
1114 event->keyval == GDK_Return ||
1115 event->keyval == GDK_ISO_Enter) {
1116 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1118 popup_calendar_dialog (ed);
1121 } else if (event->keyval == GDK_Escape)
1122 priv->skip_validation = FALSE;
1127 /* keyboard handling */
1129 hildon_date_editor_keypress (GtkWidget *widget,
1133 HildonDateEditor *ed;
1134 HildonDateEditorPrivate *priv;
1138 g_assert (HILDON_IS_DATE_EDITOR (data));
1139 g_assert (GTK_IS_ENTRY (widget));
1141 ed = HILDON_DATE_EDITOR (data);
1142 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1143 pos = gtk_editable_get_position (GTK_EDITABLE (widget));
1146 /* Show error message in case the key pressed is not allowed
1147 (only digits and control characters are allowed )*/
1148 if (!g_unichar_isdigit (event->keyval) && ! (event->keyval & 0xF000)) {
1149 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_INVALID_CHAR, &r);
1153 switch (event->keyval) {
1156 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_LEFT);
1162 if (pos >= g_utf8_strlen (gtk_entry_get_text (GTK_ENTRY (widget)), -1)) {
1163 (void) gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1169 /* Ignore return value, since we want to handle event at all times.
1170 otherwise vkb would popup when the keyrepeat starts. */
1171 (void) hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1175 priv->skip_validation = TRUE;
1185 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1186 GdkEventFocus *event,
1189 HildonDateEditor *ed;
1190 HildonDateEditorPrivate *priv;
1192 g_assert (HILDON_IS_DATE_EDITOR (data));
1194 ed = HILDON_DATE_EDITOR (data);
1195 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1198 hildon_date_editor_entry_validate (widget, data);
1199 priv->skip_validation = FALSE;
1205 hildon_date_editor_date_error (HildonDateEditor *editor,
1206 HildonDateTimeError type)
1208 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1213 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1214 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1217 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1218 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1221 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1222 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1225 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1226 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1227 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1230 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1231 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1234 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1235 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1238 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1239 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1242 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1243 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1244 priv->min_year, priv->max_year);
1247 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1248 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1251 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1252 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1256 /*default error message ?*/
1264 hildon_date_editor_size_request (GtkWidget *widget,
1265 GtkRequisition *requisition)
1267 HildonDateEditor *ed;
1268 HildonDateEditorPrivate *priv;
1269 GtkRequisition f_req, img_req;
1271 g_assert (GTK_IS_WIDGET (widget));
1272 g_assert (requisition != NULL);
1274 ed = HILDON_DATE_EDITOR (widget);
1275 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1278 /* Our own children affect our size */
1279 gtk_widget_size_request (priv->frame, &f_req);
1280 gtk_widget_size_request (priv->d_button_image, &img_req);
1282 /* calculate our size */
1283 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1285 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1286 doing so would break current pixel specifications, since
1287 the text entry by itself is already 30px tall + then frame takes
1289 requisition->height = DATE_EDITOR_HEIGHT;
1293 hildon_date_editor_size_allocate (GtkWidget *widget,
1294 GtkAllocation *allocation)
1296 HildonDateEditor *ed;
1297 HildonDateEditorPrivate *priv;
1298 GtkAllocation f_alloc, img_alloc;
1300 GtkRequisition max_req;
1303 g_assert (GTK_IS_WIDGET (widget));
1304 g_assert (allocation != NULL);
1306 ed = HILDON_DATE_EDITOR (widget);
1307 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1309 widget->allocation = *allocation;
1311 gtk_widget_get_child_requisition (widget, &max_req);
1313 /* Center vertically */
1314 f_alloc.y = img_alloc.y = allocation->y +
1315 MAX (allocation->height - max_req.height, 0) / 2;
1317 /* Center horizontally */
1318 f_alloc.x = img_alloc.x = allocation->x +
1319 MAX (allocation->width - max_req.width, 0) / 2;
1321 /* allocate frame */
1322 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1323 gtk_widget_get_child_requisition (priv->frame, &req);
1325 f_alloc.width = req.width;
1326 f_alloc.height = max_req.height;
1327 gtk_widget_size_allocate (priv->frame, &f_alloc);
1331 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1332 gtk_widget_get_child_requisition (priv->d_button_image,
1335 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1336 img_alloc.width = req.width;
1337 img_alloc.height = max_req.height;
1338 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1341 /* FIXME: We really should not alloc delimeters by hand (since they
1342 are not our own children, but we need to force to appear
1343 higher. This ugly hack is needed to compensate the forced
1344 height in size_request. */
1345 for (iter = priv->delims; iter; iter = iter->next)
1348 GtkAllocation alloc;
1350 delim = GTK_WIDGET (iter->data);
1351 alloc = delim->allocation;
1352 alloc.height = max_req.height;
1353 alloc.y = priv->d_entry->allocation.y - 2;
1355 gtk_widget_size_allocate (delim, &alloc);
1360 * hildon_date_editor_set_year:
1361 * @editor: the @HildonDateEditor widget
1364 * Sets the year shown in the editor.
1366 * Returns: TRUE if the year is valid and has been set.
1369 hildon_date_editor_set_year (HildonDateEditor *editor,
1372 HildonDateEditorPrivate *priv;
1373 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1375 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1378 if (g_date_valid_dmy (priv->day, priv->month, year))
1383 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1385 /* We apply the new day, but do not want automatic focus move
1386 etc to take place */
1387 g_signal_handlers_block_by_func (priv->y_entry,
1388 (gpointer) hildon_date_editor_entry_changed, editor);
1389 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1390 g_signal_handlers_unblock_by_func (priv->y_entry,
1391 (gpointer) hildon_date_editor_entry_changed, editor);
1393 g_object_notify (G_OBJECT(editor), "year");
1401 * hildon_date_editor_set_month:
1402 * @editor: the @HildonDateEditor widget
1405 * Sets the month shown in the editor.
1407 * Returns: TRUE if the month is valid and has been set.
1410 hildon_date_editor_set_month (HildonDateEditor *editor,
1413 HildonDateEditorPrivate *priv;
1414 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1415 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1418 if (g_date_valid_dmy (priv->day, month, priv->year))
1423 priv->month = month;
1424 g_date_set_dmy (&date, priv->day, month, priv->year);
1425 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1427 /* We apply the new day, but do not want automatic focus move
1428 etc to take place */
1429 g_signal_handlers_block_by_func (priv->m_entry,
1430 (gpointer) hildon_date_editor_entry_changed, editor);
1431 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1432 g_signal_handlers_unblock_by_func (priv->m_entry,
1433 (gpointer) hildon_date_editor_entry_changed, editor);
1435 g_object_notify (G_OBJECT (editor), "month");
1442 * hildon_date_editor_set_day:
1443 * @editor: the @HildonDateEditor widget
1446 * Sets the day shown in the editor.
1448 * Returns: TRUE if the day is valid and has been set.
1451 hildon_date_editor_set_day (HildonDateEditor *editor,
1454 HildonDateEditorPrivate *priv;
1456 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1457 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1460 if (g_date_valid_dmy (day, priv->month, priv->year))
1466 g_date_set_dmy (&date, day, priv->month, priv->year);
1467 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1469 /* We apply the new day, but do not want automatic focus move
1470 etc to take place */
1471 g_signal_handlers_block_by_func (priv->d_entry,
1472 (gpointer) hildon_date_editor_entry_changed, editor);
1473 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1474 g_signal_handlers_unblock_by_func (priv->d_entry,
1475 (gpointer) hildon_date_editor_entry_changed, editor);
1477 g_object_notify (G_OBJECT(editor), "day");
1484 * hildon_date_editor_get_year:
1485 * @editor: the @HildonDateEditor widget
1487 * Returns: the current year shown in the editor.
1490 hildon_date_editor_get_year (HildonDateEditor *editor)
1492 HildonDateEditorPrivate *priv;
1493 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1495 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1498 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1502 * hildon_date_editor_get_month:
1503 * @editor: the @HildonDateEditor widget
1505 * Gets the month shown in the editor.
1507 * Returns: the current month shown in the editor.
1510 hildon_date_editor_get_month (HildonDateEditor *editor)
1512 HildonDateEditorPrivate *priv;
1513 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1515 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1518 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1522 * hildon_date_editor_get_day:
1523 * @editor: the @HildonDateEditor widget
1525 * Gets the day shown in the editor.
1527 * Returns: the current day shown in the editor
1530 hildon_date_editor_get_day (HildonDateEditor *editor)
1532 HildonDateEditorPrivate *priv;
1533 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1535 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1538 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));