1 /* vim:set sw=4 expandtab cino=(0:
3 * This file is a part of hildon
5 * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
7 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@nokia.com>
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; version 2.1 of
12 * the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful, but
15 * WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
27 * SECTION:hildon-date-editor
28 * @short_description: A widget which queries a date from user or opens
29 * a HildonCalendarPopup.
30 * @see_also: #HildonCalendarPopup, #HildonTimeEditor
32 * HildonDateEditor is a widget with three entry fields (day, month,
33 * year) and an icon (button): clicking on the icon opens up a
34 * HildonCalendarPopup.
40 * GtkWidget *date_editor;
42 * dialog = GTK_DIALOG (gtk_dialog_new ());
43 * date_editor = hildon_date_editor_new ();
45 * gtk_box_pack_start (GTK_BOX (dialog->vbox), gtk_label_new ("Choose a date"), FALSE, FALSE, 10);
46 * gtk_box_pack_start (GTK_BOX (dialog->vbox), date_editor, FALSE, FALSE, 10);
47 * gtk_dialog_add_button (dialog, "Close", GTK_RESPONSE_CANCEL);
49 * gtk_widget_show_all (GTK_WIDGET (dialog));
50 * gtk_dialog_run (dialog);
52 * hildon_date_editor_get_date (HILDON_DATE_EDITOR (date_editor), &y, &m, &d);
53 * g_debug ("Date: %u-%u-%u", y, m, d);
69 #include <gdk/gdkkeysyms.h>
71 #include "hildon-date-editor.h"
72 #include "hildon-calendar-popup.h"
73 #include "hildon-defines.h"
74 #include "hildon-marshalers.h"
75 #include "hildon-enum-types.h"
76 #include "hildon-time-editor.h"
77 #include "hildon-banner.h"
78 #include "hildon-date-editor-private.h"
79 #include "hildon-private.h"
81 #define _(string) dgettext("hildon-libs", string)
83 #define c_(string) dgettext("hildon-common-strings", string)
85 #define ENTRY_BORDERS 11
87 #define DATE_EDITOR_HEIGHT 30
89 #define DAY_ENTRY_WIDTH 2
91 #define MONTH_ENTRY_WIDTH 2
93 #define YEAR_ENTRY_WIDTH 4
95 #define DEFAULT_MIN_YEAR 1970
97 #define DEFAULT_MAX_YEAR 2037
99 static GtkContainerClass* parent_class;
102 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
105 hildon_date_editor_init (HildonDateEditor *editor);
108 hildon_date_editor_icon_press (GtkWidget *widget,
112 hildon_date_editor_released (GtkWidget *widget,
116 hildon_date_editor_keypress (GtkWidget *widget,
121 hildon_date_editor_keyrelease (GtkWidget *widget,
125 hildon_date_editor_clicked (GtkWidget *widget,
129 hildon_date_editor_entry_validate (GtkWidget *widget,
133 hildon_date_editor_entry_changed (GtkEditable *widget,
137 hildon_date_editor_entry_focus_out (GtkWidget *widget,
138 GdkEventFocus *event,
142 hildon_date_editor_date_error (HildonDateEditor *editor,
143 HildonDateTimeError type);
146 hildon_date_editor_entry_focus_in (GtkWidget *widget,
147 GdkEventFocus *event,
151 hildon_date_editor_get_property (GObject *object,
157 hildon_date_editor_set_property (GObject *object,
162 hildon_child_forall (GtkContainer *container,
163 gboolean include_internals,
164 GtkCallback callback,
165 gpointer callback_data);
168 hildon_date_editor_destroy (GtkObject *self);
171 hildon_date_editor_size_allocate (GtkWidget *widget,
172 GtkAllocation *allocation);
175 hildon_date_editor_size_request (GtkWidget *widget,
176 GtkRequisition *requisition);
178 hildon_date_editor_focus (GtkWidget *widget,
179 GtkDirectionType direction);
181 hildon_date_editor_entry_select_all (GtkWidget *widget);
183 /* Property indices */
200 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
203 * hildon_date_editor_get_type:
205 * Initializes and returns the type of a hildon date editor.
207 * @Returns: GType of #HildonDateEditor
210 hildon_date_editor_get_type (void)
212 static GType editor_type = 0;
215 static const GTypeInfo editor_info = {
216 sizeof (HildonDateEditorClass),
217 NULL, /* base_init */
218 NULL, /* base_finalize */
219 (GClassInitFunc) hildon_date_editor_class_init,
220 NULL, /* class_finalize */
221 NULL, /* class_data */
222 sizeof (HildonDateEditor),
224 (GInstanceInitFunc) hildon_date_editor_init,
226 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
235 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
237 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
238 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
239 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
241 parent_class = g_type_class_peek_parent (editor_class);
243 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
245 gobject_class->set_property = hildon_date_editor_set_property;
246 gobject_class->get_property = hildon_date_editor_get_property;
247 widget_class->size_request = hildon_date_editor_size_request;
248 widget_class->size_allocate = hildon_date_editor_size_allocate;
249 widget_class->focus = hildon_date_editor_focus;
251 container_class->forall = hildon_child_forall;
252 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
254 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
256 date_editor_signals[DATE_ERROR] =
257 g_signal_new ("date-error",
258 G_OBJECT_CLASS_TYPE (gobject_class),
260 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
261 g_signal_accumulator_true_handled, NULL,
262 _hildon_marshal_BOOLEAN__ENUM,
263 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
266 * HildonDateEditor:year:
270 g_object_class_install_property (gobject_class, PROP_YEAR,
271 g_param_spec_uint ("year",
276 G_PARAM_READABLE | G_PARAM_WRITABLE));
279 * HildonDateEditor:month:
283 g_object_class_install_property (gobject_class, PROP_MONTH,
284 g_param_spec_uint ("month",
289 G_PARAM_READABLE | G_PARAM_WRITABLE));
292 * HildonDateEditor:day:
296 g_object_class_install_property (gobject_class, PROP_DAY,
297 g_param_spec_uint ("day",
302 G_PARAM_READABLE | G_PARAM_WRITABLE));
305 * HildonDateEditor:min-year:
307 * Minimum valid year.
309 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
310 g_param_spec_uint ("min-year",
311 "Minimum valid year",
312 "Minimum valid year",
318 * HildonDateEditor:max-year:
320 * Maximum valid year.
322 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
323 g_param_spec_uint ("max-year",
324 "Maximum valid year",
325 "Maximum valid year",
331 /* Forces setting of the icon to certain state. Used initially
332 and from the actual setter function */
334 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
339 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
340 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_SMALL);
342 priv->calendar_icon_pressed = pressed;
345 /* Sets the icon to given state (normal/pressed). Returns
346 info if the state actually changed. */
348 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
351 HildonDateEditorPrivate *priv;
353 g_assert (HILDON_IS_DATE_EDITOR (editor));
355 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
358 if (pressed != priv->calendar_icon_pressed) {
359 real_set_calendar_icon_state (priv, pressed);
366 /* Packing day, month and year entries depend on locale settings
367 We find out the order and all separators by converting a known
368 date to default format and inspecting the result string */
370 apply_locale_field_order (HildonDateEditorPrivate *priv)
372 GDate locale_test_date;
375 gchar *iter, *delim_text;
377 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
378 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
386 /* Try to convert the current location into number. */
387 value = strtoul (iter, &endp, 10);
389 /* If the conversion didn't progress or the detected value was
390 unknown (we used a fixed date, you remember), we treat
391 current position as a literal */
395 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
396 priv->d_entry, FALSE, FALSE, 0);
400 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
401 priv->m_entry, FALSE, FALSE, 0);
404 case 70: /* %x format uses only 2 numbers for some locales */
406 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
407 priv->y_entry, FALSE, FALSE, 0);
411 /* All non-number characters starting from current position
412 form the delimeter */
413 for (endp = iter; *endp; endp++)
414 if (g_ascii_isdigit (*endp))
417 /* Now endp points one place past the delimeter text */
418 delim_text = g_strndup (iter, endp - iter);
419 delim = gtk_label_new (delim_text);
420 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
421 delim, FALSE, FALSE, 0);
423 priv->delims = g_list_append (priv->delims, delim);
434 hildon_date_editor_init (HildonDateEditor *editor)
436 HildonDateEditorPrivate *priv;
439 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
442 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
444 gtk_widget_push_composite_child ();
446 /* initialize values */
447 g_date_clear (&cur_date, 1);
448 g_date_set_time (&cur_date, time (NULL));
450 priv->day = g_date_get_day (&cur_date);
451 priv->month = g_date_get_month (&cur_date);
452 priv->year = g_date_get_year (&cur_date);
453 priv->min_year = DEFAULT_MIN_YEAR;
454 priv->max_year = DEFAULT_MAX_YEAR;
457 priv->frame = gtk_frame_new (NULL);
458 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
460 priv->d_entry = gtk_entry_new ();
461 priv->m_entry = gtk_entry_new ();
462 priv->y_entry = gtk_entry_new ();
465 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
466 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
467 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
468 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
469 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
470 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
474 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
475 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
476 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
478 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
479 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
480 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
482 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
483 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
484 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
486 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
487 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
488 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
490 priv->d_box_date = gtk_hbox_new (FALSE, 0);
492 priv->d_button_image = gtk_button_new ();
493 priv->calendar_icon = gtk_image_new ();
494 real_set_calendar_icon_state (priv, FALSE);
495 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
497 apply_locale_field_order (priv);
499 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
500 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
501 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
502 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
504 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
505 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
506 gtk_widget_show_all (priv->frame);
507 gtk_widget_show_all (priv->d_button_image);
509 /* image button signal connects */
510 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
511 G_CALLBACK (hildon_date_editor_icon_press), editor);
512 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
513 G_CALLBACK (hildon_date_editor_released), editor);
514 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
515 G_CALLBACK (hildon_date_editor_clicked), editor);
516 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
517 G_CALLBACK (hildon_date_editor_keypress), editor);
519 /* entry signal connects */
520 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
521 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
523 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
524 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
526 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
527 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
529 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
530 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
532 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
533 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
535 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
536 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
538 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
539 G_CALLBACK (hildon_date_editor_keypress), editor);
541 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
542 G_CALLBACK (hildon_date_editor_keypress), editor);
544 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
545 G_CALLBACK (hildon_date_editor_keypress), editor);
547 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
548 G_CALLBACK (hildon_date_editor_keyrelease), editor);
550 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
551 G_CALLBACK(hildon_date_editor_keyrelease), editor);
553 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
554 G_CALLBACK (hildon_date_editor_keyrelease), editor);
556 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
558 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
559 G_CALLBACK (hildon_date_editor_entry_changed), editor);
561 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
562 G_CALLBACK (hildon_date_editor_entry_changed), editor);
564 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
565 G_CALLBACK (hildon_date_editor_entry_changed), editor);
567 gtk_widget_pop_composite_child ();
571 hildon_date_editor_set_property (GObject *object,
576 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
577 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
585 hildon_date_editor_set_year (editor, g_value_get_uint (value));
589 hildon_date_editor_set_month (editor, g_value_get_uint (value));
593 hildon_date_editor_set_day (editor, g_value_get_uint (value));
597 val = g_value_get_uint (value);
598 priv->min_year = val;
599 /* Clamp current year */
600 if (hildon_date_editor_get_year (editor) < priv->min_year)
601 hildon_date_editor_set_year (editor, priv->min_year);
605 val = g_value_get_uint (value);
606 priv->max_year = val;
607 /* Clamp current year */
608 if (hildon_date_editor_get_year (editor) > priv->max_year)
609 hildon_date_editor_set_year (editor, priv->max_year);
613 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
619 hildon_date_editor_get_property (GObject *object,
624 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
625 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
630 g_value_set_uint (value, hildon_date_editor_get_year (editor));
634 g_value_set_uint (value, hildon_date_editor_get_month (editor));
638 g_value_set_uint (value, hildon_date_editor_get_day (editor));
642 g_value_set_uint (value, priv->min_year);
646 g_value_set_uint (value, priv->max_year);
650 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
656 hildon_child_forall (GtkContainer *container,
657 gboolean include_internals,
658 GtkCallback callback,
659 gpointer callback_data)
661 HildonDateEditor *editor;
662 HildonDateEditorPrivate *priv;
664 g_assert (HILDON_IS_DATE_EDITOR (container));
667 editor = HILDON_DATE_EDITOR (container);
668 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
671 if (include_internals) {
672 (*callback) (priv->frame, callback_data);
673 (*callback) (priv->d_button_image, callback_data);
678 hildon_date_editor_destroy (GtkObject *self)
680 HildonDateEditorPrivate *priv;
682 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
686 gtk_widget_unparent (priv->frame);
689 if (priv->d_button_image) {
690 gtk_widget_unparent (priv->d_button_image);
691 priv->d_button_image = NULL;
694 g_list_free (priv->delims);
698 if (GTK_OBJECT_CLASS (parent_class)->destroy)
699 GTK_OBJECT_CLASS (parent_class)->destroy (self);
703 * hildon_date_editor_new:
705 * Creates a new date editor. The current system date
706 * is shown in the editor.
708 * Returns: pointer to a new @HildonDateEditor widget.
711 hildon_date_editor_new (void)
713 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
717 * hildon_date_editor_set_date:
718 * @date: the @HildonDateEditor widget
723 * Sets the date shown in the editor.
726 hildon_date_editor_set_date (HildonDateEditor *editor,
731 HildonDateEditorPrivate *priv;
733 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
735 /* This function cannot be implemented by calling
736 component setters, since applying the individual
737 values one by one can make the date temporarily
738 invalid (depending on what the previous values were),
739 which in turn causes that the desired date
740 is not set (even though it's valid). We must set all the
741 components at one go and not try to do any validation etc
744 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
747 if (g_date_valid_dmy (day, month, year))
756 g_date_set_dmy (&date, day, month, year);
758 /* We apply the new values, but do not want automatic focus move
760 g_snprintf (buffer, sizeof (buffer), "%04d", year);
761 g_signal_handlers_block_by_func (priv->y_entry,
762 (gpointer) hildon_date_editor_entry_changed, editor);
763 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
764 g_signal_handlers_unblock_by_func (priv->y_entry,
765 (gpointer) hildon_date_editor_entry_changed, editor);
767 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
768 g_signal_handlers_block_by_func (priv->m_entry,
769 (gpointer) hildon_date_editor_entry_changed, editor);
770 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
771 g_signal_handlers_unblock_by_func (priv->m_entry,
772 (gpointer) hildon_date_editor_entry_changed, editor);
774 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
775 g_signal_handlers_block_by_func (priv->d_entry,
776 (gpointer) hildon_date_editor_entry_changed, editor);
777 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
778 g_signal_handlers_unblock_by_func (priv->d_entry,
779 (gpointer) hildon_date_editor_entry_changed, editor);
781 g_object_notify (G_OBJECT (editor), "year");
782 g_object_notify (G_OBJECT (editor), "month");
783 g_object_notify (G_OBJECT (editor), "day");
788 * hildon_date_editor_get_date:
789 * @date: the @HildonDateEditor widget
794 * Gets the date represented by the date editor.
795 * You can pass NULL to any of the pointers if
796 * you're not interested in obtaining it.
800 hildon_date_editor_get_date (HildonDateEditor *date,
805 HildonDateEditorPrivate *priv;
807 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
809 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
811 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
812 is unclear. They do not neccesarily match and still the texts are
813 used as return values and members for some internal validation!!
814 At least a partly reason is to allow empty text to become
815 0 return value, while members are restricted to valid ranges?!
816 However, if we change the current way, we are likely to break
817 some applications if they rely on some specific way how this
818 widget currently handles empty values and temporarily invalid values.
820 The key issue is this: What should the _get methods return while
821 user is editing a field and the result is incomplete. The
822 partial result? The last good result? If we return partial result
823 we also need a way to inform if the date is not valid. Current
824 implementation is some kind of hybrid of these two...
827 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
829 easily fails, since set_day tries to force validity while get_day
832 Proposal: Always return the same values that are shown in the
833 fields. We add a separate flag (Or use GDate) to
834 indicate if the current date is valid. This would allow
835 setters to make the date invalid as well. */
838 *year = /*priv->year;*/
839 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
841 *month = /*priv->month;*/
842 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
844 *day = /*priv->day;*/
845 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
848 /* icon button press event */
850 hildon_date_editor_icon_press (GtkWidget *widget,
853 g_assert (GTK_IS_WIDGET (widget));
854 g_assert (HILDON_IS_DATE_EDITOR (data));
856 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
862 hildon_date_editor_entry_focus_in (GtkWidget *widget,
863 GdkEventFocus *event,
866 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
873 popup_calendar_dialog (HildonDateEditor *ed)
875 guint y = 0, m = 0, d = 0;
881 hildon_date_editor_get_date (ed, &y, &m, &d);
883 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
884 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
886 g_value_init (&val, G_TYPE_INT);
887 /* Set max/min year in calendar popup to date editor values */
888 g_object_get_property (G_OBJECT (ed), "min-year", &val);
889 g_object_set_property (G_OBJECT (popup), "min-year", &val);
890 g_object_get_property (G_OBJECT (ed), "max-year", &val);
891 g_object_set_property (G_OBJECT (popup), "max-year", &val);
893 /* Pop up calendar */
894 result = gtk_dialog_run (GTK_DIALOG (popup));
896 case GTK_RESPONSE_OK:
897 case GTK_RESPONSE_ACCEPT:
898 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
900 hildon_date_editor_set_date (ed, y, m, d);
903 gtk_widget_destroy (popup);
906 /* button released */
908 hildon_date_editor_released (GtkWidget *widget,
911 HildonDateEditor *ed;
913 g_assert (GTK_IS_WIDGET (widget));
914 g_assert (HILDON_IS_DATE_EDITOR (data));
916 ed = HILDON_DATE_EDITOR (data);
918 /* restores the icon state. The clicked cycle raises the dialog */
919 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
924 /* button released */
926 hildon_date_editor_clicked (GtkWidget *widget,
929 HildonDateEditor *ed;
931 g_assert (GTK_IS_WIDGET (widget));
932 g_assert (HILDON_IS_DATE_EDITOR (data));
934 ed = HILDON_DATE_EDITOR (data);
936 /* restores the non-clicked button state and raises the dialog */
937 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
938 popup_calendar_dialog (ed);
943 /* This is called whenever some editor filed loses the focus and
944 when the all of the fields are filled.
945 Earlier this was called whenever an entry changed */
946 /* FIXME: Validation on focus_out is broken by concept */
948 hildon_date_editor_entry_validate (GtkWidget *widget,
951 HildonDateEditor *ed;
952 HildonDateEditorPrivate *priv;
953 gint d, m, y, max_days;
954 gboolean r; /* temp return values for signals */
956 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
958 g_assert (HILDON_IS_DATE_EDITOR (data));
959 g_assert (GTK_IS_ENTRY (widget));
961 ed = HILDON_DATE_EDITOR (data);
962 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
965 if (priv->skip_validation)
968 /*check if the calling entry is empty*/
969 text = gtk_entry_get_text (GTK_ENTRY (widget));
970 if(text == NULL || text[0] == 0)
972 if (widget == priv->d_entry)
973 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
974 else if(widget == priv->m_entry)
975 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
977 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
979 /* restore empty entry to safe value */
980 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
984 /* Ok, we now check validity. Some fields can be empty */
985 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
986 if (text == NULL || text[0] == 0) return error_code;
988 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
989 if (text == NULL || text[0] == 0) return error_code;
991 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
992 if (text == NULL || text[0] == 0) return error_code;
995 /* Did it actually change? */
996 if (d != priv->day || m != priv->month || y != priv->year)
998 /* We could/should use hildon_date_editor_set_year and such functions
999 * to set the date, instead of use gtk_entry_set_text, and then change
1000 * the priv member but hildon_date_editor_set_year and such functions
1001 * check if the date is valid, we do want to do date validation check
1002 * here according to spec */
1004 /* Validate month */
1005 if (widget == priv->m_entry) {
1007 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1011 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1017 if(widget == priv->y_entry) {
1018 if (y < priv->min_year) {
1019 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1022 else if (y > priv->max_year) {
1023 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1028 /* Validate day. We have to do this in every case, since
1029 changing month or year can make the day number to be invalid */
1030 max_days = g_date_get_days_in_month (m,y);
1032 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1035 else if (d > max_days) {
1037 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1040 else { /* the date does not exist (is invalid) */
1041 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1042 /* check what was changed and restore previous value */
1043 if (widget == priv->y_entry)
1045 else if (widget == priv->m_entry)
1052 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1054 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1056 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1060 /* Fix and reformat the date after error signal is processed.
1061 reformatting can be needed even in a such case that numerical
1062 values of the date components are the same as earlier. */
1063 hildon_date_editor_set_date (ed, y, m, d);
1069 hildon_date_editor_entry_select_all (GtkWidget *widget)
1071 GDK_THREADS_ENTER ();
1073 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1075 GDK_THREADS_LEAVE ();
1080 /* When entry becomes full, we move the focus to the next field.
1081 If we are on the last field, the whole contents are validated. */
1083 hildon_date_editor_entry_changed (GtkEditable *ed,
1089 HildonDateEditorPrivate *priv;
1090 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1092 g_assert (GTK_IS_ENTRY (ed));
1093 g_assert (HILDON_IS_DATE_EDITOR (data));
1096 entry = GTK_ENTRY (ed);
1098 /* If day entry is full, move to next entry or validate */
1099 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1101 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1102 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1104 priv->skip_validation = TRUE;
1105 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1108 priv->skip_validation = FALSE;
1113 hildon_date_editor_keyrelease (GtkWidget *widget,
1117 HildonDateEditor *ed;
1118 HildonDateEditorPrivate *priv;
1120 g_return_val_if_fail (data, FALSE);
1121 g_return_val_if_fail (widget, FALSE);
1123 ed = HILDON_DATE_EDITOR (data);
1124 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1126 if (event->keyval == GDK_KP_Enter ||
1127 event->keyval == GDK_Return ||
1128 event->keyval == GDK_ISO_Enter) {
1129 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1131 popup_calendar_dialog (ed);
1134 } else if (event->keyval == GDK_Escape)
1135 priv->skip_validation = FALSE;
1140 /* keyboard handling */
1142 hildon_date_editor_keypress (GtkWidget *widget,
1146 HildonDateEditor *ed;
1147 HildonDateEditorPrivate *priv;
1149 g_assert (HILDON_IS_DATE_EDITOR (data));
1150 g_assert (GTK_IS_ENTRY (widget));
1152 ed = HILDON_DATE_EDITOR (data);
1154 switch (event->keyval) {
1157 /* Ignore return value, since we want to handle event at all times.
1158 otherwise vkb would popup when the keyrepeat starts. */
1159 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1162 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1163 priv->skip_validation = TRUE;
1173 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1174 GdkEventFocus *event,
1177 HildonDateEditor *ed;
1178 HildonDateEditorPrivate *priv;
1180 g_assert (HILDON_IS_DATE_EDITOR (data));
1182 ed = HILDON_DATE_EDITOR (data);
1183 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1186 hildon_date_editor_entry_validate (widget, data);
1187 priv->skip_validation = FALSE;
1193 hildon_date_editor_date_error (HildonDateEditor *editor,
1194 HildonDateTimeError type)
1196 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1201 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1202 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1205 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1206 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1209 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1210 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1213 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1214 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1215 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1218 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1219 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1222 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1223 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1226 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1227 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1230 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1231 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1232 priv->min_year, priv->max_year);
1235 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1236 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1239 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1240 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1244 /*default error message ?*/
1252 hildon_date_editor_size_request (GtkWidget *widget,
1253 GtkRequisition *requisition)
1255 HildonDateEditor *ed;
1256 HildonDateEditorPrivate *priv;
1257 GtkRequisition f_req, img_req;
1259 g_assert (GTK_IS_WIDGET (widget));
1260 g_assert (requisition != NULL);
1262 ed = HILDON_DATE_EDITOR (widget);
1263 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1266 /* Our own children affect our size */
1267 gtk_widget_size_request (priv->frame, &f_req);
1268 gtk_widget_size_request (priv->d_button_image, &img_req);
1270 /* calculate our size */
1271 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1273 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1274 doing so would break current pixel specifications, since
1275 the text entry by itself is already 30px tall + then frame takes
1277 requisition->height = DATE_EDITOR_HEIGHT;
1281 hildon_date_editor_size_allocate (GtkWidget *widget,
1282 GtkAllocation *allocation)
1284 HildonDateEditor *ed;
1285 HildonDateEditorPrivate *priv;
1286 GtkAllocation f_alloc, img_alloc;
1288 GtkRequisition max_req;
1292 g_assert (GTK_IS_WIDGET (widget));
1293 g_assert (allocation != NULL);
1295 ed = HILDON_DATE_EDITOR (widget);
1296 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1298 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1299 widget->allocation = *allocation;
1301 gtk_widget_get_child_requisition (widget, &max_req);
1303 /* Center vertically */
1304 f_alloc.y = img_alloc.y = allocation->y +
1305 MAX (allocation->height - max_req.height, 0) / 2;
1307 /* Center horizontally */
1308 f_alloc.x = img_alloc.x = allocation->x +
1309 MAX (allocation->width - max_req.width, 0) / 2;
1311 /* calculate allocations */
1312 if (GTK_WIDGET_VISIBLE (widget)) {
1313 /* allocate frame */
1314 gtk_widget_get_child_requisition (priv->frame, &req);
1316 f_alloc.width = req.width;
1317 f_alloc.height = max_req.height;
1320 gtk_widget_get_child_requisition (priv->d_button_image,
1323 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1324 img_alloc.width = req.width;
1325 img_alloc.height = max_req.height;
1329 img_alloc.x = f_alloc.x;
1330 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1333 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1334 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1337 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1338 gtk_widget_size_allocate (priv->frame, &f_alloc);
1342 /* FIXME: We really should not alloc delimeters by hand (since they
1343 are not our own children, but we need to force to appear
1344 higher. This ugly hack is needed to compensate the forced
1345 height in size_request. */
1346 for (iter = priv->delims; iter; iter = iter->next)
1349 GtkAllocation alloc;
1351 delim = GTK_WIDGET (iter->data);
1352 alloc = delim->allocation;
1353 alloc.height = max_req.height;
1354 alloc.y = priv->d_entry->allocation.y - 2;
1356 gtk_widget_size_allocate (delim, &alloc);
1361 hildon_date_editor_focus (GtkWidget *widget,
1362 GtkDirectionType direction)
1365 GtkDirectionType effective_direction;
1367 g_assert (HILDON_IS_DATE_EDITOR (widget));
1369 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1372 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1378 * hildon_date_editor_set_year:
1379 * @editor: the @HildonDateEditor widget
1382 * Sets the year shown in the editor.
1384 * Returns: TRUE if the year is valid and has been set.
1387 hildon_date_editor_set_year (HildonDateEditor *editor,
1390 HildonDateEditorPrivate *priv;
1391 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1393 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1396 if (g_date_valid_dmy (priv->day, priv->month, year))
1401 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1403 /* We apply the new day, but do not want automatic focus move
1404 etc to take place */
1405 g_signal_handlers_block_by_func (priv->y_entry,
1406 (gpointer) hildon_date_editor_entry_changed, editor);
1407 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1408 g_signal_handlers_unblock_by_func (priv->y_entry,
1409 (gpointer) hildon_date_editor_entry_changed, editor);
1411 g_object_notify (G_OBJECT(editor), "year");
1419 * hildon_date_editor_set_month:
1420 * @editor: the @HildonDateEditor widget
1423 * Sets the month shown in the editor.
1425 * Returns: TRUE if the month is valid and has been set.
1428 hildon_date_editor_set_month (HildonDateEditor *editor,
1431 HildonDateEditorPrivate *priv;
1432 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1433 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1436 if (g_date_valid_dmy (priv->day, month, priv->year))
1441 priv->month = month;
1442 g_date_set_dmy (&date, priv->day, month, priv->year);
1443 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1445 /* We apply the new day, but do not want automatic focus move
1446 etc to take place */
1447 g_signal_handlers_block_by_func (priv->m_entry,
1448 (gpointer) hildon_date_editor_entry_changed, editor);
1449 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1450 g_signal_handlers_unblock_by_func (priv->m_entry,
1451 (gpointer) hildon_date_editor_entry_changed, editor);
1453 g_object_notify (G_OBJECT (editor), "month");
1460 * hildon_date_editor_set_day:
1461 * @editor: the @HildonDateEditor widget
1464 * Sets the day shown in the editor.
1466 * Returns: TRUE if the day is valid and has been set.
1469 hildon_date_editor_set_day (HildonDateEditor *editor,
1472 HildonDateEditorPrivate *priv;
1474 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1475 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1478 if (g_date_valid_dmy (day, priv->month, priv->year))
1484 g_date_set_dmy (&date, day, priv->month, priv->year);
1485 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1487 /* We apply the new day, but do not want automatic focus move
1488 etc to take place */
1489 g_signal_handlers_block_by_func (priv->d_entry,
1490 (gpointer) hildon_date_editor_entry_changed, editor);
1491 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1492 g_signal_handlers_unblock_by_func (priv->d_entry,
1493 (gpointer) hildon_date_editor_entry_changed, editor);
1495 g_object_notify (G_OBJECT(editor), "day");
1502 * hildon_date_editor_get_year:
1503 * @editor: the @HildonDateEditor widget
1505 * Returns: the current year shown in the editor.
1508 hildon_date_editor_get_year (HildonDateEditor *editor)
1510 HildonDateEditorPrivate *priv;
1511 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1513 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1516 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1520 * hildon_date_editor_get_month:
1521 * @editor: the @HildonDateEditor widget
1523 * Gets the month shown in the editor.
1525 * Returns: the current month shown in the editor.
1528 hildon_date_editor_get_month (HildonDateEditor *editor)
1530 HildonDateEditorPrivate *priv;
1531 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1533 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1536 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1540 * hildon_date_editor_get_day:
1541 * @editor: the @HildonDateEditor widget
1543 * Gets the day shown in the editor.
1545 * Returns: the current day shown in the editor
1548 hildon_date_editor_get_day (HildonDateEditor *editor)
1550 HildonDateEditorPrivate *priv;
1551 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1553 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1556 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));