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.
38 * #HildonDateEditor has been deprecated since Hildon 2.2 and should
39 * not be used in newly written code. Use #HildonDateSelector instead.
47 * GtkWidget *date_editor;
49 * dialog = GTK_DIALOG (gtk_dialog_new ());
50 * date_editor = hildon_date_editor_new ();
52 * gtk_box_pack_start (GTK_BOX (dialog->vbox), gtk_label_new ("Choose a date"), FALSE, FALSE, 10);
53 * gtk_box_pack_start (GTK_BOX (dialog->vbox), date_editor, FALSE, FALSE, 10);
54 * gtk_dialog_add_button (dialog, "Close", GTK_RESPONSE_CANCEL);
56 * gtk_widget_show_all (GTK_WIDGET (dialog));
57 * gtk_dialog_run (dialog);
59 * hildon_date_editor_get_date (HILDON_DATE_EDITOR (date_editor), &y, &m, &d);
60 * g_debug ("Date: %u-%u-%u", y, m, d);
67 #undef HILDON_DISABLE_DEPRECATED
78 #include <gdk/gdkkeysyms.h>
80 #include "hildon-date-editor.h"
81 #include "hildon-calendar-popup.h"
82 #include "hildon-defines.h"
83 #include "hildon-marshalers.h"
84 #include "hildon-enum-types.h"
85 #include "hildon-time-editor.h"
86 #include "hildon-banner.h"
87 #include "hildon-date-editor-private.h"
88 #include "hildon-private.h"
90 #define _(string) dgettext("hildon-libs", string)
92 #define c_(string) dgettext("hildon-common-strings", string)
94 #define ENTRY_BORDERS 11
96 #define DATE_EDITOR_HEIGHT 30
98 #define DAY_ENTRY_WIDTH 2
100 #define MONTH_ENTRY_WIDTH 2
102 #define YEAR_ENTRY_WIDTH 4
104 #define DEFAULT_MIN_YEAR 1970
106 #define DEFAULT_MAX_YEAR 2037
108 static GtkContainerClass* parent_class;
111 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
114 hildon_date_editor_init (HildonDateEditor *editor);
117 hildon_date_editor_icon_press (GtkWidget *widget,
121 hildon_date_editor_released (GtkWidget *widget,
125 hildon_date_editor_keypress (GtkWidget *widget,
130 hildon_date_editor_keyrelease (GtkWidget *widget,
134 hildon_date_editor_clicked (GtkWidget *widget,
138 hildon_date_editor_entry_validate (GtkWidget *widget,
142 hildon_date_editor_entry_changed (GtkEditable *widget,
146 hildon_date_editor_entry_focus_out (GtkWidget *widget,
147 GdkEventFocus *event,
151 hildon_date_editor_date_error (HildonDateEditor *editor,
152 HildonDateTimeError type);
155 hildon_date_editor_entry_focus_in (GtkWidget *widget,
156 GdkEventFocus *event,
160 hildon_date_editor_get_property (GObject *object,
166 hildon_date_editor_set_property (GObject *object,
171 hildon_child_forall (GtkContainer *container,
172 gboolean include_internals,
173 GtkCallback callback,
174 gpointer callback_data);
177 hildon_date_editor_destroy (GtkObject *self);
180 hildon_date_editor_size_allocate (GtkWidget *widget,
181 GtkAllocation *allocation);
184 hildon_date_editor_size_request (GtkWidget *widget,
185 GtkRequisition *requisition);
187 hildon_date_editor_focus (GtkWidget *widget,
188 GtkDirectionType direction);
190 hildon_date_editor_entry_select_all (GtkWidget *widget);
192 /* Property indices */
209 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
212 * hildon_date_editor_get_type:
214 * Initializes and returns the type of a hildon date editor.
216 * Returns: GType of #HildonDateEditor
219 hildon_date_editor_get_type (void)
221 static GType editor_type = 0;
224 static const GTypeInfo editor_info = {
225 sizeof (HildonDateEditorClass),
226 NULL, /* base_init */
227 NULL, /* base_finalize */
228 (GClassInitFunc) hildon_date_editor_class_init,
229 NULL, /* class_finalize */
230 NULL, /* class_data */
231 sizeof (HildonDateEditor),
233 (GInstanceInitFunc) hildon_date_editor_init,
235 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
244 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
246 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
247 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
248 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
250 parent_class = g_type_class_peek_parent (editor_class);
252 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
254 gobject_class->set_property = hildon_date_editor_set_property;
255 gobject_class->get_property = hildon_date_editor_get_property;
256 widget_class->size_request = hildon_date_editor_size_request;
257 widget_class->size_allocate = hildon_date_editor_size_allocate;
258 widget_class->focus = hildon_date_editor_focus;
260 container_class->forall = hildon_child_forall;
261 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
263 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
265 date_editor_signals[DATE_ERROR] =
266 g_signal_new ("date-error",
267 G_OBJECT_CLASS_TYPE (gobject_class),
269 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
270 g_signal_accumulator_true_handled, NULL,
271 _hildon_marshal_BOOLEAN__ENUM,
272 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
275 * HildonDateEditor:year:
279 g_object_class_install_property (gobject_class, PROP_YEAR,
280 g_param_spec_uint ("year",
285 G_PARAM_READABLE | G_PARAM_WRITABLE));
288 * HildonDateEditor:month:
292 g_object_class_install_property (gobject_class, PROP_MONTH,
293 g_param_spec_uint ("month",
298 G_PARAM_READABLE | G_PARAM_WRITABLE));
301 * HildonDateEditor:day:
305 g_object_class_install_property (gobject_class, PROP_DAY,
306 g_param_spec_uint ("day",
311 G_PARAM_READABLE | G_PARAM_WRITABLE));
314 * HildonDateEditor:min-year:
316 * Minimum valid year.
318 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
319 g_param_spec_uint ("min-year",
320 "Minimum valid year",
321 "Minimum valid year",
327 * HildonDateEditor:max-year:
329 * Maximum valid year.
331 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
332 g_param_spec_uint ("max-year",
333 "Maximum valid year",
334 "Maximum valid year",
340 /* Forces setting of the icon to certain state. Used initially
341 and from the actual setter function */
343 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
348 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
349 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_SMALL);
351 priv->calendar_icon_pressed = pressed;
354 /* Sets the icon to given state (normal/pressed). Returns
355 info if the state actually changed. */
357 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
360 HildonDateEditorPrivate *priv;
362 g_assert (HILDON_IS_DATE_EDITOR (editor));
364 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
367 if (pressed != priv->calendar_icon_pressed) {
368 real_set_calendar_icon_state (priv, pressed);
375 /* Packing day, month and year entries depend on locale settings
376 We find out the order and all separators by converting a known
377 date to default format and inspecting the result string */
379 apply_locale_field_order (HildonDateEditorPrivate *priv)
381 GDate locale_test_date;
384 gchar *iter, *delim_text;
386 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
387 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
395 /* Try to convert the current location into number. */
396 value = strtoul (iter, &endp, 10);
398 /* If the conversion didn't progress or the detected value was
399 unknown (we used a fixed date, you remember), we treat
400 current position as a literal */
404 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
405 priv->d_entry, FALSE, FALSE, 0);
409 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
410 priv->m_entry, FALSE, FALSE, 0);
413 case 70: /* %x format uses only 2 numbers for some locales */
415 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
416 priv->y_entry, FALSE, FALSE, 0);
420 /* All non-number characters starting from current position
421 form the delimeter */
422 for (endp = iter; *endp; endp++)
423 if (g_ascii_isdigit (*endp))
426 /* Now endp points one place past the delimeter text */
427 delim_text = g_strndup (iter, endp - iter);
428 delim = gtk_label_new (delim_text);
429 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
430 delim, FALSE, FALSE, 0);
432 priv->delims = g_list_append (priv->delims, delim);
443 hildon_date_editor_init (HildonDateEditor *editor)
445 HildonDateEditorPrivate *priv;
448 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
451 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
453 gtk_widget_push_composite_child ();
455 /* initialize values */
456 g_date_clear (&cur_date, 1);
457 g_date_set_time (&cur_date, time (NULL));
459 priv->day = g_date_get_day (&cur_date);
460 priv->month = g_date_get_month (&cur_date);
461 priv->year = g_date_get_year (&cur_date);
462 priv->min_year = DEFAULT_MIN_YEAR;
463 priv->max_year = DEFAULT_MAX_YEAR;
466 priv->frame = gtk_frame_new (NULL);
467 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
469 priv->d_entry = gtk_entry_new ();
470 priv->m_entry = gtk_entry_new ();
471 priv->y_entry = gtk_entry_new ();
474 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
475 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
476 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
477 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
478 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
479 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
483 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
484 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
485 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
487 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
488 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
489 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
491 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
492 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
493 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
495 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
496 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
497 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
499 priv->d_box_date = gtk_hbox_new (FALSE, 0);
501 priv->d_button_image = gtk_button_new ();
502 priv->calendar_icon = gtk_image_new ();
503 real_set_calendar_icon_state (priv, FALSE);
504 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
506 apply_locale_field_order (priv);
508 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
509 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
510 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
511 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
513 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
514 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
515 gtk_widget_show_all (priv->frame);
516 gtk_widget_show_all (priv->d_button_image);
518 /* image button signal connects */
519 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
520 G_CALLBACK (hildon_date_editor_icon_press), editor);
521 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
522 G_CALLBACK (hildon_date_editor_released), editor);
523 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
524 G_CALLBACK (hildon_date_editor_clicked), editor);
525 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
526 G_CALLBACK (hildon_date_editor_keypress), editor);
528 /* entry signal connects */
529 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
530 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
532 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
533 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
535 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
536 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
538 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
539 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
541 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
542 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
544 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
545 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
547 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
548 G_CALLBACK (hildon_date_editor_keypress), editor);
550 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
551 G_CALLBACK (hildon_date_editor_keypress), editor);
553 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
554 G_CALLBACK (hildon_date_editor_keypress), editor);
556 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
557 G_CALLBACK (hildon_date_editor_keyrelease), editor);
559 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
560 G_CALLBACK(hildon_date_editor_keyrelease), editor);
562 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
563 G_CALLBACK (hildon_date_editor_keyrelease), editor);
565 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
567 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
568 G_CALLBACK (hildon_date_editor_entry_changed), editor);
570 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
571 G_CALLBACK (hildon_date_editor_entry_changed), editor);
573 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
574 G_CALLBACK (hildon_date_editor_entry_changed), editor);
576 gtk_widget_pop_composite_child ();
580 hildon_date_editor_set_property (GObject *object,
585 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
586 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
594 hildon_date_editor_set_year (editor, g_value_get_uint (value));
598 hildon_date_editor_set_month (editor, g_value_get_uint (value));
602 hildon_date_editor_set_day (editor, g_value_get_uint (value));
606 val = g_value_get_uint (value);
607 priv->min_year = val;
608 /* Clamp current year */
609 if (hildon_date_editor_get_year (editor) < priv->min_year)
610 hildon_date_editor_set_year (editor, priv->min_year);
614 val = g_value_get_uint (value);
615 priv->max_year = val;
616 /* Clamp current year */
617 if (hildon_date_editor_get_year (editor) > priv->max_year)
618 hildon_date_editor_set_year (editor, priv->max_year);
622 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
628 hildon_date_editor_get_property (GObject *object,
633 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
634 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
639 g_value_set_uint (value, hildon_date_editor_get_year (editor));
643 g_value_set_uint (value, hildon_date_editor_get_month (editor));
647 g_value_set_uint (value, hildon_date_editor_get_day (editor));
651 g_value_set_uint (value, priv->min_year);
655 g_value_set_uint (value, priv->max_year);
659 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
665 hildon_child_forall (GtkContainer *container,
666 gboolean include_internals,
667 GtkCallback callback,
668 gpointer callback_data)
670 HildonDateEditor *editor;
671 HildonDateEditorPrivate *priv;
673 g_assert (HILDON_IS_DATE_EDITOR (container));
676 editor = HILDON_DATE_EDITOR (container);
677 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
680 if (include_internals) {
681 (*callback) (priv->frame, callback_data);
682 (*callback) (priv->d_button_image, callback_data);
687 hildon_date_editor_destroy (GtkObject *self)
689 HildonDateEditorPrivate *priv;
691 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
695 gtk_widget_unparent (priv->frame);
698 if (priv->d_button_image) {
699 gtk_widget_unparent (priv->d_button_image);
700 priv->d_button_image = NULL;
703 g_list_free (priv->delims);
707 if (GTK_OBJECT_CLASS (parent_class)->destroy)
708 GTK_OBJECT_CLASS (parent_class)->destroy (self);
712 * hildon_date_editor_new:
714 * Creates a new date editor. The current system date
715 * is shown in the editor.
717 * Returns: pointer to a new @HildonDateEditor widget.
720 hildon_date_editor_new (void)
722 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
726 * hildon_date_editor_set_date:
727 * @date: the @HildonDateEditor widget
732 * Sets the date shown in the editor.
735 hildon_date_editor_set_date (HildonDateEditor *editor,
740 HildonDateEditorPrivate *priv;
742 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
744 /* This function cannot be implemented by calling
745 component setters, since applying the individual
746 values one by one can make the date temporarily
747 invalid (depending on what the previous values were),
748 which in turn causes that the desired date
749 is not set (even though it's valid). We must set all the
750 components at one go and not try to do any validation etc
753 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
756 if (g_date_valid_dmy (day, month, year))
765 g_date_set_dmy (&date, day, month, year);
767 /* We apply the new values, but do not want automatic focus move
769 g_snprintf (buffer, sizeof (buffer), "%04d", year);
770 g_signal_handlers_block_by_func (priv->y_entry,
771 (gpointer) hildon_date_editor_entry_changed, editor);
772 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
773 g_signal_handlers_unblock_by_func (priv->y_entry,
774 (gpointer) hildon_date_editor_entry_changed, editor);
776 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
777 g_signal_handlers_block_by_func (priv->m_entry,
778 (gpointer) hildon_date_editor_entry_changed, editor);
779 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
780 g_signal_handlers_unblock_by_func (priv->m_entry,
781 (gpointer) hildon_date_editor_entry_changed, editor);
783 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
784 g_signal_handlers_block_by_func (priv->d_entry,
785 (gpointer) hildon_date_editor_entry_changed, editor);
786 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
787 g_signal_handlers_unblock_by_func (priv->d_entry,
788 (gpointer) hildon_date_editor_entry_changed, editor);
790 g_object_notify (G_OBJECT (editor), "year");
791 g_object_notify (G_OBJECT (editor), "month");
792 g_object_notify (G_OBJECT (editor), "day");
797 * hildon_date_editor_get_date:
798 * @date: the @HildonDateEditor widget
803 * Gets the date represented by the date editor.
804 * You can pass NULL to any of the pointers if
805 * you're not interested in obtaining it.
809 hildon_date_editor_get_date (HildonDateEditor *date,
814 HildonDateEditorPrivate *priv;
816 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
818 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
820 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
821 is unclear. They do not neccesarily match and still the texts are
822 used as return values and members for some internal validation!!
823 At least a partly reason is to allow empty text to become
824 0 return value, while members are restricted to valid ranges?!
825 However, if we change the current way, we are likely to break
826 some applications if they rely on some specific way how this
827 widget currently handles empty values and temporarily invalid values.
829 The key issue is this: What should the _get methods return while
830 user is editing a field and the result is incomplete. The
831 partial result? The last good result? If we return partial result
832 we also need a way to inform if the date is not valid. Current
833 implementation is some kind of hybrid of these two...
836 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
838 easily fails, since set_day tries to force validity while get_day
841 Proposal: Always return the same values that are shown in the
842 fields. We add a separate flag (Or use GDate) to
843 indicate if the current date is valid. This would allow
844 setters to make the date invalid as well. */
847 *year = /*priv->year;*/
848 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
850 *month = /*priv->month;*/
851 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
853 *day = /*priv->day;*/
854 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
857 /* icon button press event */
859 hildon_date_editor_icon_press (GtkWidget *widget,
862 g_assert (GTK_IS_WIDGET (widget));
863 g_assert (HILDON_IS_DATE_EDITOR (data));
865 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
871 hildon_date_editor_entry_focus_in (GtkWidget *widget,
872 GdkEventFocus *event,
875 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
882 popup_calendar_dialog (HildonDateEditor *ed)
884 guint y = 0, m = 0, d = 0;
890 hildon_date_editor_get_date (ed, &y, &m, &d);
892 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
893 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
895 g_value_init (&val, G_TYPE_INT);
896 /* Set max/min year in calendar popup to date editor values */
897 g_object_get_property (G_OBJECT (ed), "min-year", &val);
898 g_object_set_property (G_OBJECT (popup), "min-year", &val);
899 g_object_get_property (G_OBJECT (ed), "max-year", &val);
900 g_object_set_property (G_OBJECT (popup), "max-year", &val);
902 /* Pop up calendar */
903 result = gtk_dialog_run (GTK_DIALOG (popup));
905 case GTK_RESPONSE_OK:
906 case GTK_RESPONSE_ACCEPT:
907 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
909 hildon_date_editor_set_date (ed, y, m, d);
912 gtk_widget_destroy (popup);
915 /* button released */
917 hildon_date_editor_released (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 icon state. The clicked cycle raises the dialog */
928 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
933 /* button released */
935 hildon_date_editor_clicked (GtkWidget *widget,
938 HildonDateEditor *ed;
940 g_assert (GTK_IS_WIDGET (widget));
941 g_assert (HILDON_IS_DATE_EDITOR (data));
943 ed = HILDON_DATE_EDITOR (data);
945 /* restores the non-clicked button state and raises the dialog */
946 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
947 popup_calendar_dialog (ed);
952 /* This is called whenever some editor filed loses the focus and
953 when the all of the fields are filled.
954 Earlier this was called whenever an entry changed */
955 /* FIXME: Validation on focus_out is broken by concept */
957 hildon_date_editor_entry_validate (GtkWidget *widget,
960 HildonDateEditor *ed;
961 HildonDateEditorPrivate *priv;
962 gint d, m, y, max_days;
963 gboolean r; /* temp return values for signals */
965 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
967 g_assert (HILDON_IS_DATE_EDITOR (data));
968 g_assert (GTK_IS_ENTRY (widget));
970 ed = HILDON_DATE_EDITOR (data);
971 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
974 if (priv->skip_validation)
977 /*check if the calling entry is empty*/
978 text = gtk_entry_get_text (GTK_ENTRY (widget));
979 if(text == NULL || text[0] == 0)
981 if (widget == priv->d_entry)
982 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
983 else if(widget == priv->m_entry)
984 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
986 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
988 /* restore empty entry to safe value */
989 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
993 /* Ok, we now check validity. Some fields can be empty */
994 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
995 if (text == NULL || text[0] == 0) return error_code;
997 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
998 if (text == NULL || text[0] == 0) return error_code;
1000 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
1001 if (text == NULL || text[0] == 0) return error_code;
1004 /* Did it actually change? */
1005 if (d != priv->day || m != priv->month || y != priv->year)
1007 /* We could/should use hildon_date_editor_set_year and such functions
1008 * to set the date, instead of use gtk_entry_set_text, and then change
1009 * the priv member but hildon_date_editor_set_year and such functions
1010 * check if the date is valid, we do want to do date validation check
1011 * here according to spec */
1013 /* Validate month */
1014 if (widget == priv->m_entry) {
1016 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1020 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1026 if(widget == priv->y_entry) {
1027 if (y < priv->min_year) {
1028 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1031 else if (y > priv->max_year) {
1032 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1037 /* Validate day. We have to do this in every case, since
1038 changing month or year can make the day number to be invalid */
1039 max_days = g_date_get_days_in_month (m,y);
1041 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1044 else if (d > max_days) {
1046 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1049 else { /* the date does not exist (is invalid) */
1050 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1051 /* check what was changed and restore previous value */
1052 if (widget == priv->y_entry)
1054 else if (widget == priv->m_entry)
1061 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1063 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1065 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1069 /* Fix and reformat the date after error signal is processed.
1070 reformatting can be needed even in a such case that numerical
1071 values of the date components are the same as earlier. */
1072 hildon_date_editor_set_date (ed, y, m, d);
1078 hildon_date_editor_entry_select_all (GtkWidget *widget)
1080 GDK_THREADS_ENTER ();
1082 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1084 GDK_THREADS_LEAVE ();
1089 /* When entry becomes full, we move the focus to the next field.
1090 If we are on the last field, the whole contents are validated. */
1092 hildon_date_editor_entry_changed (GtkEditable *ed,
1098 HildonDateEditorPrivate *priv;
1099 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1101 g_assert (GTK_IS_ENTRY (ed));
1102 g_assert (HILDON_IS_DATE_EDITOR (data));
1105 entry = GTK_ENTRY (ed);
1107 /* If day entry is full, move to next entry or validate */
1108 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1110 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1111 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1113 priv->skip_validation = TRUE;
1114 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1117 priv->skip_validation = FALSE;
1122 hildon_date_editor_keyrelease (GtkWidget *widget,
1126 HildonDateEditor *ed;
1127 HildonDateEditorPrivate *priv;
1129 g_return_val_if_fail (data, FALSE);
1130 g_return_val_if_fail (widget, FALSE);
1132 ed = HILDON_DATE_EDITOR (data);
1133 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1135 if (event->keyval == GDK_KP_Enter ||
1136 event->keyval == GDK_Return ||
1137 event->keyval == GDK_ISO_Enter) {
1138 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1140 popup_calendar_dialog (ed);
1143 } else if (event->keyval == GDK_Escape)
1144 priv->skip_validation = FALSE;
1149 /* keyboard handling */
1151 hildon_date_editor_keypress (GtkWidget *widget,
1155 HildonDateEditor *ed;
1156 HildonDateEditorPrivate *priv;
1158 g_assert (HILDON_IS_DATE_EDITOR (data));
1159 g_assert (GTK_IS_ENTRY (widget));
1161 ed = HILDON_DATE_EDITOR (data);
1163 switch (event->keyval) {
1166 /* Ignore return value, since we want to handle event at all times.
1167 otherwise vkb would popup when the keyrepeat starts. */
1168 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1171 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1172 priv->skip_validation = TRUE;
1182 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1183 GdkEventFocus *event,
1186 HildonDateEditor *ed;
1187 HildonDateEditorPrivate *priv;
1189 g_assert (HILDON_IS_DATE_EDITOR (data));
1191 ed = HILDON_DATE_EDITOR (data);
1192 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1195 hildon_date_editor_entry_validate (widget, data);
1196 priv->skip_validation = FALSE;
1202 hildon_date_editor_date_error (HildonDateEditor *editor,
1203 HildonDateTimeError type)
1205 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1210 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1211 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1214 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1215 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1218 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1219 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1222 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1223 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1224 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1227 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1228 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1231 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1232 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1235 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1236 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1239 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1240 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1241 priv->min_year, priv->max_year);
1244 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1245 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1248 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1249 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1253 /*default error message ?*/
1261 hildon_date_editor_size_request (GtkWidget *widget,
1262 GtkRequisition *requisition)
1264 HildonDateEditor *ed;
1265 HildonDateEditorPrivate *priv;
1266 GtkRequisition f_req, img_req;
1268 g_assert (GTK_IS_WIDGET (widget));
1269 g_assert (requisition != NULL);
1271 ed = HILDON_DATE_EDITOR (widget);
1272 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1275 /* Our own children affect our size */
1276 gtk_widget_size_request (priv->frame, &f_req);
1277 gtk_widget_size_request (priv->d_button_image, &img_req);
1279 /* calculate our size */
1280 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1282 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1283 doing so would break current pixel specifications, since
1284 the text entry by itself is already 30px tall + then frame takes
1286 requisition->height = DATE_EDITOR_HEIGHT;
1290 hildon_date_editor_size_allocate (GtkWidget *widget,
1291 GtkAllocation *allocation)
1293 HildonDateEditor *ed;
1294 HildonDateEditorPrivate *priv;
1295 GtkAllocation f_alloc, img_alloc;
1297 GtkRequisition max_req;
1301 g_assert (GTK_IS_WIDGET (widget));
1302 g_assert (allocation != NULL);
1304 ed = HILDON_DATE_EDITOR (widget);
1305 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1307 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1308 widget->allocation = *allocation;
1310 gtk_widget_get_child_requisition (widget, &max_req);
1312 /* Center vertically */
1313 f_alloc.y = img_alloc.y = allocation->y +
1314 MAX (allocation->height - max_req.height, 0) / 2;
1316 /* Center horizontally */
1317 f_alloc.x = img_alloc.x = allocation->x +
1318 MAX (allocation->width - max_req.width, 0) / 2;
1320 /* calculate allocations */
1321 if (GTK_WIDGET_VISIBLE (widget)) {
1322 /* allocate frame */
1323 gtk_widget_get_child_requisition (priv->frame, &req);
1325 f_alloc.width = req.width;
1326 f_alloc.height = max_req.height;
1329 gtk_widget_get_child_requisition (priv->d_button_image,
1332 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1333 img_alloc.width = req.width;
1334 img_alloc.height = max_req.height;
1338 img_alloc.x = f_alloc.x;
1339 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1342 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1343 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1346 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1347 gtk_widget_size_allocate (priv->frame, &f_alloc);
1351 /* FIXME: We really should not alloc delimeters by hand (since they
1352 are not our own children, but we need to force to appear
1353 higher. This ugly hack is needed to compensate the forced
1354 height in size_request. */
1355 for (iter = priv->delims; iter; iter = iter->next)
1358 GtkAllocation alloc;
1360 delim = GTK_WIDGET (iter->data);
1361 alloc = delim->allocation;
1362 alloc.height = max_req.height;
1363 alloc.y = priv->d_entry->allocation.y - 2;
1365 gtk_widget_size_allocate (delim, &alloc);
1370 hildon_date_editor_focus (GtkWidget *widget,
1371 GtkDirectionType direction)
1374 GtkDirectionType effective_direction;
1376 g_assert (HILDON_IS_DATE_EDITOR (widget));
1378 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1381 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1387 * hildon_date_editor_set_year:
1388 * @editor: the @HildonDateEditor widget
1391 * Sets the year shown in the editor.
1393 * Returns: TRUE if the year is valid and has been set.
1396 hildon_date_editor_set_year (HildonDateEditor *editor,
1399 HildonDateEditorPrivate *priv;
1400 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1402 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1405 if (g_date_valid_dmy (priv->day, priv->month, year))
1410 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1412 /* We apply the new day, but do not want automatic focus move
1413 etc to take place */
1414 g_signal_handlers_block_by_func (priv->y_entry,
1415 (gpointer) hildon_date_editor_entry_changed, editor);
1416 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1417 g_signal_handlers_unblock_by_func (priv->y_entry,
1418 (gpointer) hildon_date_editor_entry_changed, editor);
1420 g_object_notify (G_OBJECT(editor), "year");
1428 * hildon_date_editor_set_month:
1429 * @editor: the @HildonDateEditor widget
1432 * Sets the month shown in the editor.
1434 * Returns: TRUE if the month is valid and has been set.
1437 hildon_date_editor_set_month (HildonDateEditor *editor,
1440 HildonDateEditorPrivate *priv;
1441 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1442 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1445 if (g_date_valid_dmy (priv->day, month, priv->year))
1450 priv->month = month;
1451 g_date_set_dmy (&date, priv->day, month, priv->year);
1452 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1454 /* We apply the new day, but do not want automatic focus move
1455 etc to take place */
1456 g_signal_handlers_block_by_func (priv->m_entry,
1457 (gpointer) hildon_date_editor_entry_changed, editor);
1458 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1459 g_signal_handlers_unblock_by_func (priv->m_entry,
1460 (gpointer) hildon_date_editor_entry_changed, editor);
1462 g_object_notify (G_OBJECT (editor), "month");
1469 * hildon_date_editor_set_day:
1470 * @editor: the @HildonDateEditor widget
1473 * Sets the day shown in the editor.
1475 * Returns: TRUE if the day is valid and has been set.
1478 hildon_date_editor_set_day (HildonDateEditor *editor,
1481 HildonDateEditorPrivate *priv;
1483 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1484 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1487 if (g_date_valid_dmy (day, priv->month, priv->year))
1493 g_date_set_dmy (&date, day, priv->month, priv->year);
1494 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1496 /* We apply the new day, but do not want automatic focus move
1497 etc to take place */
1498 g_signal_handlers_block_by_func (priv->d_entry,
1499 (gpointer) hildon_date_editor_entry_changed, editor);
1500 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1501 g_signal_handlers_unblock_by_func (priv->d_entry,
1502 (gpointer) hildon_date_editor_entry_changed, editor);
1504 g_object_notify (G_OBJECT(editor), "day");
1511 * hildon_date_editor_get_year:
1512 * @editor: the @HildonDateEditor widget
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)));