1 /* vim:set sw=4 expandtab cino=(0:
3 * This file is a part of hildon
5 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
7 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; version 2.1 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27 * SECTION:hildon-date-editor
28 * @short_description: A widget which queries a date from user or opens
29 * a HildonCalendarPopup.
30 * @see_also: #HildonCalendarPopup, #HildonTimeEditor
32 * HildonDateEditor is a widget with three entry fields (day, month,
33 * year) and an icon (button): clicking on the icon opens up a
34 * HildonCalendarPopup.
40 * date_editor = hildon_date_editor_new ();
42 * hildon_date_editor_get_date(date_editor, &y, &m, &d);
53 #include "hildon-date-editor.h"
56 #include <gtk/gtkenums.h>
57 #include <gdk/gdkkeysyms.h>
62 #include "hildon-calendar-popup.h"
63 #include "hildon-defines.h"
64 #include "hildon-private.h"
65 #include "hildon-marshalers.h"
66 #include "hildon-enum-types.h"
67 #include "hildon-time-editor.h"
68 #include "hildon-banner.h"
70 #include "hildon-date-editor-private.h"
72 #define _(string) dgettext("hildon-libs", string)
74 #define c_(string) dgettext("hildon-common-strings", string)
76 #define ENTRY_BORDERS 11
78 #define DATE_EDITOR_HEIGHT 30
80 #define DAY_ENTRY_WIDTH 2
82 #define MONTH_ENTRY_WIDTH 2
84 #define YEAR_ENTRY_WIDTH 4
86 #define DEFAULT_MIN_YEAR 1970
88 #define DEFAULT_MAX_YEAR 2037
90 static GtkContainerClass* parent_class;
93 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
96 hildon_date_editor_init (HildonDateEditor *editor);
99 hildon_date_editor_icon_press (GtkWidget *widget,
103 hildon_date_editor_released (GtkWidget *widget,
107 hildon_date_editor_keypress (GtkWidget *widget,
112 hildon_date_editor_keyrelease (GtkWidget *widget,
116 hildon_date_editor_clicked (GtkWidget *widget,
120 hildon_date_editor_entry_validate (GtkWidget *widget,
124 hildon_date_editor_entry_changed (GtkEditable *widget,
128 hildon_date_editor_entry_focus_out (GtkWidget *widget,
129 GdkEventFocus *event,
133 hildon_date_editor_date_error (HildonDateEditor *editor,
134 HildonDateTimeError type);
137 hildon_date_editor_entry_focus_in (GtkWidget *widget,
138 GdkEventFocus *event,
142 hildon_date_editor_get_property (GObject *object,
148 hildon_date_editor_set_property (GObject *object,
153 hildon_child_forall (GtkContainer *container,
154 gboolean include_internals,
155 GtkCallback callback,
156 gpointer callback_data);
159 hildon_date_editor_destroy (GtkObject *self);
162 hildon_date_editor_size_allocate (GtkWidget *widget,
163 GtkAllocation *allocation);
166 hildon_date_editor_size_request (GtkWidget *widget,
167 GtkRequisition *requisition);
169 hildon_date_editor_focus (GtkWidget *widget,
170 GtkDirectionType direction);
172 hildon_date_editor_entry_select_all (GtkWidget *widget);
174 /* Property indices */
191 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
194 * hildon_date_editor_get_type:
196 * Initializes and returns the type of a hildon date editor.
198 * @Returns: GType of #HildonDateEditor
201 hildon_date_editor_get_type (void)
203 static GType editor_type = 0;
206 static const GTypeInfo editor_info = {
207 sizeof (HildonDateEditorClass),
208 NULL, /* base_init */
209 NULL, /* base_finalize */
210 (GClassInitFunc) hildon_date_editor_class_init,
211 NULL, /* class_finalize */
212 NULL, /* class_data */
213 sizeof (HildonDateEditor),
215 (GInstanceInitFunc) hildon_date_editor_init,
217 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
226 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
228 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
229 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
230 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
232 parent_class = g_type_class_peek_parent (editor_class);
234 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
236 gobject_class->set_property = hildon_date_editor_set_property;
237 gobject_class->get_property = hildon_date_editor_get_property;
238 widget_class->size_request = hildon_date_editor_size_request;
239 widget_class->size_allocate = hildon_date_editor_size_allocate;
240 widget_class->focus = hildon_date_editor_focus;
242 container_class->forall = hildon_child_forall;
243 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
245 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
247 date_editor_signals[DATE_ERROR] =
248 g_signal_new ("date-error",
249 G_OBJECT_CLASS_TYPE (gobject_class),
251 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
252 g_signal_accumulator_true_handled, NULL,
253 _hildon_marshal_BOOLEAN__ENUM,
254 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
257 * HildonDateEditor:year:
261 g_object_class_install_property (gobject_class, PROP_YEAR,
262 g_param_spec_uint ("year",
267 G_PARAM_READABLE | G_PARAM_WRITABLE));
270 * HildonDateEditor:month:
274 g_object_class_install_property (gobject_class, PROP_MONTH,
275 g_param_spec_uint ("month",
280 G_PARAM_READABLE | G_PARAM_WRITABLE));
283 * HildonDateEditor:day:
287 g_object_class_install_property (gobject_class, PROP_DAY,
288 g_param_spec_uint ("day",
293 G_PARAM_READABLE | G_PARAM_WRITABLE));
296 * HildonDateEditor:min-year:
298 * Minimum valid year.
300 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
301 g_param_spec_uint ("min-year",
302 "Minimum valid year",
303 "Minimum valid year",
309 * HildonDateEditor:max-year:
311 * Maximum valid year.
313 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
314 g_param_spec_uint ("max-year",
315 "Maximum valid year",
316 "Maximum valid year",
322 /* Forces setting of the icon to certain state. Used initially
323 and from the actual setter function */
325 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
330 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
331 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_SMALL);
333 priv->calendar_icon_pressed = pressed;
336 /* Sets the icon to given state (normal/pressed). Returns
337 info if the state actually changed. */
339 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
342 HildonDateEditorPrivate *priv;
344 g_assert (HILDON_IS_DATE_EDITOR (editor));
346 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
349 if (pressed != priv->calendar_icon_pressed) {
350 real_set_calendar_icon_state (priv, pressed);
357 /* Packing day, month and year entries depend on locale settings
358 We find out the order and all separators by converting a known
359 date to default format and inspecting the result string */
361 apply_locale_field_order (HildonDateEditorPrivate *priv)
363 GDate locale_test_date;
366 gchar *iter, *delim_text;
368 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
369 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
377 /* Try to convert the current location into number. */
378 value = strtoul (iter, &endp, 10);
380 /* If the conversion didn't progress or the detected value was
381 unknown (we used a fixed date, you remember), we treat
382 current position as a literal */
386 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
387 priv->d_entry, FALSE, FALSE, 0);
391 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
392 priv->m_entry, FALSE, FALSE, 0);
395 case 70: /* %x format uses only 2 numbers for some locales */
397 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
398 priv->y_entry, FALSE, FALSE, 0);
402 /* All non-number characters starting from current position
403 form the delimeter */
404 for (endp = iter; *endp; endp++)
405 if (g_ascii_isdigit (*endp))
408 /* Now endp points one place past the delimeter text */
409 delim_text = g_strndup (iter, endp - iter);
410 delim = gtk_label_new (delim_text);
411 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
412 delim, FALSE, FALSE, 0);
414 priv->delims = g_list_append (priv->delims, delim);
425 hildon_date_editor_init (HildonDateEditor *editor)
427 HildonDateEditorPrivate *priv;
430 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
433 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
435 gtk_widget_push_composite_child ();
437 /* initialize values */
438 g_date_clear (&cur_date, 1);
439 g_date_set_time (&cur_date, time (NULL));
441 priv->day = g_date_get_day (&cur_date);
442 priv->month = g_date_get_month (&cur_date);
443 priv->year = g_date_get_year (&cur_date);
444 priv->min_year = DEFAULT_MIN_YEAR;
445 priv->max_year = DEFAULT_MAX_YEAR;
448 priv->frame = gtk_frame_new (NULL);
449 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
451 priv->d_entry = gtk_entry_new ();
452 priv->m_entry = gtk_entry_new ();
453 priv->y_entry = gtk_entry_new ();
456 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
457 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
458 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
459 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
460 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
461 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
465 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
466 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
467 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
469 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
470 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
471 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
473 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
474 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
475 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
477 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
478 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
479 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
481 priv->d_box_date = gtk_hbox_new (FALSE, 0);
483 priv->d_button_image = gtk_button_new ();
484 priv->calendar_icon = gtk_image_new ();
485 real_set_calendar_icon_state (priv, FALSE);
486 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
488 apply_locale_field_order (priv);
490 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
491 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
492 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
493 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
495 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
496 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
497 gtk_widget_show_all (priv->frame);
498 gtk_widget_show_all (priv->d_button_image);
500 /* image button signal connects */
501 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
502 G_CALLBACK (hildon_date_editor_icon_press), editor);
503 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
504 G_CALLBACK (hildon_date_editor_released), editor);
505 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
506 G_CALLBACK (hildon_date_editor_clicked), editor);
507 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
508 G_CALLBACK (hildon_date_editor_keypress), editor);
510 /* entry signal connects */
511 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
512 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
514 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
515 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
517 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
518 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
520 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
521 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
523 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
524 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
526 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
527 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
529 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
530 G_CALLBACK (hildon_date_editor_keypress), editor);
532 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
533 G_CALLBACK (hildon_date_editor_keypress), editor);
535 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
536 G_CALLBACK (hildon_date_editor_keypress), editor);
538 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
539 G_CALLBACK (hildon_date_editor_keyrelease), editor);
541 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
542 G_CALLBACK(hildon_date_editor_keyrelease), editor);
544 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
545 G_CALLBACK (hildon_date_editor_keyrelease), editor);
547 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
549 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
550 G_CALLBACK (hildon_date_editor_entry_changed), editor);
552 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
553 G_CALLBACK (hildon_date_editor_entry_changed), editor);
555 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
556 G_CALLBACK (hildon_date_editor_entry_changed), editor);
558 gtk_widget_pop_composite_child ();
562 hildon_date_editor_set_property (GObject *object,
567 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
568 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
576 hildon_date_editor_set_year (editor, g_value_get_uint (value));
580 hildon_date_editor_set_month (editor, g_value_get_uint (value));
584 hildon_date_editor_set_day (editor, g_value_get_uint (value));
588 val = g_value_get_uint (value);
589 priv->min_year = val;
590 /* Clamp current year */
591 if (hildon_date_editor_get_year (editor) < priv->min_year)
592 hildon_date_editor_set_year (editor, priv->min_year);
596 val = g_value_get_uint (value);
597 priv->max_year = val;
598 /* Clamp current year */
599 if (hildon_date_editor_get_year (editor) > priv->max_year)
600 hildon_date_editor_set_year (editor, priv->max_year);
604 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
610 hildon_date_editor_get_property (GObject *object,
615 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
616 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
621 g_value_set_uint (value, hildon_date_editor_get_year (editor));
625 g_value_set_uint (value, hildon_date_editor_get_month (editor));
629 g_value_set_uint (value, hildon_date_editor_get_day (editor));
633 g_value_set_uint (value, priv->min_year);
637 g_value_set_uint (value, priv->max_year);
641 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
647 hildon_child_forall (GtkContainer *container,
648 gboolean include_internals,
649 GtkCallback callback,
650 gpointer callback_data)
652 HildonDateEditor *editor;
653 HildonDateEditorPrivate *priv;
655 g_assert (HILDON_IS_DATE_EDITOR (container));
658 editor = HILDON_DATE_EDITOR (container);
659 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
662 if (include_internals) {
663 (*callback) (priv->frame, callback_data);
664 (*callback) (priv->d_button_image, callback_data);
669 hildon_date_editor_destroy (GtkObject *self)
671 HildonDateEditorPrivate *priv;
673 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
677 gtk_widget_unparent (priv->frame);
680 if (priv->d_button_image) {
681 gtk_widget_unparent (priv->d_button_image);
682 priv->d_button_image = NULL;
685 g_list_free (priv->delims);
689 if (GTK_OBJECT_CLASS (parent_class)->destroy)
690 GTK_OBJECT_CLASS (parent_class)->destroy (self);
694 * hildon_date_editor_new:
696 * Creates a new date editor. The current system date
697 * is shown in the editor.
699 * Returns: pointer to a new @HildonDateEditor widget.
702 hildon_date_editor_new (void)
704 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
708 * hildon_date_editor_set_date:
709 * @date: the @HildonDateEditor widget
714 * Sets the date shown in the editor.
717 hildon_date_editor_set_date (HildonDateEditor *editor,
722 HildonDateEditorPrivate *priv;
724 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
726 /* This function cannot be implemented by calling
727 component setters, since applying the individual
728 values one by one can make the date temporarily
729 invalid (depending on what the previous values were),
730 which in turn causes that the desired date
731 is not set (even though it's valid). We must set all the
732 components at one go and not try to do any validation etc
735 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
738 if (g_date_valid_dmy (day, month, year))
747 g_date_set_dmy (&date, day, month, year);
749 /* We apply the new values, but do not want automatic focus move
751 g_snprintf (buffer, sizeof (buffer), "%04d", year);
752 g_signal_handlers_block_by_func (priv->y_entry,
753 (gpointer) hildon_date_editor_entry_changed, editor);
754 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
755 g_signal_handlers_unblock_by_func (priv->y_entry,
756 (gpointer) hildon_date_editor_entry_changed, editor);
758 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
759 g_signal_handlers_block_by_func (priv->m_entry,
760 (gpointer) hildon_date_editor_entry_changed, editor);
761 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
762 g_signal_handlers_unblock_by_func (priv->m_entry,
763 (gpointer) hildon_date_editor_entry_changed, editor);
765 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
766 g_signal_handlers_block_by_func (priv->d_entry,
767 (gpointer) hildon_date_editor_entry_changed, editor);
768 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
769 g_signal_handlers_unblock_by_func (priv->d_entry,
770 (gpointer) hildon_date_editor_entry_changed, editor);
772 g_object_notify (G_OBJECT (editor), "year");
773 g_object_notify (G_OBJECT (editor), "month");
774 g_object_notify (G_OBJECT (editor), "day");
779 * hildon_date_editor_get_date:
780 * @date: the @HildonDateEditor widget
785 * Gets the date represented by the date editor.
786 * You can pass NULL to any of the pointers if
787 * you're not interested in obtaining it.
791 hildon_date_editor_get_date (HildonDateEditor *date,
796 HildonDateEditorPrivate *priv;
798 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
800 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
802 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
803 is unclear. They do not neccesarily match and still the texts are
804 used as return values and members for some internal validation!!
805 At least a partly reason is to allow empty text to become
806 0 return value, while members are restricted to valid ranges?!
807 However, if we change the current way, we are likely to break
808 some applications if they rely on some specific way how this
809 widget currently handles empty values and temporarily invalid values.
811 The key issue is this: What should the _get methods return while
812 user is editing a field and the result is incomplete. The
813 partial result? The last good result? If we return partial result
814 we also need a way to inform if the date is not valid. Current
815 implementation is some kind of hybrid of these two...
818 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
820 easily fails, since set_day tries to force validity while get_day
823 Proposal: Always return the same values that are shown in the
824 fields. We add a separate flag (Or use GDate) to
825 indicate if the current date is valid. This would allow
826 setters to make the date invalid as well. */
829 *year = /*priv->year;*/
830 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
832 *month = /*priv->month;*/
833 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
835 *day = /*priv->day;*/
836 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
839 /* icon button press event */
841 hildon_date_editor_icon_press (GtkWidget *widget,
844 g_assert (GTK_IS_WIDGET (widget));
845 g_assert (HILDON_IS_DATE_EDITOR (data));
847 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
853 hildon_date_editor_entry_focus_in (GtkWidget *widget,
854 GdkEventFocus *event,
857 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
864 popup_calendar_dialog (HildonDateEditor *ed)
866 guint y = 0, m = 0, d = 0;
872 hildon_date_editor_get_date (ed, &y, &m, &d);
874 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
875 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
877 g_value_init (&val, G_TYPE_INT);
878 /* Set max/min year in calendar popup to date editor values */
879 g_object_get_property (G_OBJECT (ed), "min-year", &val);
880 g_object_set_property (G_OBJECT (popup), "min-year", &val);
881 g_object_get_property (G_OBJECT (ed), "max-year", &val);
882 g_object_set_property (G_OBJECT (popup), "max-year", &val);
884 /* Pop up calendar */
885 result = gtk_dialog_run (GTK_DIALOG (popup));
887 case GTK_RESPONSE_OK:
888 case GTK_RESPONSE_ACCEPT:
889 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
891 hildon_date_editor_set_date (ed, y, m, d);
894 gtk_widget_destroy (popup);
897 /* button released */
899 hildon_date_editor_released (GtkWidget *widget,
902 HildonDateEditor *ed;
904 g_assert (GTK_IS_WIDGET (widget));
905 g_assert (HILDON_IS_DATE_EDITOR (data));
907 ed = HILDON_DATE_EDITOR (data);
909 /* restores the icon state. The clicked cycle raises the dialog */
910 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
915 /* button released */
917 hildon_date_editor_clicked (GtkWidget *widget,
920 HildonDateEditor *ed;
922 g_assert (GTK_IS_WIDGET (widget));
923 g_assert (HILDON_IS_DATE_EDITOR (data));
925 ed = HILDON_DATE_EDITOR (data);
927 /* restores the non-clicked button state and raises the dialog */
928 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
929 popup_calendar_dialog (ed);
934 /* This is called whenever some editor filed loses the focus and
935 when the all of the fields are filled.
936 Earlier this was called whenever an entry changed */
937 /* FIXME: Validation on focus_out is broken by concept */
939 hildon_date_editor_entry_validate (GtkWidget *widget,
942 HildonDateEditor *ed;
943 HildonDateEditorPrivate *priv;
944 gint d, m, y, max_days;
945 gboolean r; /* temp return values for signals */
947 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
949 g_assert (HILDON_IS_DATE_EDITOR (data));
950 g_assert (GTK_IS_ENTRY (widget));
952 ed = HILDON_DATE_EDITOR (data);
953 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
956 if (priv->skip_validation)
959 /*check if the calling entry is empty*/
960 text = gtk_entry_get_text (GTK_ENTRY (widget));
961 if(text == NULL || text[0] == 0)
963 if (widget == priv->d_entry)
964 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
965 else if(widget == priv->m_entry)
966 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
968 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
970 /* restore empty entry to safe value */
971 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
975 /* Ok, we now check validity. Some fields can be empty */
976 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
977 if (text == NULL || text[0] == 0) return error_code;
979 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
980 if (text == NULL || text[0] == 0) return error_code;
982 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
983 if (text == NULL || text[0] == 0) return error_code;
986 /* Did it actually change? */
987 if (d != priv->day || m != priv->month || y != priv->year)
989 /* We could/should use hildon_date_editor_set_year and such functions
990 * to set the date, instead of use gtk_entry_set_text, and then change
991 * the priv member but hildon_date_editor_set_year and such functions
992 * check if the date is valid, we do want to do date validation check
993 * here according to spec */
996 if (widget == priv->m_entry) {
998 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1002 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1008 if(widget == priv->y_entry) {
1009 if (y < priv->min_year) {
1010 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1013 else if (y > priv->max_year) {
1014 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1019 /* Validate day. We have to do this in every case, since
1020 changing month or year can make the day number to be invalid */
1021 max_days = g_date_get_days_in_month (m,y);
1023 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1026 else if (d > max_days) {
1028 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1031 else { /* the date does not exist (is invalid) */
1032 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1033 /* check what was changed and restore previous value */
1034 if (widget == priv->y_entry)
1036 else if (widget == priv->m_entry)
1043 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1045 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1047 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1051 /* Fix and reformat the date after error signal is processed.
1052 reformatting can be needed even in a such case that numerical
1053 values of the date components are the same as earlier. */
1054 hildon_date_editor_set_date (ed, y, m, d);
1060 hildon_date_editor_entry_select_all (GtkWidget *widget)
1062 GDK_THREADS_ENTER ();
1064 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1066 GDK_THREADS_LEAVE ();
1071 /* When entry becomes full, we move the focus to the next field.
1072 If we are on the last field, the whole contents are validated. */
1074 hildon_date_editor_entry_changed (GtkEditable *ed,
1080 HildonDateEditorPrivate *priv;
1081 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1083 g_assert (GTK_IS_ENTRY (ed));
1084 g_assert (HILDON_IS_DATE_EDITOR (data));
1087 entry = GTK_ENTRY (ed);
1089 /* If day entry is full, move to next entry or validate */
1090 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1092 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1093 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1095 priv->skip_validation = TRUE;
1096 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1099 priv->skip_validation = FALSE;
1104 hildon_date_editor_keyrelease (GtkWidget *widget,
1108 HildonDateEditor *ed;
1109 HildonDateEditorPrivate *priv;
1111 g_return_val_if_fail (data, FALSE);
1112 g_return_val_if_fail (widget, FALSE);
1114 ed = HILDON_DATE_EDITOR (data);
1115 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1117 if (event->keyval == GDK_KP_Enter ||
1118 event->keyval == GDK_Return ||
1119 event->keyval == GDK_ISO_Enter) {
1120 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1122 popup_calendar_dialog (ed);
1125 } else if (event->keyval == GDK_Escape)
1126 priv->skip_validation = FALSE;
1131 /* keyboard handling */
1133 hildon_date_editor_keypress (GtkWidget *widget,
1137 HildonDateEditor *ed;
1138 HildonDateEditorPrivate *priv;
1140 g_assert (HILDON_IS_DATE_EDITOR (data));
1141 g_assert (GTK_IS_ENTRY (widget));
1143 ed = HILDON_DATE_EDITOR (data);
1145 switch (event->keyval) {
1148 /* Ignore return value, since we want to handle event at all times.
1149 otherwise vkb would popup when the keyrepeat starts. */
1150 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1153 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1154 priv->skip_validation = TRUE;
1164 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1165 GdkEventFocus *event,
1168 HildonDateEditor *ed;
1169 HildonDateEditorPrivate *priv;
1171 g_assert (HILDON_IS_DATE_EDITOR (data));
1173 ed = HILDON_DATE_EDITOR (data);
1174 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1177 hildon_date_editor_entry_validate (widget, data);
1178 priv->skip_validation = FALSE;
1184 hildon_date_editor_date_error (HildonDateEditor *editor,
1185 HildonDateTimeError type)
1187 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1192 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1193 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1196 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1197 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1200 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1201 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1204 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1205 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1206 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1209 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1210 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1213 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1214 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1217 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1218 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1221 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1222 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1223 priv->min_year, priv->max_year);
1226 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1227 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1230 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1231 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1235 /*default error message ?*/
1243 hildon_date_editor_size_request (GtkWidget *widget,
1244 GtkRequisition *requisition)
1246 HildonDateEditor *ed;
1247 HildonDateEditorPrivate *priv;
1248 GtkRequisition f_req, img_req;
1250 g_assert (GTK_IS_WIDGET (widget));
1251 g_assert (requisition != NULL);
1253 ed = HILDON_DATE_EDITOR (widget);
1254 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1257 /* Our own children affect our size */
1258 gtk_widget_size_request (priv->frame, &f_req);
1259 gtk_widget_size_request (priv->d_button_image, &img_req);
1261 /* calculate our size */
1262 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1264 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1265 doing so would break current pixel specifications, since
1266 the text entry by itself is already 30px tall + then frame takes
1268 requisition->height = DATE_EDITOR_HEIGHT;
1272 hildon_date_editor_size_allocate (GtkWidget *widget,
1273 GtkAllocation *allocation)
1275 HildonDateEditor *ed;
1276 HildonDateEditorPrivate *priv;
1277 GtkAllocation f_alloc, img_alloc;
1279 GtkRequisition max_req;
1283 g_assert (GTK_IS_WIDGET (widget));
1284 g_assert (allocation != NULL);
1286 ed = HILDON_DATE_EDITOR (widget);
1287 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1289 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1290 widget->allocation = *allocation;
1292 gtk_widget_get_child_requisition (widget, &max_req);
1294 /* Center vertically */
1295 f_alloc.y = img_alloc.y = allocation->y +
1296 MAX (allocation->height - max_req.height, 0) / 2;
1298 /* Center horizontally */
1299 f_alloc.x = img_alloc.x = allocation->x +
1300 MAX (allocation->width - max_req.width, 0) / 2;
1302 /* calculate allocations */
1303 if (GTK_WIDGET_VISIBLE (widget)) {
1304 /* allocate frame */
1305 gtk_widget_get_child_requisition (priv->frame, &req);
1307 f_alloc.width = req.width;
1308 f_alloc.height = max_req.height;
1311 gtk_widget_get_child_requisition (priv->d_button_image,
1314 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1315 img_alloc.width = req.width;
1316 img_alloc.height = max_req.height;
1320 img_alloc.x = f_alloc.x;
1321 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1324 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1325 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1328 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1329 gtk_widget_size_allocate (priv->frame, &f_alloc);
1333 /* FIXME: We really should not alloc delimeters by hand (since they
1334 are not our own children, but we need to force to appear
1335 higher. This ugly hack is needed to compensate the forced
1336 height in size_request. */
1337 for (iter = priv->delims; iter; iter = iter->next)
1340 GtkAllocation alloc;
1342 delim = GTK_WIDGET (iter->data);
1343 alloc = delim->allocation;
1344 alloc.height = max_req.height;
1345 alloc.y = priv->d_entry->allocation.y - 2;
1347 gtk_widget_size_allocate (delim, &alloc);
1352 hildon_date_editor_focus (GtkWidget *widget,
1353 GtkDirectionType direction)
1356 GtkDirectionType effective_direction;
1358 g_assert (HILDON_IS_DATE_EDITOR (widget));
1360 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1363 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1369 * hildon_date_editor_set_year:
1370 * @editor: the @HildonDateEditor widget
1373 * Sets the year shown in the editor.
1375 * Returns: TRUE if the year is valid and has been set.
1378 hildon_date_editor_set_year (HildonDateEditor *editor,
1381 HildonDateEditorPrivate *priv;
1382 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1384 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1387 if (g_date_valid_dmy (priv->day, priv->month, year))
1392 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1394 /* We apply the new day, but do not want automatic focus move
1395 etc to take place */
1396 g_signal_handlers_block_by_func (priv->y_entry,
1397 (gpointer) hildon_date_editor_entry_changed, editor);
1398 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1399 g_signal_handlers_unblock_by_func (priv->y_entry,
1400 (gpointer) hildon_date_editor_entry_changed, editor);
1402 g_object_notify (G_OBJECT(editor), "year");
1410 * hildon_date_editor_set_month:
1411 * @editor: the @HildonDateEditor widget
1414 * Sets the month shown in the editor.
1416 * Returns: TRUE if the month is valid and has been set.
1419 hildon_date_editor_set_month (HildonDateEditor *editor,
1422 HildonDateEditorPrivate *priv;
1423 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1424 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1427 if (g_date_valid_dmy (priv->day, month, priv->year))
1432 priv->month = month;
1433 g_date_set_dmy (&date, priv->day, month, priv->year);
1434 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1436 /* We apply the new day, but do not want automatic focus move
1437 etc to take place */
1438 g_signal_handlers_block_by_func (priv->m_entry,
1439 (gpointer) hildon_date_editor_entry_changed, editor);
1440 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1441 g_signal_handlers_unblock_by_func (priv->m_entry,
1442 (gpointer) hildon_date_editor_entry_changed, editor);
1444 g_object_notify (G_OBJECT (editor), "month");
1451 * hildon_date_editor_set_day:
1452 * @editor: the @HildonDateEditor widget
1455 * Sets the day shown in the editor.
1457 * Returns: TRUE if the day is valid and has been set.
1460 hildon_date_editor_set_day (HildonDateEditor *editor,
1463 HildonDateEditorPrivate *priv;
1465 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1466 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1469 if (g_date_valid_dmy (day, priv->month, priv->year))
1475 g_date_set_dmy (&date, day, priv->month, priv->year);
1476 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1478 /* We apply the new day, but do not want automatic focus move
1479 etc to take place */
1480 g_signal_handlers_block_by_func (priv->d_entry,
1481 (gpointer) hildon_date_editor_entry_changed, editor);
1482 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1483 g_signal_handlers_unblock_by_func (priv->d_entry,
1484 (gpointer) hildon_date_editor_entry_changed, editor);
1486 g_object_notify (G_OBJECT(editor), "day");
1493 * hildon_date_editor_get_year:
1494 * @editor: the @HildonDateEditor widget
1496 * Returns: the current year shown in the editor.
1499 hildon_date_editor_get_year (HildonDateEditor *editor)
1501 HildonDateEditorPrivate *priv;
1502 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1504 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1507 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1511 * hildon_date_editor_get_month:
1512 * @editor: the @HildonDateEditor widget
1514 * Gets the month shown in the editor.
1516 * Returns: the current month shown in the editor.
1519 hildon_date_editor_get_month (HildonDateEditor *editor)
1521 HildonDateEditorPrivate *priv;
1522 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1524 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1527 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1531 * hildon_date_editor_get_day:
1532 * @editor: the @HildonDateEditor widget
1534 * Gets the day shown in the editor.
1536 * Returns: the current day shown in the editor
1539 hildon_date_editor_get_day (HildonDateEditor *editor)
1541 HildonDateEditorPrivate *priv;
1542 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1544 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1547 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));