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
802 * @year: a pointer to store the selected year
803 * @month: a pointer to store the selected month
804 * @day: a pointer to store the selected day
806 * Gets the date selected in @date.
807 * You can pass %NULL in any of the pointers if
808 * you're not interested in obtaining a particular field.
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 gdk_threads_add_idle ((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 gdk_threads_add_idle ((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 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1087 /* When entry becomes full, we move the focus to the next field.
1088 If we are on the last field, the whole contents are validated. */
1090 hildon_date_editor_entry_changed (GtkEditable *ed,
1096 HildonDateEditorPrivate *priv;
1097 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1099 g_assert (GTK_IS_ENTRY (ed));
1100 g_assert (HILDON_IS_DATE_EDITOR (data));
1103 entry = GTK_ENTRY (ed);
1105 /* If day entry is full, move to next entry or validate */
1106 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1108 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1109 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1111 priv->skip_validation = TRUE;
1112 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1115 priv->skip_validation = FALSE;
1120 hildon_date_editor_keyrelease (GtkWidget *widget,
1124 HildonDateEditor *ed;
1125 HildonDateEditorPrivate *priv;
1127 g_return_val_if_fail (data, FALSE);
1128 g_return_val_if_fail (widget, FALSE);
1130 ed = HILDON_DATE_EDITOR (data);
1131 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1133 if (event->keyval == GDK_KP_Enter ||
1134 event->keyval == GDK_Return ||
1135 event->keyval == GDK_ISO_Enter) {
1136 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1138 popup_calendar_dialog (ed);
1141 } else if (event->keyval == GDK_Escape)
1142 priv->skip_validation = FALSE;
1147 /* keyboard handling */
1149 hildon_date_editor_keypress (GtkWidget *widget,
1153 HildonDateEditor *ed;
1154 HildonDateEditorPrivate *priv;
1156 g_assert (HILDON_IS_DATE_EDITOR (data));
1157 g_assert (GTK_IS_ENTRY (widget));
1159 ed = HILDON_DATE_EDITOR (data);
1161 switch (event->keyval) {
1164 /* Ignore return value, since we want to handle event at all times.
1165 otherwise vkb would popup when the keyrepeat starts. */
1166 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1169 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1170 priv->skip_validation = TRUE;
1180 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1181 GdkEventFocus *event,
1184 HildonDateEditor *ed;
1185 HildonDateEditorPrivate *priv;
1187 g_assert (HILDON_IS_DATE_EDITOR (data));
1189 ed = HILDON_DATE_EDITOR (data);
1190 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1193 hildon_date_editor_entry_validate (widget, data);
1194 priv->skip_validation = FALSE;
1200 hildon_date_editor_date_error (HildonDateEditor *editor,
1201 HildonDateTimeError type)
1203 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1208 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1209 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1212 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1213 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1216 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1217 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1220 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1221 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1222 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1225 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1226 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1229 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1230 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1233 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1234 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1237 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1238 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1239 priv->min_year, priv->max_year);
1242 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1243 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1246 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1247 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1251 /*default error message ?*/
1259 hildon_date_editor_size_request (GtkWidget *widget,
1260 GtkRequisition *requisition)
1262 HildonDateEditor *ed;
1263 HildonDateEditorPrivate *priv;
1264 GtkRequisition f_req, img_req;
1266 g_assert (GTK_IS_WIDGET (widget));
1267 g_assert (requisition != NULL);
1269 ed = HILDON_DATE_EDITOR (widget);
1270 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1273 /* Our own children affect our size */
1274 gtk_widget_size_request (priv->frame, &f_req);
1275 gtk_widget_size_request (priv->d_button_image, &img_req);
1277 /* calculate our size */
1278 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1280 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1281 doing so would break current pixel specifications, since
1282 the text entry by itself is already 30px tall + then frame takes
1284 requisition->height = DATE_EDITOR_HEIGHT;
1288 hildon_date_editor_size_allocate (GtkWidget *widget,
1289 GtkAllocation *allocation)
1291 HildonDateEditor *ed;
1292 HildonDateEditorPrivate *priv;
1293 GtkAllocation f_alloc, img_alloc;
1295 GtkRequisition max_req;
1299 g_assert (GTK_IS_WIDGET (widget));
1300 g_assert (allocation != NULL);
1302 ed = HILDON_DATE_EDITOR (widget);
1303 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1305 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1306 widget->allocation = *allocation;
1308 gtk_widget_get_child_requisition (widget, &max_req);
1310 /* Center vertically */
1311 f_alloc.y = img_alloc.y = allocation->y +
1312 MAX (allocation->height - max_req.height, 0) / 2;
1314 /* Center horizontally */
1315 f_alloc.x = img_alloc.x = allocation->x +
1316 MAX (allocation->width - max_req.width, 0) / 2;
1318 /* calculate allocations */
1319 if (GTK_WIDGET_VISIBLE (widget)) {
1320 /* allocate frame */
1321 gtk_widget_get_child_requisition (priv->frame, &req);
1323 f_alloc.width = req.width;
1324 f_alloc.height = max_req.height;
1327 gtk_widget_get_child_requisition (priv->d_button_image,
1330 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1331 img_alloc.width = req.width;
1332 img_alloc.height = max_req.height;
1336 img_alloc.x = f_alloc.x;
1337 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1340 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1341 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1344 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1345 gtk_widget_size_allocate (priv->frame, &f_alloc);
1349 /* FIXME: We really should not alloc delimeters by hand (since they
1350 are not our own children, but we need to force to appear
1351 higher. This ugly hack is needed to compensate the forced
1352 height in size_request. */
1353 for (iter = priv->delims; iter; iter = iter->next)
1356 GtkAllocation alloc;
1358 delim = GTK_WIDGET (iter->data);
1359 alloc = delim->allocation;
1360 alloc.height = max_req.height;
1361 alloc.y = priv->d_entry->allocation.y - 2;
1363 gtk_widget_size_allocate (delim, &alloc);
1368 hildon_date_editor_focus (GtkWidget *widget,
1369 GtkDirectionType direction)
1372 GtkDirectionType effective_direction;
1374 g_assert (HILDON_IS_DATE_EDITOR (widget));
1376 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1379 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1385 * hildon_date_editor_set_year:
1386 * @editor: the @HildonDateEditor widget
1389 * Sets the year shown in the editor.
1391 * Returns: TRUE if the year is valid and has been set.
1394 hildon_date_editor_set_year (HildonDateEditor *editor,
1397 HildonDateEditorPrivate *priv;
1398 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1400 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1403 if (g_date_valid_dmy (priv->day, priv->month, year))
1408 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1410 /* We apply the new day, but do not want automatic focus move
1411 etc to take place */
1412 g_signal_handlers_block_by_func (priv->y_entry,
1413 (gpointer) hildon_date_editor_entry_changed, editor);
1414 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1415 g_signal_handlers_unblock_by_func (priv->y_entry,
1416 (gpointer) hildon_date_editor_entry_changed, editor);
1418 g_object_notify (G_OBJECT(editor), "year");
1426 * hildon_date_editor_set_month:
1427 * @editor: the @HildonDateEditor widget
1430 * Sets the month shown in the editor.
1432 * Returns: TRUE if the month is valid and has been set.
1435 hildon_date_editor_set_month (HildonDateEditor *editor,
1438 HildonDateEditorPrivate *priv;
1439 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1440 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1443 if (g_date_valid_dmy (priv->day, month, priv->year))
1448 priv->month = month;
1449 g_date_set_dmy (&date, priv->day, month, priv->year);
1450 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1452 /* We apply the new day, but do not want automatic focus move
1453 etc to take place */
1454 g_signal_handlers_block_by_func (priv->m_entry,
1455 (gpointer) hildon_date_editor_entry_changed, editor);
1456 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1457 g_signal_handlers_unblock_by_func (priv->m_entry,
1458 (gpointer) hildon_date_editor_entry_changed, editor);
1460 g_object_notify (G_OBJECT (editor), "month");
1467 * hildon_date_editor_set_day:
1468 * @editor: the @HildonDateEditor widget
1471 * Sets the day shown in the editor.
1473 * Returns: TRUE if the day is valid and has been set.
1476 hildon_date_editor_set_day (HildonDateEditor *editor,
1479 HildonDateEditorPrivate *priv;
1481 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1482 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1485 if (g_date_valid_dmy (day, priv->month, priv->year))
1491 g_date_set_dmy (&date, day, priv->month, priv->year);
1492 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1494 /* We apply the new day, but do not want automatic focus move
1495 etc to take place */
1496 g_signal_handlers_block_by_func (priv->d_entry,
1497 (gpointer) hildon_date_editor_entry_changed, editor);
1498 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1499 g_signal_handlers_unblock_by_func (priv->d_entry,
1500 (gpointer) hildon_date_editor_entry_changed, editor);
1502 g_object_notify (G_OBJECT(editor), "day");
1509 * hildon_date_editor_get_year:
1510 * @editor: the #HildonDateEditor widget
1512 * Gets the year shown in the editor.
1514 * Returns: the current year shown in the editor.
1517 hildon_date_editor_get_year (HildonDateEditor *editor)
1519 HildonDateEditorPrivate *priv;
1520 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1522 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1525 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1529 * hildon_date_editor_get_month:
1530 * @editor: the @HildonDateEditor widget
1532 * Gets the month shown in the editor.
1534 * Returns: the current month shown in the editor.
1537 hildon_date_editor_get_month (HildonDateEditor *editor)
1539 HildonDateEditorPrivate *priv;
1540 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1542 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1545 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1549 * hildon_date_editor_get_day:
1550 * @editor: the @HildonDateEditor widget
1552 * Gets the day shown in the editor.
1554 * Returns: the current day shown in the editor
1557 hildon_date_editor_get_day (HildonDateEditor *editor)
1559 HildonDateEditorPrivate *priv;
1560 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1562 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1565 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));