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: Rodrigo Novo <rodrigo.novo@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.
39 * #HildonDateEditor has been deprecated since Hildon 2.2 and should
40 * not be used in newly written code. Use #HildonDateSelector instead. See
41 * <link linkend="hildon-migrating-date-widgets">Migrating Date Widgets</link>
42 * section to know how to migrate this deprecated widget.
50 * GtkWidget *date_editor;
52 * dialog = GTK_DIALOG (gtk_dialog_new ());
53 * date_editor = hildon_date_editor_new ();
55 * gtk_box_pack_start (GTK_BOX (dialog->vbox), gtk_label_new ("Choose a date"), FALSE, FALSE, 10);
56 * gtk_box_pack_start (GTK_BOX (dialog->vbox), date_editor, FALSE, FALSE, 10);
57 * gtk_dialog_add_button (dialog, "Close", GTK_RESPONSE_CANCEL);
59 * gtk_widget_show_all (GTK_WIDGET (dialog));
60 * gtk_dialog_run (dialog);
62 * hildon_date_editor_get_date (HILDON_DATE_EDITOR (date_editor), &y, &m, &d);
63 * g_debug ("Date: %u-%u-%u", y, m, d);
70 #undef HILDON_DISABLE_DEPRECATED
81 #include <gdk/gdkkeysyms.h>
83 #include "hildon-date-editor.h"
84 #include "hildon-calendar-popup.h"
85 #include "hildon-defines.h"
86 #include "hildon-marshalers.h"
87 #include "hildon-enum-types.h"
88 #include "hildon-time-editor.h"
89 #include "hildon-banner.h"
90 #include "hildon-date-editor-private.h"
91 #include "hildon-private.h"
93 #define _(string) dgettext("hildon-libs", string)
95 #define c_(string) dgettext("hildon-common-strings", string)
97 #define ENTRY_BORDERS 11
99 #define DATE_EDITOR_HEIGHT 30
101 #define DAY_ENTRY_WIDTH 2
103 #define MONTH_ENTRY_WIDTH 2
105 #define YEAR_ENTRY_WIDTH 4
107 #define DEFAULT_MIN_YEAR 1970
109 #define DEFAULT_MAX_YEAR 2037
111 static GtkContainerClass* parent_class;
114 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
117 hildon_date_editor_init (HildonDateEditor *editor);
120 hildon_date_editor_icon_press (GtkWidget *widget,
124 hildon_date_editor_released (GtkWidget *widget,
128 hildon_date_editor_keypress (GtkWidget *widget,
133 hildon_date_editor_keyrelease (GtkWidget *widget,
137 hildon_date_editor_clicked (GtkWidget *widget,
141 hildon_date_editor_entry_validate (GtkWidget *widget,
145 hildon_date_editor_entry_changed (GtkEditable *widget,
149 hildon_date_editor_entry_focus_out (GtkWidget *widget,
150 GdkEventFocus *event,
154 hildon_date_editor_date_error (HildonDateEditor *editor,
155 HildonDateTimeError type);
158 hildon_date_editor_entry_focus_in (GtkWidget *widget,
159 GdkEventFocus *event,
163 hildon_date_editor_get_property (GObject *object,
169 hildon_date_editor_set_property (GObject *object,
174 hildon_child_forall (GtkContainer *container,
175 gboolean include_internals,
176 GtkCallback callback,
177 gpointer callback_data);
180 hildon_date_editor_destroy (GtkObject *self);
183 hildon_date_editor_size_allocate (GtkWidget *widget,
184 GtkAllocation *allocation);
187 hildon_date_editor_size_request (GtkWidget *widget,
188 GtkRequisition *requisition);
190 hildon_date_editor_focus (GtkWidget *widget,
191 GtkDirectionType direction);
193 hildon_date_editor_entry_select_all (GtkWidget *widget);
195 /* Property indices */
212 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
215 * hildon_date_editor_get_type:
217 * Initializes and returns the type of a hildon date editor.
219 * Returns: GType of #HildonDateEditor
222 hildon_date_editor_get_type (void)
224 static GType editor_type = 0;
227 static const GTypeInfo editor_info = {
228 sizeof (HildonDateEditorClass),
229 NULL, /* base_init */
230 NULL, /* base_finalize */
231 (GClassInitFunc) hildon_date_editor_class_init,
232 NULL, /* class_finalize */
233 NULL, /* class_data */
234 sizeof (HildonDateEditor),
236 (GInstanceInitFunc) hildon_date_editor_init,
238 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
247 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
249 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
250 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
251 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
253 parent_class = g_type_class_peek_parent (editor_class);
255 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
257 gobject_class->set_property = hildon_date_editor_set_property;
258 gobject_class->get_property = hildon_date_editor_get_property;
259 widget_class->size_request = hildon_date_editor_size_request;
260 widget_class->size_allocate = hildon_date_editor_size_allocate;
261 widget_class->focus = hildon_date_editor_focus;
263 container_class->forall = hildon_child_forall;
264 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
266 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
268 date_editor_signals[DATE_ERROR] =
269 g_signal_new ("date-error",
270 G_OBJECT_CLASS_TYPE (gobject_class),
272 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
273 g_signal_accumulator_true_handled, NULL,
274 _hildon_marshal_BOOLEAN__ENUM,
275 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
278 * HildonDateEditor:year:
282 g_object_class_install_property (gobject_class, PROP_YEAR,
283 g_param_spec_uint ("year",
288 G_PARAM_READABLE | G_PARAM_WRITABLE));
291 * HildonDateEditor:month:
295 g_object_class_install_property (gobject_class, PROP_MONTH,
296 g_param_spec_uint ("month",
301 G_PARAM_READABLE | G_PARAM_WRITABLE));
304 * HildonDateEditor:day:
308 g_object_class_install_property (gobject_class, PROP_DAY,
309 g_param_spec_uint ("day",
314 G_PARAM_READABLE | G_PARAM_WRITABLE));
317 * HildonDateEditor:min-year:
319 * Minimum valid year.
321 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
322 g_param_spec_uint ("min-year",
323 "Minimum valid year",
324 "Minimum valid year",
330 * HildonDateEditor:max-year:
332 * Maximum valid year.
334 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
335 g_param_spec_uint ("max-year",
336 "Maximum valid year",
337 "Maximum valid year",
343 /* Forces setting of the icon to certain state. Used initially
344 and from the actual setter function */
346 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
351 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
352 "widgets_date_editor", HILDON_ICON_SIZE_SMALL);
354 priv->calendar_icon_pressed = pressed;
357 /* Sets the icon to given state (normal/pressed). Returns
358 info if the state actually changed. */
360 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
363 HildonDateEditorPrivate *priv;
365 g_assert (HILDON_IS_DATE_EDITOR (editor));
367 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
370 if (pressed != priv->calendar_icon_pressed) {
371 real_set_calendar_icon_state (priv, pressed);
378 /* Packing day, month and year entries depend on locale settings
379 We find out the order and all separators by converting a known
380 date to default format and inspecting the result string */
382 apply_locale_field_order (HildonDateEditorPrivate *priv)
384 GDate locale_test_date;
387 gchar *iter, *delim_text;
389 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
390 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
398 /* Try to convert the current location into number. */
399 value = strtoul (iter, &endp, 10);
401 /* If the conversion didn't progress or the detected value was
402 unknown (we used a fixed date, you remember), we treat
403 current position as a literal */
407 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
408 priv->d_entry, FALSE, FALSE, 0);
412 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
413 priv->m_entry, FALSE, FALSE, 0);
416 case 70: /* %x format uses only 2 numbers for some locales */
418 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
419 priv->y_entry, FALSE, FALSE, 0);
423 /* All non-number characters starting from current position
424 form the delimeter */
425 for (endp = iter; *endp; endp++)
426 if (g_ascii_isdigit (*endp))
429 /* Now endp points one place past the delimeter text */
430 delim_text = g_strndup (iter, endp - iter);
431 delim = gtk_label_new (delim_text);
432 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
433 delim, FALSE, FALSE, 0);
435 priv->delims = g_list_append (priv->delims, delim);
446 hildon_date_editor_init (HildonDateEditor *editor)
448 HildonDateEditorPrivate *priv;
451 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
454 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
456 gtk_widget_push_composite_child ();
458 /* initialize values */
459 g_date_clear (&cur_date, 1);
460 g_date_set_time (&cur_date, time (NULL));
462 priv->day = g_date_get_day (&cur_date);
463 priv->month = g_date_get_month (&cur_date);
464 priv->year = g_date_get_year (&cur_date);
465 priv->min_year = DEFAULT_MIN_YEAR;
466 priv->max_year = DEFAULT_MAX_YEAR;
469 priv->frame = gtk_frame_new (NULL);
470 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
472 priv->d_entry = gtk_entry_new ();
473 priv->m_entry = gtk_entry_new ();
474 priv->y_entry = gtk_entry_new ();
477 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
478 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
479 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
480 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
481 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
482 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
486 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
487 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
488 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
490 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
491 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
492 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
494 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
495 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
496 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
498 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
499 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
500 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
502 priv->d_box_date = gtk_hbox_new (FALSE, 0);
504 priv->d_button_image = gtk_button_new ();
505 priv->calendar_icon = gtk_image_new ();
506 real_set_calendar_icon_state (priv, FALSE);
507 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
509 apply_locale_field_order (priv);
511 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
512 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
513 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
514 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
516 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
517 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
518 gtk_widget_show_all (priv->frame);
519 gtk_widget_show_all (priv->d_button_image);
521 /* image button signal connects */
522 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
523 G_CALLBACK (hildon_date_editor_icon_press), editor);
524 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
525 G_CALLBACK (hildon_date_editor_released), editor);
526 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
527 G_CALLBACK (hildon_date_editor_clicked), editor);
528 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
529 G_CALLBACK (hildon_date_editor_keypress), editor);
531 /* entry signal connects */
532 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
533 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
535 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
536 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
538 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
539 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
541 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
542 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
544 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
545 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
547 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
548 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
550 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
551 G_CALLBACK (hildon_date_editor_keypress), editor);
553 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
554 G_CALLBACK (hildon_date_editor_keypress), editor);
556 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
557 G_CALLBACK (hildon_date_editor_keypress), editor);
559 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
560 G_CALLBACK (hildon_date_editor_keyrelease), editor);
562 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
563 G_CALLBACK(hildon_date_editor_keyrelease), editor);
565 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
566 G_CALLBACK (hildon_date_editor_keyrelease), editor);
568 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
570 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
571 G_CALLBACK (hildon_date_editor_entry_changed), editor);
573 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
574 G_CALLBACK (hildon_date_editor_entry_changed), editor);
576 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
577 G_CALLBACK (hildon_date_editor_entry_changed), editor);
579 gtk_widget_pop_composite_child ();
583 hildon_date_editor_set_property (GObject *object,
588 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
589 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
597 hildon_date_editor_set_year (editor, g_value_get_uint (value));
601 hildon_date_editor_set_month (editor, g_value_get_uint (value));
605 hildon_date_editor_set_day (editor, g_value_get_uint (value));
609 val = g_value_get_uint (value);
610 priv->min_year = val;
611 /* Clamp current year */
612 if (hildon_date_editor_get_year (editor) < priv->min_year)
613 hildon_date_editor_set_year (editor, priv->min_year);
617 val = g_value_get_uint (value);
618 priv->max_year = val;
619 /* Clamp current year */
620 if (hildon_date_editor_get_year (editor) > priv->max_year)
621 hildon_date_editor_set_year (editor, priv->max_year);
625 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
631 hildon_date_editor_get_property (GObject *object,
636 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
637 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
642 g_value_set_uint (value, hildon_date_editor_get_year (editor));
646 g_value_set_uint (value, hildon_date_editor_get_month (editor));
650 g_value_set_uint (value, hildon_date_editor_get_day (editor));
654 g_value_set_uint (value, priv->min_year);
658 g_value_set_uint (value, priv->max_year);
662 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
668 hildon_child_forall (GtkContainer *container,
669 gboolean include_internals,
670 GtkCallback callback,
671 gpointer callback_data)
673 HildonDateEditor *editor;
674 HildonDateEditorPrivate *priv;
676 g_assert (HILDON_IS_DATE_EDITOR (container));
679 editor = HILDON_DATE_EDITOR (container);
680 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
683 if (include_internals) {
684 (*callback) (priv->frame, callback_data);
685 (*callback) (priv->d_button_image, callback_data);
690 hildon_date_editor_destroy (GtkObject *self)
692 HildonDateEditorPrivate *priv;
694 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
698 gtk_widget_unparent (priv->frame);
701 if (priv->d_button_image) {
702 gtk_widget_unparent (priv->d_button_image);
703 priv->d_button_image = NULL;
706 g_list_free (priv->delims);
710 if (GTK_OBJECT_CLASS (parent_class)->destroy)
711 GTK_OBJECT_CLASS (parent_class)->destroy (self);
715 * hildon_date_editor_new:
717 * Creates a new date editor. The current system date
718 * is shown in the editor.
720 * Returns: pointer to a new @HildonDateEditor widget.
723 hildon_date_editor_new (void)
725 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
729 * hildon_date_editor_set_date:
730 * @date: the @HildonDateEditor widget
735 * Sets the date shown in the editor.
738 hildon_date_editor_set_date (HildonDateEditor *editor,
743 HildonDateEditorPrivate *priv;
745 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
747 /* This function cannot be implemented by calling
748 component setters, since applying the individual
749 values one by one can make the date temporarily
750 invalid (depending on what the previous values were),
751 which in turn causes that the desired date
752 is not set (even though it's valid). We must set all the
753 components at one go and not try to do any validation etc
756 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
759 if (g_date_valid_dmy (day, month, year))
768 g_date_set_dmy (&date, day, month, year);
770 /* We apply the new values, but do not want automatic focus move
772 g_snprintf (buffer, sizeof (buffer), "%04d", year);
773 g_signal_handlers_block_by_func (priv->y_entry,
774 (gpointer) hildon_date_editor_entry_changed, editor);
775 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
776 g_signal_handlers_unblock_by_func (priv->y_entry,
777 (gpointer) hildon_date_editor_entry_changed, editor);
779 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
780 g_signal_handlers_block_by_func (priv->m_entry,
781 (gpointer) hildon_date_editor_entry_changed, editor);
782 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
783 g_signal_handlers_unblock_by_func (priv->m_entry,
784 (gpointer) hildon_date_editor_entry_changed, editor);
786 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
787 g_signal_handlers_block_by_func (priv->d_entry,
788 (gpointer) hildon_date_editor_entry_changed, editor);
789 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
790 g_signal_handlers_unblock_by_func (priv->d_entry,
791 (gpointer) hildon_date_editor_entry_changed, editor);
793 g_object_notify (G_OBJECT (editor), "year");
794 g_object_notify (G_OBJECT (editor), "month");
795 g_object_notify (G_OBJECT (editor), "day");
800 * hildon_date_editor_get_date:
801 * @date: the @HildonDateEditor widget
806 * Gets the date represented by the date editor.
807 * You can pass NULL to any of the pointers if
808 * you're not interested in obtaining it.
812 hildon_date_editor_get_date (HildonDateEditor *date,
817 HildonDateEditorPrivate *priv;
819 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
821 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
823 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
824 is unclear. They do not neccesarily match and still the texts are
825 used as return values and members for some internal validation!!
826 At least a partly reason is to allow empty text to become
827 0 return value, while members are restricted to valid ranges?!
828 However, if we change the current way, we are likely to break
829 some applications if they rely on some specific way how this
830 widget currently handles empty values and temporarily invalid values.
832 The key issue is this: What should the _get methods return while
833 user is editing a field and the result is incomplete. The
834 partial result? The last good result? If we return partial result
835 we also need a way to inform if the date is not valid. Current
836 implementation is some kind of hybrid of these two...
839 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
841 easily fails, since set_day tries to force validity while get_day
844 Proposal: Always return the same values that are shown in the
845 fields. We add a separate flag (Or use GDate) to
846 indicate if the current date is valid. This would allow
847 setters to make the date invalid as well. */
850 *year = /*priv->year;*/
851 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
853 *month = /*priv->month;*/
854 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
856 *day = /*priv->day;*/
857 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
860 /* icon button press event */
862 hildon_date_editor_icon_press (GtkWidget *widget,
865 g_assert (GTK_IS_WIDGET (widget));
866 g_assert (HILDON_IS_DATE_EDITOR (data));
868 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
874 hildon_date_editor_entry_focus_in (GtkWidget *widget,
875 GdkEventFocus *event,
878 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
885 popup_calendar_dialog (HildonDateEditor *ed)
887 guint y = 0, m = 0, d = 0;
893 hildon_date_editor_get_date (ed, &y, &m, &d);
895 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
896 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
898 g_value_init (&val, G_TYPE_INT);
899 /* Set max/min year in calendar popup to date editor values */
900 g_object_get_property (G_OBJECT (ed), "min-year", &val);
901 g_object_set_property (G_OBJECT (popup), "min-year", &val);
902 g_object_get_property (G_OBJECT (ed), "max-year", &val);
903 g_object_set_property (G_OBJECT (popup), "max-year", &val);
905 /* Pop up calendar */
906 result = gtk_dialog_run (GTK_DIALOG (popup));
908 case GTK_RESPONSE_OK:
909 case GTK_RESPONSE_ACCEPT:
910 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
912 hildon_date_editor_set_date (ed, y, m, d);
915 gtk_widget_destroy (popup);
918 /* button released */
920 hildon_date_editor_released (GtkWidget *widget,
923 HildonDateEditor *ed;
925 g_assert (GTK_IS_WIDGET (widget));
926 g_assert (HILDON_IS_DATE_EDITOR (data));
928 ed = HILDON_DATE_EDITOR (data);
930 /* restores the icon state. The clicked cycle raises the dialog */
931 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
936 /* button released */
938 hildon_date_editor_clicked (GtkWidget *widget,
941 HildonDateEditor *ed;
943 g_assert (GTK_IS_WIDGET (widget));
944 g_assert (HILDON_IS_DATE_EDITOR (data));
946 ed = HILDON_DATE_EDITOR (data);
948 /* restores the non-clicked button state and raises the dialog */
949 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
950 popup_calendar_dialog (ed);
955 /* This is called whenever some editor filed loses the focus and
956 when the all of the fields are filled.
957 Earlier this was called whenever an entry changed */
958 /* FIXME: Validation on focus_out is broken by concept */
960 hildon_date_editor_entry_validate (GtkWidget *widget,
963 HildonDateEditor *ed;
964 HildonDateEditorPrivate *priv;
965 gint d, m, y, max_days;
966 gboolean r; /* temp return values for signals */
968 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
970 g_assert (HILDON_IS_DATE_EDITOR (data));
971 g_assert (GTK_IS_ENTRY (widget));
973 ed = HILDON_DATE_EDITOR (data);
974 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
977 if (priv->skip_validation)
980 /*check if the calling entry is empty*/
981 text = gtk_entry_get_text (GTK_ENTRY (widget));
982 if(text == NULL || text[0] == 0)
984 if (widget == priv->d_entry)
985 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
986 else if(widget == priv->m_entry)
987 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
989 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
991 /* restore empty entry to safe value */
992 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
996 /* Ok, we now check validity. Some fields can be empty */
997 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
998 if (text == NULL || text[0] == 0) return error_code;
1000 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
1001 if (text == NULL || text[0] == 0) return error_code;
1003 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
1004 if (text == NULL || text[0] == 0) return error_code;
1007 /* Did it actually change? */
1008 if (d != priv->day || m != priv->month || y != priv->year)
1010 /* We could/should use hildon_date_editor_set_year and such functions
1011 * to set the date, instead of use gtk_entry_set_text, and then change
1012 * the priv member but hildon_date_editor_set_year and such functions
1013 * check if the date is valid, we do want to do date validation check
1014 * here according to spec */
1016 /* Validate month */
1017 if (widget == priv->m_entry) {
1019 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1023 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1029 if(widget == priv->y_entry) {
1030 if (y < priv->min_year) {
1031 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1034 else if (y > priv->max_year) {
1035 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1040 /* Validate day. We have to do this in every case, since
1041 changing month or year can make the day number to be invalid */
1042 max_days = g_date_get_days_in_month (m,y);
1044 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1047 else if (d > max_days) {
1049 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1052 else { /* the date does not exist (is invalid) */
1053 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1054 /* check what was changed and restore previous value */
1055 if (widget == priv->y_entry)
1057 else if (widget == priv->m_entry)
1064 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1066 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1068 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1072 /* Fix and reformat the date after error signal is processed.
1073 reformatting can be needed even in a such case that numerical
1074 values of the date components are the same as earlier. */
1075 hildon_date_editor_set_date (ed, y, m, d);
1081 hildon_date_editor_entry_select_all (GtkWidget *widget)
1083 GDK_THREADS_ENTER ();
1085 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1087 GDK_THREADS_LEAVE ();
1092 /* When entry becomes full, we move the focus to the next field.
1093 If we are on the last field, the whole contents are validated. */
1095 hildon_date_editor_entry_changed (GtkEditable *ed,
1101 HildonDateEditorPrivate *priv;
1102 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1104 g_assert (GTK_IS_ENTRY (ed));
1105 g_assert (HILDON_IS_DATE_EDITOR (data));
1108 entry = GTK_ENTRY (ed);
1110 /* If day entry is full, move to next entry or validate */
1111 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1113 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1114 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1116 priv->skip_validation = TRUE;
1117 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1120 priv->skip_validation = FALSE;
1125 hildon_date_editor_keyrelease (GtkWidget *widget,
1129 HildonDateEditor *ed;
1130 HildonDateEditorPrivate *priv;
1132 g_return_val_if_fail (data, FALSE);
1133 g_return_val_if_fail (widget, FALSE);
1135 ed = HILDON_DATE_EDITOR (data);
1136 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1138 if (event->keyval == GDK_KP_Enter ||
1139 event->keyval == GDK_Return ||
1140 event->keyval == GDK_ISO_Enter) {
1141 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1143 popup_calendar_dialog (ed);
1146 } else if (event->keyval == GDK_Escape)
1147 priv->skip_validation = FALSE;
1152 /* keyboard handling */
1154 hildon_date_editor_keypress (GtkWidget *widget,
1158 HildonDateEditor *ed;
1159 HildonDateEditorPrivate *priv;
1161 g_assert (HILDON_IS_DATE_EDITOR (data));
1162 g_assert (GTK_IS_ENTRY (widget));
1164 ed = HILDON_DATE_EDITOR (data);
1166 switch (event->keyval) {
1169 /* Ignore return value, since we want to handle event at all times.
1170 otherwise vkb would popup when the keyrepeat starts. */
1171 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1174 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
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;
1304 g_assert (GTK_IS_WIDGET (widget));
1305 g_assert (allocation != NULL);
1307 ed = HILDON_DATE_EDITOR (widget);
1308 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1310 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1311 widget->allocation = *allocation;
1313 gtk_widget_get_child_requisition (widget, &max_req);
1315 /* Center vertically */
1316 f_alloc.y = img_alloc.y = allocation->y +
1317 MAX (allocation->height - max_req.height, 0) / 2;
1319 /* Center horizontally */
1320 f_alloc.x = img_alloc.x = allocation->x +
1321 MAX (allocation->width - max_req.width, 0) / 2;
1323 /* calculate allocations */
1324 if (GTK_WIDGET_VISIBLE (widget)) {
1325 /* allocate frame */
1326 gtk_widget_get_child_requisition (priv->frame, &req);
1328 f_alloc.width = req.width;
1329 f_alloc.height = max_req.height;
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;
1341 img_alloc.x = f_alloc.x;
1342 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1345 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1346 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1349 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1350 gtk_widget_size_allocate (priv->frame, &f_alloc);
1354 /* FIXME: We really should not alloc delimeters by hand (since they
1355 are not our own children, but we need to force to appear
1356 higher. This ugly hack is needed to compensate the forced
1357 height in size_request. */
1358 for (iter = priv->delims; iter; iter = iter->next)
1361 GtkAllocation alloc;
1363 delim = GTK_WIDGET (iter->data);
1364 alloc = delim->allocation;
1365 alloc.height = max_req.height;
1366 alloc.y = priv->d_entry->allocation.y - 2;
1368 gtk_widget_size_allocate (delim, &alloc);
1373 hildon_date_editor_focus (GtkWidget *widget,
1374 GtkDirectionType direction)
1377 GtkDirectionType effective_direction;
1379 g_assert (HILDON_IS_DATE_EDITOR (widget));
1381 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1384 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1390 * hildon_date_editor_set_year:
1391 * @editor: the @HildonDateEditor widget
1394 * Sets the year shown in the editor.
1396 * Returns: TRUE if the year is valid and has been set.
1399 hildon_date_editor_set_year (HildonDateEditor *editor,
1402 HildonDateEditorPrivate *priv;
1403 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1405 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1408 if (g_date_valid_dmy (priv->day, priv->month, year))
1413 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1415 /* We apply the new day, but do not want automatic focus move
1416 etc to take place */
1417 g_signal_handlers_block_by_func (priv->y_entry,
1418 (gpointer) hildon_date_editor_entry_changed, editor);
1419 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1420 g_signal_handlers_unblock_by_func (priv->y_entry,
1421 (gpointer) hildon_date_editor_entry_changed, editor);
1423 g_object_notify (G_OBJECT(editor), "year");
1431 * hildon_date_editor_set_month:
1432 * @editor: the @HildonDateEditor widget
1435 * Sets the month shown in the editor.
1437 * Returns: TRUE if the month is valid and has been set.
1440 hildon_date_editor_set_month (HildonDateEditor *editor,
1443 HildonDateEditorPrivate *priv;
1444 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1445 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1448 if (g_date_valid_dmy (priv->day, month, priv->year))
1453 priv->month = month;
1454 g_date_set_dmy (&date, priv->day, month, priv->year);
1455 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1457 /* We apply the new day, but do not want automatic focus move
1458 etc to take place */
1459 g_signal_handlers_block_by_func (priv->m_entry,
1460 (gpointer) hildon_date_editor_entry_changed, editor);
1461 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1462 g_signal_handlers_unblock_by_func (priv->m_entry,
1463 (gpointer) hildon_date_editor_entry_changed, editor);
1465 g_object_notify (G_OBJECT (editor), "month");
1472 * hildon_date_editor_set_day:
1473 * @editor: the @HildonDateEditor widget
1476 * Sets the day shown in the editor.
1478 * Returns: TRUE if the day is valid and has been set.
1481 hildon_date_editor_set_day (HildonDateEditor *editor,
1484 HildonDateEditorPrivate *priv;
1486 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1487 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1490 if (g_date_valid_dmy (day, priv->month, priv->year))
1496 g_date_set_dmy (&date, day, priv->month, priv->year);
1497 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1499 /* We apply the new day, but do not want automatic focus move
1500 etc to take place */
1501 g_signal_handlers_block_by_func (priv->d_entry,
1502 (gpointer) hildon_date_editor_entry_changed, editor);
1503 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1504 g_signal_handlers_unblock_by_func (priv->d_entry,
1505 (gpointer) hildon_date_editor_entry_changed, editor);
1507 g_object_notify (G_OBJECT(editor), "day");
1514 * hildon_date_editor_get_year:
1515 * @editor: the @HildonDateEditor widget
1517 * Returns: the current year shown in the editor.
1520 hildon_date_editor_get_year (HildonDateEditor *editor)
1522 HildonDateEditorPrivate *priv;
1523 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1525 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1528 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1532 * hildon_date_editor_get_month:
1533 * @editor: the @HildonDateEditor widget
1535 * Gets the month shown in the editor.
1537 * Returns: the current month shown in the editor.
1540 hildon_date_editor_get_month (HildonDateEditor *editor)
1542 HildonDateEditorPrivate *priv;
1543 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1545 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1548 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1552 * hildon_date_editor_get_day:
1553 * @editor: the @HildonDateEditor widget
1555 * Gets the day shown in the editor.
1557 * Returns: the current day shown in the editor
1560 hildon_date_editor_get_day (HildonDateEditor *editor)
1562 HildonDateEditorPrivate *priv;
1563 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1565 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1568 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));