2 * This file is part of hildon-libs
4 * Copyright (C) 2005 Nokia Corporation.
6 * Contact: Luc Pionchon <luc.pionchon@nokia.com>
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
26 * SECTION:hildon-date-editor
27 * @short_description: A widget which queries a date from user or opens
28 * a HildonCalendarPopup
29 * @see_also: #HildonCalendarPopup, #HildonTimeEditor
31 * HildonDateEditor is a widget with three entry fields (day, month,
32 * year) and an icon (eventbox): clicking on the icon opens up a
33 * HildonCalendarPopup.
38 #include <gdk/gdkkeysyms.h>
44 #include <hildon-widgets/hildon-date-editor.h>
45 #include <hildon-widgets/hildon-calendar-popup.h>
46 #include <hildon-widgets/gtk-infoprint.h>
47 #include <hildon-widgets/hildon-defines.h>
48 #include <hildon-widgets/hildon-input-mode-hint.h>
49 #include "hildon-composite-widget.h"
50 #include "hildon-marshalers.h"
57 #define _(string) dgettext(PACKAGE, string)
59 #define ENTRY_BORDERS 11
60 #define DATE_EDITOR_HEIGHT 30
62 #define DAY_ENTRY_WIDTH 2
63 #define MONTH_ENTRY_WIDTH 2
64 #define YEAR_ENTRY_WIDTH 4
66 #define DEFAULT_MIN_YEAR 1970
67 #define DEFAULT_MAX_YEAR 2037
69 #define HILDON_DATE_EDITOR_GET_PRIVATE(obj) \
70 (G_TYPE_INSTANCE_GET_PRIVATE((obj),\
71 HILDON_DATE_EDITOR_TYPE, HildonDateEditorPrivate));
73 static GtkContainerClass *parent_class;
75 typedef struct _HildonDateEditorPrivate HildonDateEditorPrivate;
78 hildon_date_editor_class_init(HildonDateEditorClass * editor_class);
80 static void hildon_date_editor_init(HildonDateEditor * editor);
83 hildon_date_editor_icon_press(GtkWidget * widget, GdkEventButton * event,
87 hildon_date_editor_entry_released(GtkWidget * widget,
88 GdkEventButton * event, gpointer data);
90 hildon_date_editor_released(GtkWidget * widget, GdkEventButton * event,
94 hildon_date_editor_keypress(GtkWidget * widget, GdkEventKey * event,
98 hildon_date_editor_keyrelease(GtkWidget * widget, GdkEventKey * event,
101 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data);
104 hildon_date_editor_entry_changed(GtkEditable *widget, gpointer data);
107 hildon_date_editor_entry_focus_out(GtkWidget * widget, GdkEventFocus * event,
110 static gboolean hildon_date_editor_date_error(HildonDateEditor *editor,
111 HildonDateEditorErrorType type);
113 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
114 GdkEventFocus * event,
116 static void hildon_date_editor_get_property( GObject *object, guint param_id,
117 GValue *value, GParamSpec *pspec );
118 static void hildon_date_editor_set_property (GObject *object, guint param_id,
119 const GValue *value, GParamSpec *pspec);
121 hildon_child_forall(GtkContainer * container,
122 gboolean include_internals,
123 GtkCallback callback, gpointer callback_data);
125 static void hildon_date_editor_destroy(GtkObject * self);
128 hildon_date_editor_size_allocate(GtkWidget * widget,
129 GtkAllocation * allocation);
132 hildon_date_editor_size_request(GtkWidget * widget,
133 GtkRequisition * requisition);
136 _hildon_date_editor_entry_select_all(GtkWidget *widget);
138 /* Property indices */
148 struct _HildonDateEditorPrivate {
149 /* Cache of values in the entries, used in setting only parts of the date */
150 guint year; /* current year in the entry */
151 guint month; /* current month in the entry */
152 guint day; /* current day in the entry */
154 gboolean calendar_icon_pressed;
156 GtkWidget *frame; /* borders around the date */
157 GtkWidget *d_event_box_image; /* icon */
158 GtkWidget *d_box_date; /* hbox for date */
160 GtkWidget *d_entry; /* GtkEntry for day */
161 GtkWidget *m_entry; /* GtkEntry for month */
162 GtkWidget *y_entry; /* GtkEntry for year */
164 GList *delims; /* List of delimeters between the fields (and possible at the ends) */
165 GtkWidget *calendar_icon;
167 gboolean skip_validation; /* don't validate date at all */
169 gint min_year; /* minimum year allowed */
170 gint max_year; /* maximum year allowed */
178 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
180 GType hildon_date_editor_get_type(void)
182 static GType editor_type = 0;
185 static const GTypeInfo editor_info = {
186 sizeof(HildonDateEditorClass),
187 NULL, /* base_init */
188 NULL, /* base_finalize */
189 (GClassInitFunc) hildon_date_editor_class_init,
190 NULL, /* class_finalize */
191 NULL, /* class_data */
192 sizeof(HildonDateEditor),
194 (GInstanceInitFunc) hildon_date_editor_init,
196 editor_type = g_type_register_static(GTK_TYPE_CONTAINER,
204 hildon_date_editor_class_init(HildonDateEditorClass * editor_class)
206 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(editor_class);
207 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(editor_class);
208 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
210 parent_class = g_type_class_peek_parent(editor_class);
212 g_type_class_add_private(editor_class,
213 sizeof(HildonDateEditorPrivate));
215 gobject_class->set_property = hildon_date_editor_set_property;
216 gobject_class->get_property = hildon_date_editor_get_property;
217 widget_class->size_request = hildon_date_editor_size_request;
218 widget_class->size_allocate = hildon_date_editor_size_allocate;
219 widget_class->focus = hildon_composite_widget_focus;
221 container_class->forall = hildon_child_forall;
222 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
224 editor_class->date_error = hildon_date_editor_date_error;
226 date_editor_signals[DATE_ERROR] =
227 g_signal_new("date-error",
228 G_OBJECT_CLASS_TYPE(gobject_class),
230 G_STRUCT_OFFSET(HildonDateEditorClass, date_error),
231 g_signal_accumulator_true_handled, NULL,
232 _hildon_marshal_BOOLEAN__INT,
233 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
236 * HildonDateEditor:year:
240 g_object_class_install_property( gobject_class, PROP_YEAR,
241 g_param_spec_uint("year",
246 G_PARAM_READABLE | G_PARAM_WRITABLE) );
249 * HildonDateEditor:month:
253 g_object_class_install_property( gobject_class, PROP_MONTH,
254 g_param_spec_uint("month",
259 G_PARAM_READABLE | G_PARAM_WRITABLE) );
262 * HildonDateEditor:day:
266 g_object_class_install_property( gobject_class, PROP_DAY,
267 g_param_spec_uint("day",
272 G_PARAM_READABLE | G_PARAM_WRITABLE) );
275 * HildonDateEditor:min-year:
277 * Minimum valid year.
279 g_object_class_install_property( gobject_class, PROP_MIN_YEAR,
280 g_param_spec_uint("min-year",
281 "Minimum valid year",
282 "Minimum valid year",
285 G_PARAM_READWRITE) );
288 * HildonDateEditor:max-year:
290 * Maximum valid year.
292 g_object_class_install_property( gobject_class, PROP_MAX_YEAR,
293 g_param_spec_uint("max-year",
294 "Maximum valid year",
295 "Maximum valid year",
298 G_PARAM_READWRITE) );
301 /* Forces setting of the icon to certain state. Used initially
302 and from the actual setter function */
304 real_set_calendar_icon_state(HildonDateEditorPrivate *priv,
307 gtk_image_set_from_icon_name(GTK_IMAGE(priv->calendar_icon),
308 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit",
309 HILDON_ICON_SIZE_WIDG);
311 priv->calendar_icon_pressed = pressed;
314 /* Sets the icon to given state (normal/pressed). Returns
315 info if the state actually changed. */
317 hildon_date_editor_set_calendar_icon_state(HildonDateEditor *editor,
320 HildonDateEditorPrivate *priv;
322 g_assert(HILDON_IS_DATE_EDITOR(editor));
324 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
325 if (pressed != priv->calendar_icon_pressed) {
326 real_set_calendar_icon_state(priv, pressed);
333 /* Packing day, month and year entries depend on locale settings
334 We find out the order and all separators by converting a known
335 date to default format and inspecting the result string */
336 static void apply_locale_field_order(HildonDateEditorPrivate *priv)
338 GDate locale_test_date;
341 gchar *iter, *delim_text;
343 g_date_set_dmy(&locale_test_date, 1, 2, 1970);
344 (void) g_date_strftime(buffer, sizeof(buffer), "%x", &locale_test_date);
352 /* Try to convert the current location into number. */
353 value = strtoul(iter, &endp, 10);
355 /* If the conversion didn't progress or the detected value was
356 unknown (we used a fixed date, you remember), we treat
357 current position as a literal */
361 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
362 priv->d_entry, FALSE, FALSE, 0);
365 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
366 priv->m_entry, FALSE, FALSE, 0);
368 case 70: /* %x format uses only 2 numbers for some locales */
370 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
371 priv->y_entry, FALSE, FALSE, 0);
374 /* All non-number characters starting from current position
375 form the delimeter */
376 for (endp = iter; *endp; endp++)
377 if (g_ascii_isdigit(*endp))
380 /* Now endp points one place past the delimeter text */
381 delim_text = g_strndup(iter, endp - iter);
382 delim = gtk_label_new(delim_text);
383 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
384 delim, FALSE, FALSE, 0);
385 priv->delims = g_list_append(priv->delims, delim);
395 static void hildon_date_editor_init(HildonDateEditor * editor)
397 HildonDateEditorPrivate *priv;
400 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
402 GTK_WIDGET_SET_FLAGS(GTK_WIDGET(editor), GTK_NO_WINDOW);
404 gtk_widget_push_composite_child();
406 /* initialize values */
407 g_date_clear(&cur_date, 1);
408 g_date_set_time(&cur_date, time(NULL));
410 priv->day = g_date_get_day(&cur_date);
411 priv->month = g_date_get_month(&cur_date);
412 priv->year = g_date_get_year(&cur_date);
413 priv->min_year = DEFAULT_MIN_YEAR;
414 priv->max_year = DEFAULT_MAX_YEAR;
417 priv->frame = gtk_frame_new(NULL);
418 gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
420 priv->d_entry = gtk_entry_new();
421 priv->m_entry = gtk_entry_new();
422 priv->y_entry = gtk_entry_new();
424 g_object_set (G_OBJECT(priv->d_entry), "input-mode",
425 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
426 g_object_set (G_OBJECT(priv->m_entry), "input-mode",
427 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
428 g_object_set (G_OBJECT(priv->y_entry), "input-mode",
429 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
432 gtk_entry_set_width_chars(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
433 gtk_entry_set_width_chars(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
434 gtk_entry_set_width_chars(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
436 gtk_entry_set_max_length(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
437 gtk_entry_set_max_length(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
438 gtk_entry_set_max_length(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
440 gtk_entry_set_has_frame(GTK_ENTRY(priv->d_entry), FALSE);
441 gtk_entry_set_has_frame(GTK_ENTRY(priv->m_entry), FALSE);
442 gtk_entry_set_has_frame(GTK_ENTRY(priv->y_entry), FALSE);
444 gtk_widget_set_composite_name(priv->d_entry, "day_entry");
445 gtk_widget_set_composite_name(priv->m_entry, "month_entry");
446 gtk_widget_set_composite_name(priv->y_entry, "year_entry");
448 priv->d_box_date = gtk_hbox_new(FALSE, 0);
450 priv->d_event_box_image = gtk_event_box_new();
451 priv->calendar_icon = gtk_image_new();
452 real_set_calendar_icon_state(priv, FALSE);
454 apply_locale_field_order(priv);
456 gtk_container_add(GTK_CONTAINER(priv->frame), priv->d_box_date);
457 gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->calendar_icon);
459 gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
460 gtk_widget_set_parent(priv->d_event_box_image, GTK_WIDGET(editor));
461 gtk_widget_show_all(priv->frame);
462 gtk_widget_show_all(priv->d_event_box_image);
464 /* image signal connects */
465 g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "button_press_event",
466 G_CALLBACK(hildon_date_editor_icon_press), editor);
467 g_signal_connect(GTK_OBJECT(priv->d_event_box_image),
468 "button_release_event",
469 G_CALLBACK(hildon_date_editor_released), editor);
470 g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "key-press-event",
471 G_CALLBACK(hildon_date_editor_keypress), editor);
472 g_signal_connect(GTK_OBJECT(priv->calendar_icon), "key-press-event",
473 G_CALLBACK(hildon_date_editor_keypress), editor);
476 /* entry signal connects */
477 g_signal_connect(GTK_OBJECT(priv->d_entry), "button_release_event",
478 G_CALLBACK(hildon_date_editor_entry_released), editor);
480 g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-in-event",
481 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
483 g_signal_connect(GTK_OBJECT(priv->m_entry), "button_release_event",
484 G_CALLBACK(hildon_date_editor_entry_released), editor);
486 g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-in-event",
487 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
489 g_signal_connect(GTK_OBJECT(priv->y_entry), "button_release_event",
490 G_CALLBACK(hildon_date_editor_entry_released), editor);
492 g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-in-event",
493 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
495 g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-out-event",
496 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
498 g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-out-event",
499 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
501 g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-out-event",
502 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
504 g_signal_connect(GTK_OBJECT(priv->d_entry), "key-press-event",
505 G_CALLBACK(hildon_date_editor_keypress), editor);
507 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-press-event",
508 G_CALLBACK(hildon_date_editor_keypress), editor);
510 g_signal_connect(GTK_OBJECT(priv->y_entry), "key-press-event",
511 G_CALLBACK(hildon_date_editor_keypress), editor);
513 g_signal_connect(GTK_OBJECT(priv->d_entry), "key-release-event",
514 G_CALLBACK(hildon_date_editor_keyrelease), editor);
516 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
517 G_CALLBACK(hildon_date_editor_keyrelease), editor);
519 g_signal_connect(GTK_OBJECT(priv->y_entry), "key-release-event",
520 G_CALLBACK(hildon_date_editor_keyrelease), editor);
522 hildon_date_editor_set_date(editor, priv->year, priv->month, priv->day);
524 g_signal_connect(GTK_OBJECT(priv->d_entry), "changed",
525 G_CALLBACK(hildon_date_editor_entry_changed), editor);
527 g_signal_connect(GTK_OBJECT(priv->m_entry), "changed",
528 G_CALLBACK(hildon_date_editor_entry_changed), editor);
530 g_signal_connect(GTK_OBJECT(priv->y_entry), "changed",
531 G_CALLBACK(hildon_date_editor_entry_changed), editor);
533 gtk_widget_pop_composite_child();
536 static void hildon_date_editor_set_property (GObject *object, guint param_id,
537 const GValue *value, GParamSpec *pspec)
539 HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
540 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
546 hildon_date_editor_set_year (editor, g_value_get_uint(value));
550 hildon_date_editor_set_month (editor, g_value_get_uint(value));
554 hildon_date_editor_set_day (editor, g_value_get_uint(value));
558 val = g_value_get_uint(value);
559 if (val <= priv->max_year)
561 priv->min_year = val;
562 /* Clamp current year */
563 if (hildon_date_editor_get_year (editor) < priv->min_year)
564 hildon_date_editor_set_year (editor, priv->min_year);
567 g_warning("min-year cannot be greater than max-year");
571 val = g_value_get_uint(value);
572 if (val >= priv->min_year)
574 priv->max_year = val;
575 /* Clamp current year */
576 if (hildon_date_editor_get_year (editor) > priv->max_year)
577 hildon_date_editor_set_year (editor, priv->max_year);
580 g_warning("max-year cannot be less than min-year");
584 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
589 static void hildon_date_editor_get_property( GObject *object, guint param_id,
590 GValue *value, GParamSpec *pspec )
592 HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
593 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
598 g_value_set_uint (value, hildon_date_editor_get_year (editor));
602 g_value_set_uint (value, hildon_date_editor_get_month (editor));
606 g_value_set_uint (value, hildon_date_editor_get_day (editor));
610 g_value_set_uint (value, priv->min_year);
614 g_value_set_uint (value, priv->max_year);
618 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
623 static void hildon_child_forall(GtkContainer * container,
624 gboolean include_internals,
625 GtkCallback callback,
626 gpointer callback_data)
628 HildonDateEditor *editor;
629 HildonDateEditorPrivate *priv;
631 g_assert(HILDON_IS_DATE_EDITOR(container));
634 editor = HILDON_DATE_EDITOR(container);
635 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
637 if (include_internals) {
638 (*callback) (priv->frame, callback_data);
639 (*callback) (priv->d_event_box_image, callback_data);
643 static void hildon_date_editor_destroy(GtkObject * self)
645 HildonDateEditorPrivate *priv;
647 priv = HILDON_DATE_EDITOR_GET_PRIVATE(self);
650 gtk_widget_unparent(priv->frame);
653 if (priv->d_event_box_image) {
654 gtk_widget_unparent(priv->d_event_box_image);
655 priv->d_event_box_image = NULL;
658 g_list_free(priv->delims);
662 if (GTK_OBJECT_CLASS(parent_class)->destroy)
663 GTK_OBJECT_CLASS(parent_class)->destroy(self);
667 * hildon_date_editor_new:
669 * Creates a new date editor. The current system date
670 * is shown in the editor.
672 * Returns: pointer to a new @HildonDateEditor widget.
674 GtkWidget *hildon_date_editor_new(void)
676 return GTK_WIDGET(g_object_new(HILDON_DATE_EDITOR_TYPE, NULL));
680 * hildon_date_editor_set_date:
681 * @date: the @HildonDateEditor widget
686 * Sets the date shown in the editor.
688 void hildon_date_editor_set_date(HildonDateEditor * editor,
689 guint year, guint month, guint day)
691 HildonDateEditorPrivate *priv;
693 g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
695 /* This function cannot be implemented by calling
696 component setters, since applying the individual
697 values one by one can make the date temporarily
698 invalid (depending on what the previous values were),
699 which in turn causes that the desired date
700 is not set (even though it's valid). We must set all the
701 components at one go and not try to do any validation etc
704 g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
705 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
707 if (g_date_valid_dmy(day, month, year))
716 g_date_set_dmy(&date, day, month, year);
718 /* We apply the new values, but do not want automatic focus move
720 g_snprintf(buffer, sizeof(buffer), "%04d", year);
721 g_signal_handlers_block_by_func(priv->y_entry,
722 (gpointer) hildon_date_editor_entry_changed, editor);
723 gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
724 g_signal_handlers_unblock_by_func(priv->y_entry,
725 (gpointer) hildon_date_editor_entry_changed, editor);
727 g_date_strftime(buffer, sizeof(buffer), "%m", &date);
728 g_signal_handlers_block_by_func(priv->m_entry,
729 (gpointer) hildon_date_editor_entry_changed, editor);
730 gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
731 g_signal_handlers_unblock_by_func(priv->m_entry,
732 (gpointer) hildon_date_editor_entry_changed, editor);
734 g_date_strftime(buffer, sizeof(buffer), "%d", &date);
735 g_signal_handlers_block_by_func(priv->d_entry,
736 (gpointer) hildon_date_editor_entry_changed, editor);
737 gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
738 g_signal_handlers_unblock_by_func(priv->d_entry,
739 (gpointer) hildon_date_editor_entry_changed, editor);
741 g_object_notify(G_OBJECT(editor), "year");
742 g_object_notify(G_OBJECT(editor), "month");
743 g_object_notify(G_OBJECT(editor), "day");
748 * hildon_date_editor_get_date:
749 * @date: the @HildonDateEditor widget
754 * Returns: the year, month, and day currently on the
757 void hildon_date_editor_get_date(HildonDateEditor * date,
758 guint * year, guint * month, guint * day)
760 HildonDateEditorPrivate *priv;
762 g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
763 g_return_if_fail(year);
764 g_return_if_fail(month);
765 g_return_if_fail(day);
767 priv = HILDON_DATE_EDITOR_GET_PRIVATE(date);
769 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
770 is unclear. They do not neccesarily match and still the texts are
771 used as return values and members for some internal validation!!
772 At least a partly reason is to allow empty text to become
773 0 return value, while members are restricted to valid ranges?!
774 However, if we change the current way, we are likely to break
775 some applications if they rely on some specific way how this
776 widget currently handles empty values and temporarily invalid values.
778 The key issue is this: What should the _get methods return while
779 user is editing a field and the result is incomplete. The
780 partial result? The last good result? If we return partial result
781 we also need a way to inform if the date is not valid. Current
782 implementation is some kind of hybrid of these two...
785 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
787 easily fails, since set_day tries to force validity while get_day
790 Proposal: Always return the same values that are shown in the
791 fields. We add a separate flag (Or use GDate) to
792 indicate if the current date is valid. This would allow
793 setters to make the date invalid as well.
795 *year = /*priv->year;*/
796 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
797 *month = /*priv->month;*/
798 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
799 *day = /*priv->day;*/
800 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
803 static gboolean hildon_date_editor_icon_press(GtkWidget * widget,
804 GdkEventButton * event,
807 g_assert(GTK_IS_WIDGET(widget));
808 g_assert(event != NULL);
809 g_assert(HILDON_IS_DATE_EDITOR(data));
811 if (event->button == 1)
812 hildon_date_editor_set_calendar_icon_state(HILDON_DATE_EDITOR(data), TRUE);
817 static gboolean hildon_date_editor_entry_released(GtkWidget * widget,
818 GdkEventButton * event,
821 HildonDateEditor *ed;
822 HildonDateEditorPrivate *priv;
824 ed = HILDON_DATE_EDITOR(data);
825 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
827 if (event->button == 1) {
828 /* We might not get focus because of invalid values in entries */
829 if (GTK_WIDGET_HAS_FOCUS(widget))
830 g_idle_add((GSourceFunc)
831 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
837 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
838 GdkEventFocus * event,
841 if (!GTK_ENTRY(widget)->button)
843 g_idle_add((GSourceFunc)
844 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
851 static void popup_calendar_dialog(HildonDateEditor *ed)
853 guint y = 0, m = 0, d = 0;
859 hildon_date_editor_get_date(ed, &y, &m, &d);
861 parent = gtk_widget_get_ancestor(GTK_WIDGET(ed), GTK_TYPE_WINDOW);
862 popup = hildon_calendar_popup_new(GTK_WINDOW(parent), y, m, d);
864 g_value_init(&val, G_TYPE_INT);
865 /* Set max/min year in calendar popup to date editor values */
866 g_object_get_property(G_OBJECT(ed), "min-year", &val);
867 g_object_set_property(G_OBJECT(popup), "min-year", &val);
868 g_object_get_property(G_OBJECT(ed), "max-year", &val);
869 g_object_set_property(G_OBJECT(popup), "max-year", &val);
871 /* Pop up calendar */
872 result = gtk_dialog_run(GTK_DIALOG(popup));
874 case GTK_RESPONSE_OK:
875 case GTK_RESPONSE_ACCEPT:
876 hildon_calendar_popup_get_date(HILDON_CALENDAR_POPUP(popup), &y,
878 hildon_date_editor_set_date(ed, y, m, d);
881 gtk_widget_destroy(popup);
884 /* button released */
885 static gboolean hildon_date_editor_released(GtkWidget * widget,
886 GdkEventButton * event,
889 HildonDateEditor *ed;
891 g_assert(GTK_IS_WIDGET(widget));
892 g_assert(event != NULL);
893 g_assert(HILDON_IS_DATE_EDITOR(data));
895 ed = HILDON_DATE_EDITOR(data);
897 if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
898 popup_calendar_dialog(ed);
903 /* This is called whenever some editor filed loses the focus and
904 when the all of the fields are filled.
905 Earlier this was called whenever an entry changed */
906 /* FIXME: Validation on focus_out is broken by concept */
908 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data)
910 HildonDateEditor *ed;
911 HildonDateEditorPrivate *priv;
912 gint d, m, y, max_days;
913 gboolean r; /* temp return values for signals */
915 gint error_code = NO_ERROR;
917 g_assert(HILDON_IS_DATE_EDITOR(data));
918 g_assert(GTK_IS_ENTRY(widget));
920 ed = HILDON_DATE_EDITOR(data);
921 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
923 if (priv->skip_validation)
926 /*check if the calling entry is empty*/
927 text = gtk_entry_get_text(GTK_ENTRY(widget));
928 if(text == NULL || text[0] == 0)
930 if (widget == priv->d_entry)
931 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_DAY, &r);
932 else if(widget == priv->m_entry)
933 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_MONTH, &r);
935 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_YEAR, &r);
940 /* Ok, we now check validity. Some fields can be empty */
941 text = gtk_entry_get_text(GTK_ENTRY(priv->d_entry));
942 if (text == NULL || text[0] == 0) return;
944 text = gtk_entry_get_text(GTK_ENTRY(priv->m_entry));
945 if (text == NULL || text[0] == 0) return;
947 text = gtk_entry_get_text(GTK_ENTRY(priv->y_entry));
948 if (text == NULL || text[0] == 0) return;
951 /* Did it actually change? */
952 if (d != priv->day || m != priv->month || y != priv->year)
954 /* We could/should use hildon_date_editor_set_year and such functions
955 * to set the date, instead of use gtk_entry_set_text, and then change
956 * the priv member but hildon_date_editor_set_year and such functions
957 * check if the date is valid, we do want to do date validation check
958 * here according to spec */
961 if(widget == priv->m_entry) {
963 error_code = MIN_MONTH;
967 error_code = MAX_MONTH;
973 if(widget == priv->y_entry) {
974 if (y < priv->min_year) {
975 error_code = MIN_YEAR;
978 else if (y > priv->max_year) {
979 error_code = MAX_YEAR;
984 /* Validate day. We have to do this in every case, since
985 changing month or year can make the day number to be invalid */
986 max_days = g_date_get_days_in_month(m,y);
988 error_code = MIN_DAY;
991 else if (d > max_days) {
993 error_code = MAX_DAY;
996 else { /* the date does not exist (is invalid) */
997 error_code = INVALID_DATE;
998 /* check what was changed and restore previous value */
999 if ( widget == priv->y_entry )
1001 else if ( widget == priv->m_entry )
1008 if (error_code != NO_ERROR)
1010 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1012 g_idle_add ((GSourceFunc)
1013 _hildon_date_editor_entry_select_all,
1016 /* Set focus back to invalid entry */
1017 gtk_widget_grab_focus(widget);
1021 /* Fix and reformat the date after error signal is processed.
1022 reformatting can be needed even in a such case that numerical
1023 values of the date components are the same as earlier. */
1024 hildon_date_editor_set_date(ed, y, m, d);
1027 /* When entry becomes full, we move the focus to the next field.
1028 If we are on the last field, the whole contents are validated. */
1030 hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
1034 g_assert(GTK_IS_ENTRY(ed));
1035 g_assert(HILDON_IS_DATE_EDITOR(data));
1037 entry = GTK_ENTRY(ed);
1039 /* If day entry is full, move to next entry or validate */
1040 if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
1042 if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
1043 hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
1047 static gboolean hildon_date_editor_keyrelease(GtkWidget * widget,
1048 GdkEventKey * event,
1051 HildonDateEditor *ed;
1052 HildonDateEditorPrivate *priv;
1054 g_return_val_if_fail(data, FALSE);
1055 g_return_val_if_fail(widget, FALSE);
1057 ed = HILDON_DATE_EDITOR(data);
1058 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1060 if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return ||
1061 event->keyval == GDK_ISO_Enter) {
1062 if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
1064 popup_calendar_dialog(ed);
1067 } else if (event->keyval == GDK_Escape)
1068 priv->skip_validation = FALSE;
1073 /* keyboard handling */
1074 static gboolean hildon_date_editor_keypress(GtkWidget * widget,
1075 GdkEventKey * event,
1078 HildonDateEditor *ed;
1079 HildonDateEditorPrivate *priv;
1082 g_assert(HILDON_IS_DATE_EDITOR(data));
1083 g_assert(GTK_IS_ENTRY(widget));
1085 ed = HILDON_DATE_EDITOR(data);
1086 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1087 pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1089 switch (event->keyval) {
1091 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
1095 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
1100 /* Ignore return value, since we want to handle event at all times.
1101 otherwise vkb would popup when the keyrepeat starts. */
1102 (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
1106 priv->skip_validation = TRUE;
1115 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
1116 GdkEventFocus * event,
1119 hildon_date_editor_entry_validate(widget, data);
1124 hildon_date_editor_date_error(HildonDateEditor *editor,
1125 HildonDateEditorErrorType type)
1127 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1132 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 31);
1135 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 12);
1138 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), priv->max_year);
1142 gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), 1);
1145 gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), priv->min_year);
1148 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 31);
1151 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 12);
1154 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"),
1155 priv->min_year, priv->max_year);
1158 gtk_infoprint(NULL, _("Ckct_ib_date_does_not_exist"));
1161 /*default error message ?*/
1167 static void hildon_date_editor_size_request(GtkWidget * widget,
1168 GtkRequisition * requisition)
1170 HildonDateEditor *ed;
1171 HildonDateEditorPrivate *priv;
1172 GtkRequisition f_req, img_req;
1174 g_assert(GTK_IS_WIDGET(widget));
1175 g_assert(requisition != NULL);
1177 ed = HILDON_DATE_EDITOR(widget);
1178 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1180 /* Our own children affect our size */
1181 gtk_widget_size_request(priv->frame, &f_req);
1182 gtk_widget_size_request(priv->d_event_box_image, &img_req);
1184 /* calculate our size */
1185 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1187 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1188 doing so would break current pixel specifications, since
1189 the text entry by itself is already 30px tall + then frame takes
1191 requisition->height = DATE_EDITOR_HEIGHT;
1194 static void hildon_date_editor_size_allocate(GtkWidget * widget,
1195 GtkAllocation * allocation)
1197 HildonDateEditor *ed;
1198 HildonDateEditorPrivate *priv;
1199 GtkAllocation f_alloc, img_alloc;
1201 GtkRequisition max_req;
1204 g_assert(GTK_IS_WIDGET(widget));
1205 g_assert(allocation != NULL);
1207 ed = HILDON_DATE_EDITOR(widget);
1208 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1210 widget->allocation = *allocation;
1212 gtk_widget_get_child_requisition(widget, &max_req);
1214 /* Center vertically */
1215 f_alloc.y = img_alloc.y = allocation->y +
1216 MAX(allocation->height - max_req.height, 0) / 2;
1218 /* Center horizontally */
1219 f_alloc.x = img_alloc.x = allocation->x +
1220 MAX(allocation->width - max_req.width, 0) / 2;
1222 /* allocate frame */
1223 if (GTK_WIDGET_VISIBLE(priv->frame)) {
1224 gtk_widget_get_child_requisition(priv->frame, &req);
1226 f_alloc.width = req.width;
1227 f_alloc.height = max_req.height;
1228 gtk_widget_size_allocate(priv->frame, &f_alloc);
1232 if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
1233 gtk_widget_get_child_requisition(priv->d_event_box_image,
1236 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1237 img_alloc.width = req.width;
1238 img_alloc.height = max_req.height;
1239 gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
1242 /* FIXME: We really should not alloc delimeters by hand (since they
1243 are not our own children, but we need to force to appear
1244 higher. This ugly hack is needed to compensate the forced
1245 height in size_request. */
1246 for (iter = priv->delims; iter; iter = iter->next)
1249 GtkAllocation alloc;
1251 delim = GTK_WIDGET(iter->data);
1252 alloc = delim->allocation;
1253 alloc.height = max_req.height;
1254 alloc.y = priv->d_entry->allocation.y - 2;
1256 gtk_widget_size_allocate(delim, &alloc);
1261 * hildon_date_editor_set_year:
1262 * @editor: the @HildonDateEditor widget
1265 * Sets the year shown in the editor.
1267 * Returns: TRUE if the year is valid
1269 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
1271 HildonDateEditorPrivate *priv;
1272 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1273 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1275 if (g_date_valid_dmy(priv->day, priv->month, year))
1280 g_snprintf(buffer, sizeof(buffer), "%04d", year);
1282 /* We apply the new day, but do not want automatic focus move
1283 etc to take place */
1284 g_signal_handlers_block_by_func(priv->y_entry,
1285 (gpointer) hildon_date_editor_entry_changed, editor);
1286 gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
1287 g_signal_handlers_unblock_by_func(priv->y_entry,
1288 (gpointer) hildon_date_editor_entry_changed, editor);
1290 g_object_notify(G_OBJECT(editor), "year");
1298 * hildon_date_editor_set_month:
1299 * @editor: the @HildonDateEditor widget
1302 * Sets the month shown in the editor.
1304 * Returns: TRUE if the month is valid
1306 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
1308 HildonDateEditorPrivate *priv;
1309 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1310 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1312 if (g_date_valid_dmy(priv->day, month, priv->year))
1317 priv->month = month;
1318 g_date_set_dmy(&date, priv->day, month, priv->year);
1319 g_date_strftime(buffer, sizeof(buffer), "%m", &date);
1321 /* We apply the new day, but do not want automatic focus move
1322 etc to take place */
1323 g_signal_handlers_block_by_func(priv->m_entry,
1324 (gpointer) hildon_date_editor_entry_changed, editor);
1325 gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
1326 g_signal_handlers_unblock_by_func(priv->m_entry,
1327 (gpointer) hildon_date_editor_entry_changed, editor);
1329 g_object_notify(G_OBJECT(editor), "month");
1336 * hildon_date_editor_set_day:
1337 * @editor: the @HildonDateEditor widget
1340 * Sets the day shown in the editor.
1342 * Returns: TRUE if the day is valid
1344 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
1346 HildonDateEditorPrivate *priv;
1348 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1349 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1351 if (g_date_valid_dmy(day, priv->month, priv->year))
1357 g_date_set_dmy(&date, day, priv->month, priv->year);
1358 g_date_strftime(buffer, sizeof(buffer), "%d", &date);
1360 /* We apply the new day, but do not want automatic focus move
1361 etc to take place */
1362 g_signal_handlers_block_by_func(priv->d_entry,
1363 (gpointer) hildon_date_editor_entry_changed, editor);
1364 gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
1365 g_signal_handlers_unblock_by_func(priv->d_entry,
1366 (gpointer) hildon_date_editor_entry_changed, editor);
1368 g_object_notify(G_OBJECT(editor), "day");
1375 * hildon_date_editor_get_year:
1376 * @editor: the @HildonDateEditor widget
1378 * Returns: the current year shown in the editor.
1380 guint hildon_date_editor_get_year(HildonDateEditor *editor)
1382 HildonDateEditorPrivate *priv;
1383 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1384 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1385 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
1389 * hildon_date_editor_get_month:
1390 * @editor: the @HildonDateEditor widget
1392 * Gets the month shown in the editor.
1394 * Returns: the current month shown in the editor.
1397 guint hildon_date_editor_get_month(HildonDateEditor *editor)
1399 HildonDateEditorPrivate *priv;
1400 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1401 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1402 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
1406 * hildon_date_editor_get_day:
1407 * @editor: the @HildonDateEditor widget
1409 * Gets the day shown in the editor.
1411 * Returns: the current day shown in the editor
1414 guint hildon_date_editor_get_day(HildonDateEditor *editor)
1416 HildonDateEditorPrivate *priv;
1417 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1418 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1419 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
1424 _hildon_date_editor_entry_select_all (GtkWidget *widget)
1426 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);