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);
64 #include "hildon-date-editor.h"
67 #include <gtk/gtkenums.h>
68 #include <gdk/gdkkeysyms.h>
73 #include "hildon-calendar-popup.h"
74 #include "hildon-defines.h"
75 #include "hildon-private.h"
76 #include "hildon-marshalers.h"
77 #include "hildon-enum-types.h"
78 #include "hildon-time-editor.h"
79 #include "hildon-banner.h"
81 #include "hildon-date-editor-private.h"
83 #define _(string) dgettext("hildon-libs", string)
85 #define c_(string) dgettext("hildon-common-strings", string)
87 #define ENTRY_BORDERS 11
89 #define DATE_EDITOR_HEIGHT 30
91 #define DAY_ENTRY_WIDTH 2
93 #define MONTH_ENTRY_WIDTH 2
95 #define YEAR_ENTRY_WIDTH 4
97 #define DEFAULT_MIN_YEAR 1970
99 #define DEFAULT_MAX_YEAR 2037
101 static GtkContainerClass* parent_class;
104 hildon_date_editor_class_init (HildonDateEditorClass *editor_class);
107 hildon_date_editor_init (HildonDateEditor *editor);
110 hildon_date_editor_icon_press (GtkWidget *widget,
114 hildon_date_editor_released (GtkWidget *widget,
118 hildon_date_editor_keypress (GtkWidget *widget,
123 hildon_date_editor_keyrelease (GtkWidget *widget,
127 hildon_date_editor_clicked (GtkWidget *widget,
131 hildon_date_editor_entry_validate (GtkWidget *widget,
135 hildon_date_editor_entry_changed (GtkEditable *widget,
139 hildon_date_editor_entry_focus_out (GtkWidget *widget,
140 GdkEventFocus *event,
144 hildon_date_editor_date_error (HildonDateEditor *editor,
145 HildonDateTimeError type);
148 hildon_date_editor_entry_focus_in (GtkWidget *widget,
149 GdkEventFocus *event,
153 hildon_date_editor_get_property (GObject *object,
159 hildon_date_editor_set_property (GObject *object,
164 hildon_child_forall (GtkContainer *container,
165 gboolean include_internals,
166 GtkCallback callback,
167 gpointer callback_data);
170 hildon_date_editor_destroy (GtkObject *self);
173 hildon_date_editor_size_allocate (GtkWidget *widget,
174 GtkAllocation *allocation);
177 hildon_date_editor_size_request (GtkWidget *widget,
178 GtkRequisition *requisition);
180 hildon_date_editor_focus (GtkWidget *widget,
181 GtkDirectionType direction);
183 hildon_date_editor_entry_select_all (GtkWidget *widget);
185 /* Property indices */
202 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
205 * hildon_date_editor_get_type:
207 * Initializes and returns the type of a hildon date editor.
209 * @Returns: GType of #HildonDateEditor
212 hildon_date_editor_get_type (void)
214 static GType editor_type = 0;
217 static const GTypeInfo editor_info = {
218 sizeof (HildonDateEditorClass),
219 NULL, /* base_init */
220 NULL, /* base_finalize */
221 (GClassInitFunc) hildon_date_editor_class_init,
222 NULL, /* class_finalize */
223 NULL, /* class_data */
224 sizeof (HildonDateEditor),
226 (GInstanceInitFunc) hildon_date_editor_init,
228 editor_type = g_type_register_static (GTK_TYPE_CONTAINER,
237 hildon_date_editor_class_init (HildonDateEditorClass *editor_class)
239 GtkContainerClass *container_class = GTK_CONTAINER_CLASS (editor_class);
240 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (editor_class);
241 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
243 parent_class = g_type_class_peek_parent (editor_class);
245 g_type_class_add_private (editor_class, sizeof (HildonDateEditorPrivate));
247 gobject_class->set_property = hildon_date_editor_set_property;
248 gobject_class->get_property = hildon_date_editor_get_property;
249 widget_class->size_request = hildon_date_editor_size_request;
250 widget_class->size_allocate = hildon_date_editor_size_allocate;
251 widget_class->focus = hildon_date_editor_focus;
253 container_class->forall = hildon_child_forall;
254 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
256 editor_class->date_error = (gpointer) hildon_date_editor_date_error;
258 date_editor_signals[DATE_ERROR] =
259 g_signal_new ("date-error",
260 G_OBJECT_CLASS_TYPE (gobject_class),
262 G_STRUCT_OFFSET (HildonDateEditorClass, date_error),
263 g_signal_accumulator_true_handled, NULL,
264 _hildon_marshal_BOOLEAN__ENUM,
265 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_TIME_ERROR);
268 * HildonDateEditor:year:
272 g_object_class_install_property (gobject_class, PROP_YEAR,
273 g_param_spec_uint ("year",
278 G_PARAM_READABLE | G_PARAM_WRITABLE));
281 * HildonDateEditor:month:
285 g_object_class_install_property (gobject_class, PROP_MONTH,
286 g_param_spec_uint ("month",
291 G_PARAM_READABLE | G_PARAM_WRITABLE));
294 * HildonDateEditor:day:
298 g_object_class_install_property (gobject_class, PROP_DAY,
299 g_param_spec_uint ("day",
304 G_PARAM_READABLE | G_PARAM_WRITABLE));
307 * HildonDateEditor:min-year:
309 * Minimum valid year.
311 g_object_class_install_property (gobject_class, PROP_MIN_YEAR,
312 g_param_spec_uint ("min-year",
313 "Minimum valid year",
314 "Minimum valid year",
320 * HildonDateEditor:max-year:
322 * Maximum valid year.
324 g_object_class_install_property (gobject_class, PROP_MAX_YEAR,
325 g_param_spec_uint ("max-year",
326 "Maximum valid year",
327 "Maximum valid year",
333 /* Forces setting of the icon to certain state. Used initially
334 and from the actual setter function */
336 real_set_calendar_icon_state (HildonDateEditorPrivate *priv,
341 gtk_image_set_from_icon_name (GTK_IMAGE (priv->calendar_icon),
342 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit", HILDON_ICON_SIZE_SMALL);
344 priv->calendar_icon_pressed = pressed;
347 /* Sets the icon to given state (normal/pressed). Returns
348 info if the state actually changed. */
350 hildon_date_editor_set_calendar_icon_state (HildonDateEditor *editor,
353 HildonDateEditorPrivate *priv;
355 g_assert (HILDON_IS_DATE_EDITOR (editor));
357 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
360 if (pressed != priv->calendar_icon_pressed) {
361 real_set_calendar_icon_state (priv, pressed);
368 /* Packing day, month and year entries depend on locale settings
369 We find out the order and all separators by converting a known
370 date to default format and inspecting the result string */
372 apply_locale_field_order (HildonDateEditorPrivate *priv)
374 GDate locale_test_date;
377 gchar *iter, *delim_text;
379 g_date_set_dmy (&locale_test_date, 1, 2, 1970);
380 (void) g_date_strftime (buffer, sizeof (buffer), "%x", &locale_test_date);
388 /* Try to convert the current location into number. */
389 value = strtoul (iter, &endp, 10);
391 /* If the conversion didn't progress or the detected value was
392 unknown (we used a fixed date, you remember), we treat
393 current position as a literal */
397 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
398 priv->d_entry, FALSE, FALSE, 0);
402 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
403 priv->m_entry, FALSE, FALSE, 0);
406 case 70: /* %x format uses only 2 numbers for some locales */
408 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
409 priv->y_entry, FALSE, FALSE, 0);
413 /* All non-number characters starting from current position
414 form the delimeter */
415 for (endp = iter; *endp; endp++)
416 if (g_ascii_isdigit (*endp))
419 /* Now endp points one place past the delimeter text */
420 delim_text = g_strndup (iter, endp - iter);
421 delim = gtk_label_new (delim_text);
422 gtk_box_pack_start (GTK_BOX (priv->d_box_date),
423 delim, FALSE, FALSE, 0);
425 priv->delims = g_list_append (priv->delims, delim);
436 hildon_date_editor_init (HildonDateEditor *editor)
438 HildonDateEditorPrivate *priv;
441 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
444 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (editor), GTK_NO_WINDOW);
446 gtk_widget_push_composite_child ();
448 /* initialize values */
449 g_date_clear (&cur_date, 1);
450 g_date_set_time (&cur_date, time (NULL));
452 priv->day = g_date_get_day (&cur_date);
453 priv->month = g_date_get_month (&cur_date);
454 priv->year = g_date_get_year (&cur_date);
455 priv->min_year = DEFAULT_MIN_YEAR;
456 priv->max_year = DEFAULT_MAX_YEAR;
459 priv->frame = gtk_frame_new (NULL);
460 gtk_container_set_border_width (GTK_CONTAINER (priv->frame), 0);
462 priv->d_entry = gtk_entry_new ();
463 priv->m_entry = gtk_entry_new ();
464 priv->y_entry = gtk_entry_new ();
467 g_object_set (G_OBJECT(priv->d_entry), "hildon-input-mode",
468 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
469 g_object_set (G_OBJECT(priv->m_entry), "hildon-input-mode",
470 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
471 g_object_set (G_OBJECT(priv->y_entry), "hildon-input-mode",
472 HILDON_GTK_INPUT_MODE_NUMERIC, NULL);
476 gtk_entry_set_width_chars (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
477 gtk_entry_set_width_chars (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
478 gtk_entry_set_width_chars (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
480 gtk_entry_set_max_length (GTK_ENTRY (priv->d_entry), DAY_ENTRY_WIDTH);
481 gtk_entry_set_max_length (GTK_ENTRY (priv->m_entry), MONTH_ENTRY_WIDTH);
482 gtk_entry_set_max_length (GTK_ENTRY (priv->y_entry), YEAR_ENTRY_WIDTH);
484 gtk_entry_set_has_frame (GTK_ENTRY (priv->d_entry), FALSE);
485 gtk_entry_set_has_frame (GTK_ENTRY (priv->m_entry), FALSE);
486 gtk_entry_set_has_frame (GTK_ENTRY (priv->y_entry), FALSE);
488 gtk_widget_set_composite_name (priv->d_entry, "day_entry");
489 gtk_widget_set_composite_name (priv->m_entry, "month_entry");
490 gtk_widget_set_composite_name (priv->y_entry, "year_entry");
492 priv->d_box_date = gtk_hbox_new (FALSE, 0);
494 priv->d_button_image = gtk_button_new ();
495 priv->calendar_icon = gtk_image_new ();
496 real_set_calendar_icon_state (priv, FALSE);
497 GTK_WIDGET_UNSET_FLAGS (priv->d_button_image, GTK_CAN_FOCUS | GTK_CAN_DEFAULT);
499 apply_locale_field_order (priv);
501 gtk_container_add (GTK_CONTAINER (priv->frame), priv->d_box_date);
502 gtk_container_add (GTK_CONTAINER (priv->d_button_image), priv->calendar_icon);
503 gtk_button_set_relief (GTK_BUTTON (priv->d_button_image), GTK_RELIEF_NONE);
504 gtk_button_set_focus_on_click (GTK_BUTTON (priv->d_button_image), FALSE);
506 gtk_widget_set_parent (priv->frame, GTK_WIDGET (editor));
507 gtk_widget_set_parent (priv->d_button_image, GTK_WIDGET (editor));
508 gtk_widget_show_all (priv->frame);
509 gtk_widget_show_all (priv->d_button_image);
511 /* image button signal connects */
512 g_signal_connect (GTK_OBJECT (priv->d_button_image), "pressed",
513 G_CALLBACK (hildon_date_editor_icon_press), editor);
514 g_signal_connect (GTK_OBJECT (priv->d_button_image), "released",
515 G_CALLBACK (hildon_date_editor_released), editor);
516 g_signal_connect (GTK_OBJECT (priv->d_button_image), "clicked",
517 G_CALLBACK (hildon_date_editor_clicked), editor);
518 g_signal_connect (GTK_OBJECT (priv->d_button_image), "key_press_event",
519 G_CALLBACK (hildon_date_editor_keypress), editor);
521 /* entry signal connects */
522 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-in-event",
523 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
525 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-in-event",
526 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
528 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-in-event",
529 G_CALLBACK (hildon_date_editor_entry_focus_in), editor);
531 g_signal_connect (GTK_OBJECT (priv->d_entry), "focus-out-event",
532 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
534 g_signal_connect (GTK_OBJECT (priv->m_entry), "focus-out-event",
535 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
537 g_signal_connect (GTK_OBJECT (priv->y_entry), "focus-out-event",
538 G_CALLBACK (hildon_date_editor_entry_focus_out), editor);
540 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-press-event",
541 G_CALLBACK (hildon_date_editor_keypress), editor);
543 g_signal_connect (GTK_OBJECT (priv->m_entry), "key-press-event",
544 G_CALLBACK (hildon_date_editor_keypress), editor);
546 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-press-event",
547 G_CALLBACK (hildon_date_editor_keypress), editor);
549 g_signal_connect (GTK_OBJECT (priv->d_entry), "key-release-event",
550 G_CALLBACK (hildon_date_editor_keyrelease), editor);
552 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
553 G_CALLBACK(hildon_date_editor_keyrelease), editor);
555 g_signal_connect (GTK_OBJECT (priv->y_entry), "key-release-event",
556 G_CALLBACK (hildon_date_editor_keyrelease), editor);
558 hildon_date_editor_set_date (editor, priv->year, priv->month, priv->day);
560 g_signal_connect (GTK_OBJECT (priv->d_entry), "changed",
561 G_CALLBACK (hildon_date_editor_entry_changed), editor);
563 g_signal_connect (GTK_OBJECT (priv->m_entry), "changed",
564 G_CALLBACK (hildon_date_editor_entry_changed), editor);
566 g_signal_connect (GTK_OBJECT (priv->y_entry), "changed",
567 G_CALLBACK (hildon_date_editor_entry_changed), editor);
569 gtk_widget_pop_composite_child ();
573 hildon_date_editor_set_property (GObject *object,
578 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
579 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
587 hildon_date_editor_set_year (editor, g_value_get_uint (value));
591 hildon_date_editor_set_month (editor, g_value_get_uint (value));
595 hildon_date_editor_set_day (editor, g_value_get_uint (value));
599 val = g_value_get_uint (value);
600 priv->min_year = val;
601 /* Clamp current year */
602 if (hildon_date_editor_get_year (editor) < priv->min_year)
603 hildon_date_editor_set_year (editor, priv->min_year);
607 val = g_value_get_uint (value);
608 priv->max_year = val;
609 /* Clamp current year */
610 if (hildon_date_editor_get_year (editor) > priv->max_year)
611 hildon_date_editor_set_year (editor, priv->max_year);
615 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
621 hildon_date_editor_get_property (GObject *object,
626 HildonDateEditor *editor = HILDON_DATE_EDITOR (object);
627 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
632 g_value_set_uint (value, hildon_date_editor_get_year (editor));
636 g_value_set_uint (value, hildon_date_editor_get_month (editor));
640 g_value_set_uint (value, hildon_date_editor_get_day (editor));
644 g_value_set_uint (value, priv->min_year);
648 g_value_set_uint (value, priv->max_year);
652 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
658 hildon_child_forall (GtkContainer *container,
659 gboolean include_internals,
660 GtkCallback callback,
661 gpointer callback_data)
663 HildonDateEditor *editor;
664 HildonDateEditorPrivate *priv;
666 g_assert (HILDON_IS_DATE_EDITOR (container));
669 editor = HILDON_DATE_EDITOR (container);
670 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
673 if (include_internals) {
674 (*callback) (priv->frame, callback_data);
675 (*callback) (priv->d_button_image, callback_data);
680 hildon_date_editor_destroy (GtkObject *self)
682 HildonDateEditorPrivate *priv;
684 priv = HILDON_DATE_EDITOR_GET_PRIVATE (self);
688 gtk_widget_unparent (priv->frame);
691 if (priv->d_button_image) {
692 gtk_widget_unparent (priv->d_button_image);
693 priv->d_button_image = NULL;
696 g_list_free (priv->delims);
700 if (GTK_OBJECT_CLASS (parent_class)->destroy)
701 GTK_OBJECT_CLASS (parent_class)->destroy (self);
705 * hildon_date_editor_new:
707 * Creates a new date editor. The current system date
708 * is shown in the editor.
710 * Returns: pointer to a new @HildonDateEditor widget.
713 hildon_date_editor_new (void)
715 return (GtkWidget *) g_object_new (HILDON_TYPE_DATE_EDITOR, NULL);
719 * hildon_date_editor_set_date:
720 * @date: the @HildonDateEditor widget
725 * Sets the date shown in the editor.
728 hildon_date_editor_set_date (HildonDateEditor *editor,
733 HildonDateEditorPrivate *priv;
735 g_return_if_fail (HILDON_IS_DATE_EDITOR (editor));
737 /* This function cannot be implemented by calling
738 component setters, since applying the individual
739 values one by one can make the date temporarily
740 invalid (depending on what the previous values were),
741 which in turn causes that the desired date
742 is not set (even though it's valid). We must set all the
743 components at one go and not try to do any validation etc
746 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
749 if (g_date_valid_dmy (day, month, year))
758 g_date_set_dmy (&date, day, month, year);
760 /* We apply the new values, but do not want automatic focus move
762 g_snprintf (buffer, sizeof (buffer), "%04d", year);
763 g_signal_handlers_block_by_func (priv->y_entry,
764 (gpointer) hildon_date_editor_entry_changed, editor);
765 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
766 g_signal_handlers_unblock_by_func (priv->y_entry,
767 (gpointer) hildon_date_editor_entry_changed, editor);
769 g_date_strftime (buffer, sizeof (buffer), "%m", &date);
770 g_signal_handlers_block_by_func (priv->m_entry,
771 (gpointer) hildon_date_editor_entry_changed, editor);
772 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
773 g_signal_handlers_unblock_by_func (priv->m_entry,
774 (gpointer) hildon_date_editor_entry_changed, editor);
776 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
777 g_signal_handlers_block_by_func (priv->d_entry,
778 (gpointer) hildon_date_editor_entry_changed, editor);
779 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
780 g_signal_handlers_unblock_by_func (priv->d_entry,
781 (gpointer) hildon_date_editor_entry_changed, editor);
783 g_object_notify (G_OBJECT (editor), "year");
784 g_object_notify (G_OBJECT (editor), "month");
785 g_object_notify (G_OBJECT (editor), "day");
790 * hildon_date_editor_get_date:
791 * @date: the @HildonDateEditor widget
796 * Gets the date represented by the date editor.
797 * You can pass NULL to any of the pointers if
798 * you're not interested in obtaining it.
802 hildon_date_editor_get_date (HildonDateEditor *date,
807 HildonDateEditorPrivate *priv;
809 g_return_if_fail (HILDON_IS_DATE_EDITOR (date));
811 priv = HILDON_DATE_EDITOR_GET_PRIVATE (date);
813 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
814 is unclear. They do not neccesarily match and still the texts are
815 used as return values and members for some internal validation!!
816 At least a partly reason is to allow empty text to become
817 0 return value, while members are restricted to valid ranges?!
818 However, if we change the current way, we are likely to break
819 some applications if they rely on some specific way how this
820 widget currently handles empty values and temporarily invalid values.
822 The key issue is this: What should the _get methods return while
823 user is editing a field and the result is incomplete. The
824 partial result? The last good result? If we return partial result
825 we also need a way to inform if the date is not valid. Current
826 implementation is some kind of hybrid of these two...
829 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
831 easily fails, since set_day tries to force validity while get_day
834 Proposal: Always return the same values that are shown in the
835 fields. We add a separate flag (Or use GDate) to
836 indicate if the current date is valid. This would allow
837 setters to make the date invalid as well. */
840 *year = /*priv->year;*/
841 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
843 *month = /*priv->month;*/
844 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
846 *day = /*priv->day;*/
847 (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));
850 /* icon button press event */
852 hildon_date_editor_icon_press (GtkWidget *widget,
855 g_assert (GTK_IS_WIDGET (widget));
856 g_assert (HILDON_IS_DATE_EDITOR (data));
858 hildon_date_editor_set_calendar_icon_state (HILDON_DATE_EDITOR (data), TRUE);
864 hildon_date_editor_entry_focus_in (GtkWidget *widget,
865 GdkEventFocus *event,
868 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, GTK_ENTRY (widget));
875 popup_calendar_dialog (HildonDateEditor *ed)
877 guint y = 0, m = 0, d = 0;
883 hildon_date_editor_get_date (ed, &y, &m, &d);
885 parent = gtk_widget_get_ancestor (GTK_WIDGET (ed), GTK_TYPE_WINDOW);
886 popup = hildon_calendar_popup_new (GTK_WINDOW (parent), y, m, d);
888 g_value_init (&val, G_TYPE_INT);
889 /* Set max/min year in calendar popup to date editor values */
890 g_object_get_property (G_OBJECT (ed), "min-year", &val);
891 g_object_set_property (G_OBJECT (popup), "min-year", &val);
892 g_object_get_property (G_OBJECT (ed), "max-year", &val);
893 g_object_set_property (G_OBJECT (popup), "max-year", &val);
895 /* Pop up calendar */
896 result = gtk_dialog_run (GTK_DIALOG (popup));
898 case GTK_RESPONSE_OK:
899 case GTK_RESPONSE_ACCEPT:
900 hildon_calendar_popup_get_date (HILDON_CALENDAR_POPUP (popup), &y,
902 hildon_date_editor_set_date (ed, y, m, d);
905 gtk_widget_destroy (popup);
908 /* button released */
910 hildon_date_editor_released (GtkWidget *widget,
913 HildonDateEditor *ed;
915 g_assert (GTK_IS_WIDGET (widget));
916 g_assert (HILDON_IS_DATE_EDITOR (data));
918 ed = HILDON_DATE_EDITOR (data);
920 /* restores the icon state. The clicked cycle raises the dialog */
921 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
926 /* button released */
928 hildon_date_editor_clicked (GtkWidget *widget,
931 HildonDateEditor *ed;
933 g_assert (GTK_IS_WIDGET (widget));
934 g_assert (HILDON_IS_DATE_EDITOR (data));
936 ed = HILDON_DATE_EDITOR (data);
938 /* restores the non-clicked button state and raises the dialog */
939 hildon_date_editor_set_calendar_icon_state (ed, FALSE);
940 popup_calendar_dialog (ed);
945 /* This is called whenever some editor filed loses the focus and
946 when the all of the fields are filled.
947 Earlier this was called whenever an entry changed */
948 /* FIXME: Validation on focus_out is broken by concept */
950 hildon_date_editor_entry_validate (GtkWidget *widget,
953 HildonDateEditor *ed;
954 HildonDateEditorPrivate *priv;
955 gint d, m, y, max_days;
956 gboolean r; /* temp return values for signals */
958 gint error_code = HILDON_DATE_TIME_ERROR_NO_ERROR;
960 g_assert (HILDON_IS_DATE_EDITOR (data));
961 g_assert (GTK_IS_ENTRY (widget));
963 ed = HILDON_DATE_EDITOR (data);
964 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
967 if (priv->skip_validation)
970 /*check if the calling entry is empty*/
971 text = gtk_entry_get_text (GTK_ENTRY (widget));
972 if(text == NULL || text[0] == 0)
974 if (widget == priv->d_entry)
975 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_DAY, &r);
976 else if(widget == priv->m_entry)
977 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_MONTH, &r);
979 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, HILDON_DATE_TIME_ERROR_EMPTY_YEAR, &r);
981 /* restore empty entry to safe value */
982 hildon_date_editor_set_date (ed, priv->year, priv->month, priv->day);
986 /* Ok, we now check validity. Some fields can be empty */
987 text = gtk_entry_get_text (GTK_ENTRY (priv->d_entry));
988 if (text == NULL || text[0] == 0) return error_code;
990 text = gtk_entry_get_text (GTK_ENTRY (priv->m_entry));
991 if (text == NULL || text[0] == 0) return error_code;
993 text = gtk_entry_get_text (GTK_ENTRY (priv->y_entry));
994 if (text == NULL || text[0] == 0) return error_code;
997 /* Did it actually change? */
998 if (d != priv->day || m != priv->month || y != priv->year)
1000 /* We could/should use hildon_date_editor_set_year and such functions
1001 * to set the date, instead of use gtk_entry_set_text, and then change
1002 * the priv member but hildon_date_editor_set_year and such functions
1003 * check if the date is valid, we do want to do date validation check
1004 * here according to spec */
1006 /* Validate month */
1007 if (widget == priv->m_entry) {
1009 error_code = HILDON_DATE_TIME_ERROR_MIN_MONTH;
1013 error_code = HILDON_DATE_TIME_ERROR_MAX_MONTH;
1019 if(widget == priv->y_entry) {
1020 if (y < priv->min_year) {
1021 error_code = HILDON_DATE_TIME_ERROR_MIN_YEAR;
1024 else if (y > priv->max_year) {
1025 error_code = HILDON_DATE_TIME_ERROR_MAX_YEAR;
1030 /* Validate day. We have to do this in every case, since
1031 changing month or year can make the day number to be invalid */
1032 max_days = g_date_get_days_in_month (m,y);
1034 error_code = HILDON_DATE_TIME_ERROR_MIN_DAY;
1037 else if (d > max_days) {
1039 error_code = HILDON_DATE_TIME_ERROR_MAX_DAY;
1042 else { /* the date does not exist (is invalid) */
1043 error_code = HILDON_DATE_TIME_ERROR_INVALID_DATE;
1044 /* check what was changed and restore previous value */
1045 if (widget == priv->y_entry)
1047 else if (widget == priv->m_entry)
1054 if (error_code != HILDON_DATE_TIME_ERROR_NO_ERROR)
1056 g_signal_emit (ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1058 g_idle_add ((GSourceFunc) hildon_date_editor_entry_select_all, widget);
1062 /* Fix and reformat the date after error signal is processed.
1063 reformatting can be needed even in a such case that numerical
1064 values of the date components are the same as earlier. */
1065 hildon_date_editor_set_date (ed, y, m, d);
1071 hildon_date_editor_entry_select_all (GtkWidget *widget)
1073 GDK_THREADS_ENTER ();
1075 gtk_editable_select_region (GTK_EDITABLE (widget), 0, -1);
1077 GDK_THREADS_LEAVE ();
1082 /* When entry becomes full, we move the focus to the next field.
1083 If we are on the last field, the whole contents are validated. */
1085 hildon_date_editor_entry_changed (GtkEditable *ed,
1091 HildonDateEditorPrivate *priv;
1092 priv = HILDON_DATE_EDITOR_GET_PRIVATE (HILDON_DATE_EDITOR (data));
1094 g_assert (GTK_IS_ENTRY (ed));
1095 g_assert (HILDON_IS_DATE_EDITOR (data));
1098 entry = GTK_ENTRY (ed);
1100 /* If day entry is full, move to next entry or validate */
1101 if (g_utf8_strlen (gtk_entry_get_text (entry), -1) == gtk_entry_get_max_length (entry))
1103 error_code = hildon_date_editor_entry_validate (GTK_WIDGET (entry), data);
1104 if (error_code == HILDON_DATE_TIME_ERROR_NO_ERROR)
1106 priv->skip_validation = TRUE;
1107 gtk_widget_child_focus (GTK_WIDGET (data), GTK_DIR_RIGHT);
1110 priv->skip_validation = FALSE;
1115 hildon_date_editor_keyrelease (GtkWidget *widget,
1119 HildonDateEditor *ed;
1120 HildonDateEditorPrivate *priv;
1122 g_return_val_if_fail (data, FALSE);
1123 g_return_val_if_fail (widget, FALSE);
1125 ed = HILDON_DATE_EDITOR (data);
1126 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1128 if (event->keyval == GDK_KP_Enter ||
1129 event->keyval == GDK_Return ||
1130 event->keyval == GDK_ISO_Enter) {
1131 if (hildon_date_editor_set_calendar_icon_state (ed, FALSE))
1133 popup_calendar_dialog (ed);
1136 } else if (event->keyval == GDK_Escape)
1137 priv->skip_validation = FALSE;
1142 /* keyboard handling */
1144 hildon_date_editor_keypress (GtkWidget *widget,
1148 HildonDateEditor *ed;
1149 HildonDateEditorPrivate *priv;
1151 g_assert (HILDON_IS_DATE_EDITOR (data));
1152 g_assert (GTK_IS_ENTRY (widget));
1154 ed = HILDON_DATE_EDITOR (data);
1156 switch (event->keyval) {
1159 /* Ignore return value, since we want to handle event at all times.
1160 otherwise vkb would popup when the keyrepeat starts. */
1161 hildon_date_editor_set_calendar_icon_state (ed, TRUE);
1164 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1165 priv->skip_validation = TRUE;
1175 hildon_date_editor_entry_focus_out (GtkWidget *widget,
1176 GdkEventFocus *event,
1179 HildonDateEditor *ed;
1180 HildonDateEditorPrivate *priv;
1182 g_assert (HILDON_IS_DATE_EDITOR (data));
1184 ed = HILDON_DATE_EDITOR (data);
1185 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1188 hildon_date_editor_entry_validate (widget, data);
1189 priv->skip_validation = FALSE;
1195 hildon_date_editor_date_error (HildonDateEditor *editor,
1196 HildonDateTimeError type)
1198 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1203 case HILDON_DATE_TIME_ERROR_MAX_DAY:
1204 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 31);
1207 case HILDON_DATE_TIME_ERROR_MAX_MONTH:
1208 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), 12);
1211 case HILDON_DATE_TIME_ERROR_MAX_YEAR:
1212 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_maximum_value"), priv->max_year);
1215 case HILDON_DATE_TIME_ERROR_MIN_DAY:
1216 case HILDON_DATE_TIME_ERROR_MIN_MONTH:
1217 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), 1);
1220 case HILDON_DATE_TIME_ERROR_MIN_YEAR:
1221 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_minimum_value"), priv->min_year);
1224 case HILDON_DATE_TIME_ERROR_EMPTY_DAY:
1225 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1228 case HILDON_DATE_TIME_ERROR_EMPTY_MONTH:
1229 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1232 case HILDON_DATE_TIME_ERROR_EMPTY_YEAR:
1233 hildon_banner_show_informationf (GTK_WIDGET (editor), NULL, _("ckct_ib_set_a_value_within_range"),
1234 priv->min_year, priv->max_year);
1237 case HILDON_DATE_TIME_ERROR_INVALID_CHAR:
1238 hildon_banner_show_information (GTK_WIDGET (editor), NULL, c_("ckct_ib_illegal_character"));
1241 case HILDON_DATE_TIME_ERROR_INVALID_DATE:
1242 hildon_banner_show_information (GTK_WIDGET (editor), NULL, _("ckct_ib_date_does_not_exist"));
1246 /*default error message ?*/
1254 hildon_date_editor_size_request (GtkWidget *widget,
1255 GtkRequisition *requisition)
1257 HildonDateEditor *ed;
1258 HildonDateEditorPrivate *priv;
1259 GtkRequisition f_req, img_req;
1261 g_assert (GTK_IS_WIDGET (widget));
1262 g_assert (requisition != NULL);
1264 ed = HILDON_DATE_EDITOR (widget);
1265 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1268 /* Our own children affect our size */
1269 gtk_widget_size_request (priv->frame, &f_req);
1270 gtk_widget_size_request (priv->d_button_image, &img_req);
1272 /* calculate our size */
1273 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1275 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1276 doing so would break current pixel specifications, since
1277 the text entry by itself is already 30px tall + then frame takes
1279 requisition->height = DATE_EDITOR_HEIGHT;
1283 hildon_date_editor_size_allocate (GtkWidget *widget,
1284 GtkAllocation *allocation)
1286 HildonDateEditor *ed;
1287 HildonDateEditorPrivate *priv;
1288 GtkAllocation f_alloc, img_alloc;
1290 GtkRequisition max_req;
1294 g_assert (GTK_IS_WIDGET (widget));
1295 g_assert (allocation != NULL);
1297 ed = HILDON_DATE_EDITOR (widget);
1298 priv = HILDON_DATE_EDITOR_GET_PRIVATE (ed);
1300 rtl = (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL);
1301 widget->allocation = *allocation;
1303 gtk_widget_get_child_requisition (widget, &max_req);
1305 /* Center vertically */
1306 f_alloc.y = img_alloc.y = allocation->y +
1307 MAX (allocation->height - max_req.height, 0) / 2;
1309 /* Center horizontally */
1310 f_alloc.x = img_alloc.x = allocation->x +
1311 MAX (allocation->width - max_req.width, 0) / 2;
1313 /* calculate allocations */
1314 if (GTK_WIDGET_VISIBLE (widget)) {
1315 /* allocate frame */
1316 gtk_widget_get_child_requisition (priv->frame, &req);
1318 f_alloc.width = req.width;
1319 f_alloc.height = max_req.height;
1322 gtk_widget_get_child_requisition (priv->d_button_image,
1325 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1326 img_alloc.width = req.width;
1327 img_alloc.height = max_req.height;
1331 img_alloc.x = f_alloc.x;
1332 f_alloc.x += img_alloc.width + HILDON_MARGIN_DEFAULT;
1335 if (GTK_WIDGET_VISIBLE (priv->d_button_image)) {
1336 gtk_widget_size_allocate (priv->d_button_image, &img_alloc);
1339 if (GTK_WIDGET_VISIBLE (priv->frame)) {
1340 gtk_widget_size_allocate (priv->frame, &f_alloc);
1344 /* FIXME: We really should not alloc delimeters by hand (since they
1345 are not our own children, but we need to force to appear
1346 higher. This ugly hack is needed to compensate the forced
1347 height in size_request. */
1348 for (iter = priv->delims; iter; iter = iter->next)
1351 GtkAllocation alloc;
1353 delim = GTK_WIDGET (iter->data);
1354 alloc = delim->allocation;
1355 alloc.height = max_req.height;
1356 alloc.y = priv->d_entry->allocation.y - 2;
1358 gtk_widget_size_allocate (delim, &alloc);
1363 hildon_date_editor_focus (GtkWidget *widget,
1364 GtkDirectionType direction)
1367 GtkDirectionType effective_direction;
1369 g_assert (HILDON_IS_DATE_EDITOR (widget));
1371 retval = hildon_private_composite_focus (widget, direction, &effective_direction);
1374 return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
1380 * hildon_date_editor_set_year:
1381 * @editor: the @HildonDateEditor widget
1384 * Sets the year shown in the editor.
1386 * Returns: TRUE if the year is valid and has been set.
1389 hildon_date_editor_set_year (HildonDateEditor *editor,
1392 HildonDateEditorPrivate *priv;
1393 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1395 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1398 if (g_date_valid_dmy (priv->day, priv->month, year))
1403 g_snprintf (buffer, sizeof (buffer), "%04d", year);
1405 /* We apply the new day, but do not want automatic focus move
1406 etc to take place */
1407 g_signal_handlers_block_by_func (priv->y_entry,
1408 (gpointer) hildon_date_editor_entry_changed, editor);
1409 gtk_entry_set_text (GTK_ENTRY (priv->y_entry), buffer);
1410 g_signal_handlers_unblock_by_func (priv->y_entry,
1411 (gpointer) hildon_date_editor_entry_changed, editor);
1413 g_object_notify (G_OBJECT(editor), "year");
1421 * hildon_date_editor_set_month:
1422 * @editor: the @HildonDateEditor widget
1425 * Sets the month shown in the editor.
1427 * Returns: TRUE if the month is valid and has been set.
1430 hildon_date_editor_set_month (HildonDateEditor *editor,
1433 HildonDateEditorPrivate *priv;
1434 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1435 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1438 if (g_date_valid_dmy (priv->day, month, priv->year))
1443 priv->month = month;
1444 g_date_set_dmy (&date, priv->day, month, priv->year);
1445 g_date_strftime (buffer, sizeof(buffer), "%m", &date);
1447 /* We apply the new day, but do not want automatic focus move
1448 etc to take place */
1449 g_signal_handlers_block_by_func (priv->m_entry,
1450 (gpointer) hildon_date_editor_entry_changed, editor);
1451 gtk_entry_set_text (GTK_ENTRY (priv->m_entry), buffer);
1452 g_signal_handlers_unblock_by_func (priv->m_entry,
1453 (gpointer) hildon_date_editor_entry_changed, editor);
1455 g_object_notify (G_OBJECT (editor), "month");
1462 * hildon_date_editor_set_day:
1463 * @editor: the @HildonDateEditor widget
1466 * Sets the day shown in the editor.
1468 * Returns: TRUE if the day is valid and has been set.
1471 hildon_date_editor_set_day (HildonDateEditor *editor,
1474 HildonDateEditorPrivate *priv;
1476 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), FALSE);
1477 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1480 if (g_date_valid_dmy (day, priv->month, priv->year))
1486 g_date_set_dmy (&date, day, priv->month, priv->year);
1487 g_date_strftime (buffer, sizeof (buffer), "%d", &date);
1489 /* We apply the new day, but do not want automatic focus move
1490 etc to take place */
1491 g_signal_handlers_block_by_func (priv->d_entry,
1492 (gpointer) hildon_date_editor_entry_changed, editor);
1493 gtk_entry_set_text (GTK_ENTRY (priv->d_entry), buffer);
1494 g_signal_handlers_unblock_by_func (priv->d_entry,
1495 (gpointer) hildon_date_editor_entry_changed, editor);
1497 g_object_notify (G_OBJECT(editor), "day");
1504 * hildon_date_editor_get_year:
1505 * @editor: the @HildonDateEditor widget
1507 * Returns: the current year shown in the editor.
1510 hildon_date_editor_get_year (HildonDateEditor *editor)
1512 HildonDateEditorPrivate *priv;
1513 g_return_val_if_fail (HILDON_IS_DATE_EDITOR(editor), 0);
1515 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1518 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->y_entry)));
1522 * hildon_date_editor_get_month:
1523 * @editor: the @HildonDateEditor widget
1525 * Gets the month shown in the editor.
1527 * Returns: the current month shown in the editor.
1530 hildon_date_editor_get_month (HildonDateEditor *editor)
1532 HildonDateEditorPrivate *priv;
1533 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1535 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1538 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->m_entry)));
1542 * hildon_date_editor_get_day:
1543 * @editor: the @HildonDateEditor widget
1545 * Gets the day shown in the editor.
1547 * Returns: the current day shown in the editor
1550 hildon_date_editor_get_day (HildonDateEditor *editor)
1552 HildonDateEditorPrivate *priv;
1553 g_return_val_if_fail (HILDON_IS_DATE_EDITOR (editor), 0);
1555 priv = HILDON_DATE_EDITOR_GET_PRIVATE (editor);
1558 return (guint) atoi (gtk_entry_get_text (GTK_ENTRY (priv->d_entry)));