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);
168 hildon_date_editor_focus (GtkWidget *widget,
169 GtkDirectionType direction);
171 hildon_date_editor_entry_select_all (GtkWidget *widget);
173 /* Property indices */
190 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
193 * hildon_date_editor_get_type:
195 * Initializes and returns the type of a hildon date editor.
197 * @Returns: GType of #HildonDateEditor
200 hildon_date_editor_get_type (void)
202 static GType editor_type = 0;
205 static const GTypeInfo editor_info = {
206 sizeof (HildonDateEditorClass),
207 NULL, /* base_init */
208 NULL, /* base_finalize */
209 (GClassInitFunc) hildon_date_editor_class_init,
210 NULL, /* class_finalize */
211 NULL, /* class_data */
212 sizeof (HildonDateEditor),
214 (GInstanceInitFunc) hildon_date_editor_init,
216 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
225 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
227 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
228 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
229 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
231 parent_class = g_type_class_peek_parent (editor_class);
233 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
235 gobject_class->set_property = hildon_date_editor_set_property;
236 gobject_class->get_property = hildon_date_editor_get_property;
237 widget_class->size_request = hildon_date_editor_size_request;
238 widget_class->size_allocate = hildon_date_editor_size_allocate;
239 widget_class->focus = hildon_date_editor_focus;
241 container_class->forall = hildon_child_forall;
242 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
244 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
246 date_editor_signals[DATE_ERROR] =
247 g_signal_new ("date-error",
248 G_OBJECT_CLASS_TYPE (gobject_class),
250 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
251 g_signal_accumulator_true_handled, NULL,
252 _hildon_marshal_BOOLEAN__ENUM,
253 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
256 * HildonDateEditor:year:
260 g_object_class_install_property (gobject_class, PROP_YEAR,
261 g_param_spec_uint ("year",
266 G_PARAM_READABLE | G_PARAM_WRITABLE));
269 * HildonDateEditor:month:
273 g_object_class_install_property (gobject_class, PROP_MONTH,
274 g_param_spec_uint ("month",
279 G_PARAM_READABLE | G_PARAM_WRITABLE));
282 * HildonDateEditor:day:
286 g_object_class_install_property (gobject_class, PROP_DAY,
287 g_param_spec_uint ("day",
292 G_PARAM_READABLE | G_PARAM_WRITABLE));
295 * HildonDateEditor:min-year:
297 * Minimum valid year.
299 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
300 g_param_spec_uint ("min-year",
301 "Minimum valid year",
302 "Minimum valid year",
308 * HildonDateEditor:max-year:
310 * Maximum valid year.
312 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
313 g_param_spec_uint ("max-year",
314 "Maximum valid year",
315 "Maximum valid year",
321 /* Forces setting of the icon to certain state. Used initially
322 and from the actual setter function */
324 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
329 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
330 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_SMALL);
332 priv->calendar_icon_pressed = pressed;
335 /* Sets the icon to given state (normal/pressed). Returns
336 info if the state actually changed. */
338 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
341 HildonDateEditorPrivate *priv;
343 g_assert (HILDON_IS_DATE_EDITOR (editor));
345 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
348 if (pressed != priv->calendar_icon_pressed) {
349 real_set_calendar_icon_state (priv, pressed);
356 /* Packing day, month and year entries depend on locale settings
357 We find out the order and all separators by converting a known
358 date to default format and inspecting the result string */
360 apply_locale_field_order (HildonDateEditorPrivate *priv)
362 GDate locale_test_date;
365 gchar *iter, *delim_text;
367 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
368 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
376 /* Try to convert the current location into number. */
377 value = strtoul (iter, &endp, 10);
379 /* If the conversion didn't progress or the detected value was
380 unknown (we used a fixed date, you remember), we treat
381 current position as a literal */
385 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
386 priv->d_entry, FALSE, FALSE, 0);
390 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
391 priv->m_entry, FALSE, FALSE, 0);
394 case 70: /* %x format uses only 2 numbers for some locales */
396 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
397 priv->y_entry, FALSE, FALSE, 0);
401 /* All non-number characters starting from current position
402 form the delimeter */
403 for (endp = iter; *endp; endp++)
404 if (g_ascii_isdigit (*endp))
407 /* Now endp points one place past the delimeter text */
408 delim_text = g_strndup (iter, endp - iter);
409 delim = gtk_label_new (delim_text);
410 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
411 delim, FALSE, FALSE, 0);
413 priv->delims = g_list_append (priv->delims, delim);
424 hildon_date_editor_init (HildonDateEditor *editor)
426 HildonDateEditorPrivate *priv;
429 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
432 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
434 gtk_widget_push_composite_child ();
436 /* initialize values */
437 g_date_clear (&cur_date, 1);
438 g_date_set_time (&cur_date, time (NULL));
440 priv->day = g_date_get_day (&cur_date);
441 priv->month = g_date_get_month (&cur_date);
442 priv->year = g_date_get_year (&cur_date);
443 priv->min_year = DEFAULT_MIN_YEAR;
444 priv->max_year = DEFAULT_MAX_YEAR;
447 priv->frame = gtk_frame_new (NULL);
448 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
450 priv->d_entry = gtk_entry_new ();
451 priv->m_entry = gtk_entry_new ();
452 priv->y_entry = gtk_entry_new ();
455 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
456 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
457 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
458 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
459 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
460 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
464 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
465 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
466 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
468 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
469 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
470 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
472 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
473 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
474 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
476 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
477 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
478 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
480 priv->d_box_date = gtk_hbox_new (FALSE, 0);
482 priv->d_button_image = gtk_button_new ();
483 priv->calendar_icon = gtk_image_new ();
484 real_set_calendar_icon_state (priv, FALSE);
485 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
487 apply_locale_field_order (priv);
489 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
490 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
491 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
492 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
494 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
495 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
496 gtk_widget_show_all (priv->frame);
497 gtk_widget_show_all (priv->d_button_image);
499 /* image button signal connects */
500 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
501 G_CALLBACK (hildon_date_editor_icon_press), editor);
502 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
503 G_CALLBACK (hildon_date_editor_released), editor);
504 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
505 G_CALLBACK (hildon_date_editor_clicked), editor);
506 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
507 G_CALLBACK (hildon_date_editor_keypress), editor);
509 /* entry signal connects */
510 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
511 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
513 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
514 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
516 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
517 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
519 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
520 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
522 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
523 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
525 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
526 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
528 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
529 G_CALLBACK (hildon_date_editor_keypress), editor);
531 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
532 G_CALLBACK (hildon_date_editor_keypress), editor);
534 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
535 G_CALLBACK (hildon_date_editor_keypress), editor);
537 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
538 G_CALLBACK (hildon_date_editor_keyrelease), editor);
540 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
541 G_CALLBACK(hildon_date_editor_keyrelease), editor);
543 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
544 G_CALLBACK (hildon_date_editor_keyrelease), editor);
546 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
548 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
549 G_CALLBACK (hildon_date_editor_entry_changed), editor);
551 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
552 G_CALLBACK (hildon_date_editor_entry_changed), editor);
554 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
555 G_CALLBACK (hildon_date_editor_entry_changed), editor);
557 gtk_widget_pop_composite_child ();
561 hildon_date_editor_set_property (GObject *object,
566 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
567 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
575 hildon_date_editor_set_year (editor, g_value_get_uint (value));
579 hildon_date_editor_set_month (editor, g_value_get_uint (value));
583 hildon_date_editor_set_day (editor, g_value_get_uint (value));
587 val = g_value_get_uint (value);
588 priv->min_year = val;
589 /* Clamp current year */
590 if (hildon_date_editor_get_year (editor) < priv->min_year)
591 hildon_date_editor_set_year (editor, priv->min_year);
595 val = g_value_get_uint (value);
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,
1079 HildonDateEditorPrivate *priv;
1080 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1082 g_assert (GTK_IS_ENTRY (ed));
1083 g_assert (HILDON_IS_DATE_EDITOR (data));
1086 entry = GTK_ENTRY (ed);
1088 /* If day entry is full, move to next entry or validate */
1089 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1091 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1092 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1094 priv->skip_validation = TRUE;
1095 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1098 priv->skip_validation = FALSE;
1103 hildon_date_editor_keyrelease (GtkWidget *widget,
1107 HildonDateEditor *ed;
1108 HildonDateEditorPrivate *priv;
1110 g_return_val_if_fail (data, FALSE);
1111 g_return_val_if_fail (widget, FALSE);
1113 ed = HILDON_DATE_EDITOR (data);
1114 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1116 if (event->keyval == GDK_KP_Enter ||
1117 event->keyval == GDK_Return ||
1118 event->keyval == GDK_ISO_Enter) {
1119 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1121 popup_calendar_dialog (ed);
1124 } else if (event->keyval == GDK_Escape)
1125 priv->skip_validation = FALSE;
1130 /* keyboard handling */
1132 hildon_date_editor_keypress (GtkWidget *widget,
1136 HildonDateEditor *ed;
1137 HildonDateEditorPrivate *priv;
1139 g_assert (HILDON_IS_DATE_EDITOR (data));
1140 g_assert (GTK_IS_ENTRY (widget));
1142 ed = HILDON_DATE_EDITOR (data);
1144 switch (event->keyval) {
1147 /* Ignore return value, since we want to handle event at all times.
1148 otherwise vkb would popup when the keyrepeat starts. */
1149 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1152 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1153 priv->skip_validation = TRUE;
1163 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1164 GdkEventFocus *event,
1167 HildonDateEditor *ed;
1168 HildonDateEditorPrivate *priv;
1170 g_assert (HILDON_IS_DATE_EDITOR (data));
1172 ed = HILDON_DATE_EDITOR (data);
1173 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1176 hildon_date_editor_entry_validate (widget, data);
1177 priv->skip_validation = FALSE;
1183 hildon_date_editor_date_error (HildonDateEditor *editor,
1184 HildonDateTimeError type)
1186 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1191 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1192 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1195 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1196 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1199 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1200 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1203 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1204 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1205 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1208 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1209 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1212 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1213 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1216 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1217 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1220 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1221 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1222 priv->min_year, priv->max_year);
1225 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1226 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1229 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1230 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1234 /*default error message ?*/
1242 hildon_date_editor_size_request (GtkWidget *widget,
1243 GtkRequisition *requisition)
1245 HildonDateEditor *ed;
1246 HildonDateEditorPrivate *priv;
1247 GtkRequisition f_req, img_req;
1249 g_assert (GTK_IS_WIDGET (widget));
1250 g_assert (requisition != NULL);
1252 ed = HILDON_DATE_EDITOR (widget);
1253 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1256 /* Our own children affect our size */
1257 gtk_widget_size_request (priv->frame, &f_req);
1258 gtk_widget_size_request (priv->d_button_image, &img_req);
1260 /* calculate our size */
1261 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1263 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1264 doing so would break current pixel specifications, since
1265 the text entry by itself is already 30px tall + then frame takes
1267 requisition->height = DATE_EDITOR_HEIGHT;
1271 hildon_date_editor_size_allocate (GtkWidget *widget,
1272 GtkAllocation *allocation)
1274 HildonDateEditor *ed;
1275 HildonDateEditorPrivate *priv;
1276 GtkAllocation f_alloc, img_alloc;
1278 GtkRequisition max_req;
1281 g_assert (GTK_IS_WIDGET (widget));
1282 g_assert (allocation != NULL);
1284 ed = HILDON_DATE_EDITOR (widget);
1285 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1287 widget->allocation = *allocation;
1289 gtk_widget_get_child_requisition (widget, &max_req);
1291 /* Center vertically */
1292 f_alloc.y = img_alloc.y = allocation->y +
1293 MAX (allocation->height - max_req.height, 0) / 2;
1295 /* Center horizontally */
1296 f_alloc.x = img_alloc.x = allocation->x +
1297 MAX (allocation->width - max_req.width, 0) / 2;
1299 /* allocate frame */
1300 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1301 gtk_widget_get_child_requisition (priv->frame, &req);
1303 f_alloc.width = req.width;
1304 f_alloc.height = max_req.height;
1305 gtk_widget_size_allocate (priv->frame, &f_alloc);
1309 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1310 gtk_widget_get_child_requisition (priv->d_button_image,
1313 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1314 img_alloc.width = req.width;
1315 img_alloc.height = max_req.height;
1316 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1319 /* FIXME: We really should not alloc delimeters by hand (since they
1320 are not our own children, but we need to force to appear
1321 higher. This ugly hack is needed to compensate the forced
1322 height in size_request. */
1323 for (iter = priv->delims; iter; iter = iter->next)
1326 GtkAllocation alloc;
1328 delim = GTK_WIDGET (iter->data);
1329 alloc = delim->allocation;
1330 alloc.height = max_req.height;
1331 alloc.y = priv->d_entry->allocation.y - 2;
1333 gtk_widget_size_allocate (delim, &alloc);
1338 hildon_date_editor_focus (GtkWidget *widget,
1339 GtkDirectionType direction)
1342 GtkDirectionType effective_direction;
1344 g_assert (HILDON_IS_EDITOR_EDITOR (widget));
1346 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1349 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1355 * hildon_date_editor_set_year:
1356 * @editor: the @HildonDateEditor widget
1359 * Sets the year shown in the editor.
1361 * Returns: TRUE if the year is valid and has been set.
1364 hildon_date_editor_set_year (HildonDateEditor *editor,
1367 HildonDateEditorPrivate *priv;
1368 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1370 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1373 if (g_date_valid_dmy (priv->day, priv->month, year))
1378 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1380 /* We apply the new day, but do not want automatic focus move
1381 etc to take place */
1382 g_signal_handlers_block_by_func (priv->y_entry,
1383 (gpointer) hildon_date_editor_entry_changed, editor);
1384 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1385 g_signal_handlers_unblock_by_func (priv->y_entry,
1386 (gpointer) hildon_date_editor_entry_changed, editor);
1388 g_object_notify (G_OBJECT(editor), "year");
1396 * hildon_date_editor_set_month:
1397 * @editor: the @HildonDateEditor widget
1400 * Sets the month shown in the editor.
1402 * Returns: TRUE if the month is valid and has been set.
1405 hildon_date_editor_set_month (HildonDateEditor *editor,
1408 HildonDateEditorPrivate *priv;
1409 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1410 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1413 if (g_date_valid_dmy (priv->day, month, priv->year))
1418 priv->month = month;
1419 g_date_set_dmy (&date, priv->day, month, priv->year);
1420 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1422 /* We apply the new day, but do not want automatic focus move
1423 etc to take place */
1424 g_signal_handlers_block_by_func (priv->m_entry,
1425 (gpointer) hildon_date_editor_entry_changed, editor);
1426 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1427 g_signal_handlers_unblock_by_func (priv->m_entry,
1428 (gpointer) hildon_date_editor_entry_changed, editor);
1430 g_object_notify (G_OBJECT (editor), "month");
1437 * hildon_date_editor_set_day:
1438 * @editor: the @HildonDateEditor widget
1441 * Sets the day shown in the editor.
1443 * Returns: TRUE if the day is valid and has been set.
1446 hildon_date_editor_set_day (HildonDateEditor *editor,
1449 HildonDateEditorPrivate *priv;
1451 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1452 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1455 if (g_date_valid_dmy (day, priv->month, priv->year))
1461 g_date_set_dmy (&date, day, priv->month, priv->year);
1462 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1464 /* We apply the new day, but do not want automatic focus move
1465 etc to take place */
1466 g_signal_handlers_block_by_func (priv->d_entry,
1467 (gpointer) hildon_date_editor_entry_changed, editor);
1468 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1469 g_signal_handlers_unblock_by_func (priv->d_entry,
1470 (gpointer) hildon_date_editor_entry_changed, editor);
1472 g_object_notify (G_OBJECT(editor), "day");
1479 * hildon_date_editor_get_year:
1480 * @editor: the @HildonDateEditor widget
1482 * Returns: the current year shown in the editor.
1485 hildon_date_editor_get_year (HildonDateEditor *editor)
1487 HildonDateEditorPrivate *priv;
1488 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1490 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1493 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1497 * hildon_date_editor_get_month:
1498 * @editor: the @HildonDateEditor widget
1500 * Gets the month shown in the editor.
1502 * Returns: the current month shown in the editor.
1505 hildon_date_editor_get_month (HildonDateEditor *editor)
1507 HildonDateEditorPrivate *priv;
1508 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1510 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1513 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1517 * hildon_date_editor_get_day:
1518 * @editor: the @HildonDateEditor widget
1520 * Gets the day shown in the editor.
1522 * Returns: the current day shown in the editor
1525 hildon_date_editor_get_day (HildonDateEditor *editor)
1527 HildonDateEditorPrivate *priv;
1528 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1530 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1533 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));