2 * This file is part of hildon-libs
4 * Copyright (C) 2005, 2006 Nokia Corporation.
6 * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@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; version 2.1 of
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"
51 #include "hildon-libs-enum-types.h"
58 #define _(string) dgettext(PACKAGE, string)
60 #define ENTRY_BORDERS 11
61 #define DATE_EDITOR_HEIGHT 30
63 #define DAY_ENTRY_WIDTH 2
64 #define MONTH_ENTRY_WIDTH 2
65 #define YEAR_ENTRY_WIDTH 4
67 #define DEFAULT_MIN_YEAR 1970
68 #define DEFAULT_MAX_YEAR 2037
70 #define HILDON_DATE_EDITOR_GET_PRIVATE(obj) \
71 (G_TYPE_INSTANCE_GET_PRIVATE((obj),\
72 HILDON_TYPE_DATE_EDITOR, HildonDateEditorPrivate));
74 static GtkContainerClass *parent_class;
76 typedef struct _HildonDateEditorPrivate HildonDateEditorPrivate;
79 hildon_date_editor_class_init(HildonDateEditorClass * editor_class);
81 static void hildon_date_editor_init(HildonDateEditor * editor);
84 hildon_date_editor_icon_press(GtkWidget * widget, GdkEventButton * event,
88 hildon_date_editor_entry_released(GtkWidget * widget,
89 GdkEventButton * event, gpointer data);
91 hildon_date_editor_released(GtkWidget * widget, GdkEventButton * event,
95 hildon_date_editor_keypress(GtkWidget * widget, GdkEventKey * event,
99 hildon_date_editor_keyrelease(GtkWidget * widget, GdkEventKey * event,
102 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data);
105 hildon_date_editor_entry_changed(GtkEditable *widget, gpointer data);
108 hildon_date_editor_entry_focus_out(GtkWidget * widget, GdkEventFocus * event,
111 static gboolean hildon_date_editor_date_error(HildonDateEditor *editor,
112 HildonDateEditorErrorType type);
114 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
115 GdkEventFocus * event,
117 static void hildon_date_editor_get_property( GObject *object, guint param_id,
118 GValue *value, GParamSpec *pspec );
119 static void hildon_date_editor_set_property (GObject *object, guint param_id,
120 const GValue *value, GParamSpec *pspec);
122 hildon_child_forall(GtkContainer * container,
123 gboolean include_internals,
124 GtkCallback callback, gpointer callback_data);
126 static void hildon_date_editor_destroy(GtkObject * self);
129 hildon_date_editor_size_allocate(GtkWidget * widget,
130 GtkAllocation * allocation);
133 hildon_date_editor_size_request(GtkWidget * widget,
134 GtkRequisition * requisition);
137 _hildon_date_editor_entry_select_all(GtkWidget *widget);
139 /* Property indices */
149 struct _HildonDateEditorPrivate {
150 /* Cache of values in the entries, used in setting only parts of the date */
151 guint year; /* current year in the entry */
152 guint month; /* current month in the entry */
153 guint day; /* current day in the entry */
155 gboolean calendar_icon_pressed;
157 GtkWidget *frame; /* borders around the date */
158 GtkWidget *d_event_box_image; /* icon */
159 GtkWidget *d_box_date; /* hbox for date */
161 GtkWidget *d_entry; /* GtkEntry for day */
162 GtkWidget *m_entry; /* GtkEntry for month */
163 GtkWidget *y_entry; /* GtkEntry for year */
165 GList *delims; /* List of delimeters between the fields (and possible at the ends) */
166 GtkWidget *calendar_icon;
168 gboolean skip_validation; /* don't validate date at all */
170 gint min_year; /* minimum year allowed */
171 gint max_year; /* maximum year allowed */
179 static guint date_editor_signals[LAST_SIGNAL] = { 0 };
181 GType hildon_date_editor_get_type(void)
183 static GType editor_type = 0;
186 static const GTypeInfo editor_info = {
187 sizeof(HildonDateEditorClass),
188 NULL, /* base_init */
189 NULL, /* base_finalize */
190 (GClassInitFunc) hildon_date_editor_class_init,
191 NULL, /* class_finalize */
192 NULL, /* class_data */
193 sizeof(HildonDateEditor),
195 (GInstanceInitFunc) hildon_date_editor_init,
197 editor_type = g_type_register_static(GTK_TYPE_CONTAINER,
205 hildon_date_editor_class_init(HildonDateEditorClass * editor_class)
207 GtkContainerClass *container_class = GTK_CONTAINER_CLASS(editor_class);
208 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(editor_class);
209 GObjectClass *gobject_class = G_OBJECT_CLASS (editor_class);
211 parent_class = g_type_class_peek_parent(editor_class);
213 g_type_class_add_private(editor_class,
214 sizeof(HildonDateEditorPrivate));
216 gobject_class->set_property = hildon_date_editor_set_property;
217 gobject_class->get_property = hildon_date_editor_get_property;
218 widget_class->size_request = hildon_date_editor_size_request;
219 widget_class->size_allocate = hildon_date_editor_size_allocate;
220 widget_class->focus = hildon_composite_widget_focus;
222 container_class->forall = hildon_child_forall;
223 GTK_OBJECT_CLASS(editor_class)->destroy = hildon_date_editor_destroy;
225 editor_class->date_error = hildon_date_editor_date_error;
227 date_editor_signals[DATE_ERROR] =
228 g_signal_new("date-error",
229 G_OBJECT_CLASS_TYPE(gobject_class),
231 G_STRUCT_OFFSET(HildonDateEditorClass, date_error),
232 g_signal_accumulator_true_handled, NULL,
233 _hildon_marshal_BOOLEAN__ENUM,
234 G_TYPE_BOOLEAN, 1, HILDON_TYPE_DATE_EDITOR_ERROR_TYPE);
237 * HildonDateEditor:year:
241 g_object_class_install_property( gobject_class, PROP_YEAR,
242 g_param_spec_uint("year",
247 G_PARAM_READABLE | G_PARAM_WRITABLE) );
250 * HildonDateEditor:month:
254 g_object_class_install_property( gobject_class, PROP_MONTH,
255 g_param_spec_uint("month",
260 G_PARAM_READABLE | G_PARAM_WRITABLE) );
263 * HildonDateEditor:day:
267 g_object_class_install_property( gobject_class, PROP_DAY,
268 g_param_spec_uint("day",
273 G_PARAM_READABLE | G_PARAM_WRITABLE) );
276 * HildonDateEditor:min-year:
278 * Minimum valid year.
280 g_object_class_install_property( gobject_class, PROP_MIN_YEAR,
281 g_param_spec_uint("min-year",
282 "Minimum valid year",
283 "Minimum valid year",
286 G_PARAM_READWRITE) );
289 * HildonDateEditor:max-year:
291 * Maximum valid year.
293 g_object_class_install_property( gobject_class, PROP_MAX_YEAR,
294 g_param_spec_uint("max-year",
295 "Maximum valid year",
296 "Maximum valid year",
299 G_PARAM_READWRITE) );
302 /* Forces setting of the icon to certain state. Used initially
303 and from the actual setter function */
305 real_set_calendar_icon_state(HildonDateEditorPrivate *priv,
308 gtk_image_set_from_icon_name(GTK_IMAGE(priv->calendar_icon),
309 pressed ? "qgn_widg_datedit_pr" : "qgn_widg_datedit",
310 HILDON_ICON_SIZE_WIDG);
312 priv->calendar_icon_pressed = pressed;
315 /* Sets the icon to given state (normal/pressed). Returns
316 info if the state actually changed. */
318 hildon_date_editor_set_calendar_icon_state(HildonDateEditor *editor,
321 HildonDateEditorPrivate *priv;
323 g_assert(HILDON_IS_DATE_EDITOR(editor));
325 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
326 if (pressed != priv->calendar_icon_pressed) {
327 real_set_calendar_icon_state(priv, pressed);
334 /* Packing day, month and year entries depend on locale settings
335 We find out the order and all separators by converting a known
336 date to default format and inspecting the result string */
337 static void apply_locale_field_order(HildonDateEditorPrivate *priv)
339 GDate locale_test_date;
342 gchar *iter, *delim_text;
344 g_date_set_dmy(&locale_test_date, 1, 2, 1970);
345 (void) g_date_strftime(buffer, sizeof(buffer), "%x", &locale_test_date);
353 /* Try to convert the current location into number. */
354 value = strtoul(iter, &endp, 10);
356 /* If the conversion didn't progress or the detected value was
357 unknown (we used a fixed date, you remember), we treat
358 current position as a literal */
362 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
363 priv->d_entry, FALSE, FALSE, 0);
366 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
367 priv->m_entry, FALSE, FALSE, 0);
369 case 70: /* %x format uses only 2 numbers for some locales */
371 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
372 priv->y_entry, FALSE, FALSE, 0);
375 /* All non-number characters starting from current position
376 form the delimeter */
377 for (endp = iter; *endp; endp++)
378 if (g_ascii_isdigit(*endp))
381 /* Now endp points one place past the delimeter text */
382 delim_text = g_strndup(iter, endp - iter);
383 delim = gtk_label_new(delim_text);
384 gtk_box_pack_start(GTK_BOX(priv->d_box_date),
385 delim, FALSE, FALSE, 0);
386 priv->delims = g_list_append(priv->delims, delim);
396 static void hildon_date_editor_init(HildonDateEditor * editor)
398 HildonDateEditorPrivate *priv;
401 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
403 GTK_WIDGET_SET_FLAGS(GTK_WIDGET(editor), GTK_NO_WINDOW);
405 gtk_widget_push_composite_child();
407 /* initialize values */
408 g_date_clear(&cur_date, 1);
409 g_date_set_time(&cur_date, time(NULL));
411 priv->day = g_date_get_day(&cur_date);
412 priv->month = g_date_get_month(&cur_date);
413 priv->year = g_date_get_year(&cur_date);
414 priv->min_year = DEFAULT_MIN_YEAR;
415 priv->max_year = DEFAULT_MAX_YEAR;
418 priv->frame = gtk_frame_new(NULL);
419 gtk_container_set_border_width(GTK_CONTAINER(priv->frame), 0);
421 priv->d_entry = gtk_entry_new();
422 priv->m_entry = gtk_entry_new();
423 priv->y_entry = gtk_entry_new();
425 g_object_set (G_OBJECT(priv->d_entry), "input-mode",
426 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
427 g_object_set (G_OBJECT(priv->m_entry), "input-mode",
428 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
429 g_object_set (G_OBJECT(priv->y_entry), "input-mode",
430 HILDON_INPUT_MODE_HINT_NUMERIC, NULL);
433 gtk_entry_set_width_chars(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
434 gtk_entry_set_width_chars(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
435 gtk_entry_set_width_chars(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
437 gtk_entry_set_max_length(GTK_ENTRY(priv->d_entry), DAY_ENTRY_WIDTH);
438 gtk_entry_set_max_length(GTK_ENTRY(priv->m_entry), MONTH_ENTRY_WIDTH);
439 gtk_entry_set_max_length(GTK_ENTRY(priv->y_entry), YEAR_ENTRY_WIDTH);
441 gtk_entry_set_has_frame(GTK_ENTRY(priv->d_entry), FALSE);
442 gtk_entry_set_has_frame(GTK_ENTRY(priv->m_entry), FALSE);
443 gtk_entry_set_has_frame(GTK_ENTRY(priv->y_entry), FALSE);
445 gtk_widget_set_composite_name(priv->d_entry, "day_entry");
446 gtk_widget_set_composite_name(priv->m_entry, "month_entry");
447 gtk_widget_set_composite_name(priv->y_entry, "year_entry");
449 priv->d_box_date = gtk_hbox_new(FALSE, 0);
451 priv->d_event_box_image = gtk_event_box_new();
452 priv->calendar_icon = gtk_image_new();
453 real_set_calendar_icon_state(priv, FALSE);
455 apply_locale_field_order(priv);
457 gtk_container_add(GTK_CONTAINER(priv->frame), priv->d_box_date);
458 gtk_container_add(GTK_CONTAINER(priv->d_event_box_image), priv->calendar_icon);
460 gtk_widget_set_parent(priv->frame, GTK_WIDGET(editor));
461 gtk_widget_set_parent(priv->d_event_box_image, GTK_WIDGET(editor));
462 gtk_widget_show_all(priv->frame);
463 gtk_widget_show_all(priv->d_event_box_image);
465 /* image signal connects */
466 g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "button_press_event",
467 G_CALLBACK(hildon_date_editor_icon_press), editor);
468 g_signal_connect(GTK_OBJECT(priv->d_event_box_image),
469 "button_release_event",
470 G_CALLBACK(hildon_date_editor_released), editor);
471 g_signal_connect(GTK_OBJECT(priv->d_event_box_image), "key-press-event",
472 G_CALLBACK(hildon_date_editor_keypress), editor);
473 g_signal_connect(GTK_OBJECT(priv->calendar_icon), "key-press-event",
474 G_CALLBACK(hildon_date_editor_keypress), editor);
477 /* entry signal connects */
478 g_signal_connect(GTK_OBJECT(priv->d_entry), "button_release_event",
479 G_CALLBACK(hildon_date_editor_entry_released), editor);
481 g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-in-event",
482 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
484 g_signal_connect(GTK_OBJECT(priv->m_entry), "button_release_event",
485 G_CALLBACK(hildon_date_editor_entry_released), editor);
487 g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-in-event",
488 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
490 g_signal_connect(GTK_OBJECT(priv->y_entry), "button_release_event",
491 G_CALLBACK(hildon_date_editor_entry_released), editor);
493 g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-in-event",
494 G_CALLBACK(hildon_date_editor_entry_focusin), editor);
496 g_signal_connect(GTK_OBJECT(priv->d_entry), "focus-out-event",
497 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
499 g_signal_connect(GTK_OBJECT(priv->m_entry), "focus-out-event",
500 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
502 g_signal_connect(GTK_OBJECT(priv->y_entry), "focus-out-event",
503 G_CALLBACK(hildon_date_editor_entry_focus_out), editor);
505 g_signal_connect(GTK_OBJECT(priv->d_entry), "key-press-event",
506 G_CALLBACK(hildon_date_editor_keypress), editor);
508 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-press-event",
509 G_CALLBACK(hildon_date_editor_keypress), editor);
511 g_signal_connect(GTK_OBJECT(priv->y_entry), "key-press-event",
512 G_CALLBACK(hildon_date_editor_keypress), editor);
514 g_signal_connect(GTK_OBJECT(priv->d_entry), "key-release-event",
515 G_CALLBACK(hildon_date_editor_keyrelease), editor);
517 g_signal_connect(GTK_OBJECT(priv->m_entry), "key-release-event",
518 G_CALLBACK(hildon_date_editor_keyrelease), editor);
520 g_signal_connect(GTK_OBJECT(priv->y_entry), "key-release-event",
521 G_CALLBACK(hildon_date_editor_keyrelease), editor);
523 hildon_date_editor_set_date(editor, priv->year, priv->month, priv->day);
525 g_signal_connect(GTK_OBJECT(priv->d_entry), "changed",
526 G_CALLBACK(hildon_date_editor_entry_changed), editor);
528 g_signal_connect(GTK_OBJECT(priv->m_entry), "changed",
529 G_CALLBACK(hildon_date_editor_entry_changed), editor);
531 g_signal_connect(GTK_OBJECT(priv->y_entry), "changed",
532 G_CALLBACK(hildon_date_editor_entry_changed), editor);
534 gtk_widget_pop_composite_child();
537 static void hildon_date_editor_set_property (GObject *object, guint param_id,
538 const GValue *value, GParamSpec *pspec)
540 HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
541 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
547 hildon_date_editor_set_year (editor, g_value_get_uint(value));
551 hildon_date_editor_set_month (editor, g_value_get_uint(value));
555 hildon_date_editor_set_day (editor, g_value_get_uint(value));
559 val = g_value_get_uint(value);
560 if (val <= priv->max_year)
562 priv->min_year = val;
563 /* Clamp current year */
564 if (hildon_date_editor_get_year (editor) < priv->min_year)
565 hildon_date_editor_set_year (editor, priv->min_year);
568 g_warning("min-year cannot be greater than max-year");
572 val = g_value_get_uint(value);
573 if (val >= priv->min_year)
575 priv->max_year = val;
576 /* Clamp current year */
577 if (hildon_date_editor_get_year (editor) > priv->max_year)
578 hildon_date_editor_set_year (editor, priv->max_year);
581 g_warning("max-year cannot be less than min-year");
585 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
590 static void hildon_date_editor_get_property( GObject *object, guint param_id,
591 GValue *value, GParamSpec *pspec )
593 HildonDateEditor *editor = HILDON_DATE_EDITOR(object);
594 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
599 g_value_set_uint (value, hildon_date_editor_get_year (editor));
603 g_value_set_uint (value, hildon_date_editor_get_month (editor));
607 g_value_set_uint (value, hildon_date_editor_get_day (editor));
611 g_value_set_uint (value, priv->min_year);
615 g_value_set_uint (value, priv->max_year);
619 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, param_id, pspec);
624 static void hildon_child_forall(GtkContainer * container,
625 gboolean include_internals,
626 GtkCallback callback,
627 gpointer callback_data)
629 HildonDateEditor *editor;
630 HildonDateEditorPrivate *priv;
632 g_assert(HILDON_IS_DATE_EDITOR(container));
635 editor = HILDON_DATE_EDITOR(container);
636 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
638 if (include_internals) {
639 (*callback) (priv->frame, callback_data);
640 (*callback) (priv->d_event_box_image, callback_data);
644 static void hildon_date_editor_destroy(GtkObject * self)
646 HildonDateEditorPrivate *priv;
648 priv = HILDON_DATE_EDITOR_GET_PRIVATE(self);
651 gtk_widget_unparent(priv->frame);
654 if (priv->d_event_box_image) {
655 gtk_widget_unparent(priv->d_event_box_image);
656 priv->d_event_box_image = NULL;
659 g_list_free(priv->delims);
663 if (GTK_OBJECT_CLASS(parent_class)->destroy)
664 GTK_OBJECT_CLASS(parent_class)->destroy(self);
668 * hildon_date_editor_new:
670 * Creates a new date editor. The current system date
671 * is shown in the editor.
673 * Returns: pointer to a new @HildonDateEditor widget.
675 GtkWidget *hildon_date_editor_new(void)
677 return GTK_WIDGET(g_object_new(HILDON_TYPE_DATE_EDITOR, NULL));
681 * hildon_date_editor_set_date:
682 * @date: the @HildonDateEditor widget
687 * Sets the date shown in the editor.
689 void hildon_date_editor_set_date(HildonDateEditor * editor,
690 guint year, guint month, guint day)
692 HildonDateEditorPrivate *priv;
694 g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
696 /* This function cannot be implemented by calling
697 component setters, since applying the individual
698 values one by one can make the date temporarily
699 invalid (depending on what the previous values were),
700 which in turn causes that the desired date
701 is not set (even though it's valid). We must set all the
702 components at one go and not try to do any validation etc
705 g_return_if_fail(HILDON_IS_DATE_EDITOR(editor));
706 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
708 if (g_date_valid_dmy(day, month, year))
717 g_date_set_dmy(&date, day, month, year);
719 /* We apply the new values, but do not want automatic focus move
721 g_snprintf(buffer, sizeof(buffer), "%04d", year);
722 g_signal_handlers_block_by_func(priv->y_entry,
723 (gpointer) hildon_date_editor_entry_changed, editor);
724 gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
725 g_signal_handlers_unblock_by_func(priv->y_entry,
726 (gpointer) hildon_date_editor_entry_changed, editor);
728 g_date_strftime(buffer, sizeof(buffer), "%m", &date);
729 g_signal_handlers_block_by_func(priv->m_entry,
730 (gpointer) hildon_date_editor_entry_changed, editor);
731 gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
732 g_signal_handlers_unblock_by_func(priv->m_entry,
733 (gpointer) hildon_date_editor_entry_changed, editor);
735 g_date_strftime(buffer, sizeof(buffer), "%d", &date);
736 g_signal_handlers_block_by_func(priv->d_entry,
737 (gpointer) hildon_date_editor_entry_changed, editor);
738 gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
739 g_signal_handlers_unblock_by_func(priv->d_entry,
740 (gpointer) hildon_date_editor_entry_changed, editor);
742 g_object_notify(G_OBJECT(editor), "year");
743 g_object_notify(G_OBJECT(editor), "month");
744 g_object_notify(G_OBJECT(editor), "day");
749 * hildon_date_editor_get_date:
750 * @date: the @HildonDateEditor widget
755 * Returns: the year, month, and day currently on the
758 void hildon_date_editor_get_date(HildonDateEditor * date,
759 guint * year, guint * month, guint * day)
761 HildonDateEditorPrivate *priv;
763 g_return_if_fail(HILDON_IS_DATE_EDITOR(date));
764 g_return_if_fail(year);
765 g_return_if_fail(month);
766 g_return_if_fail(day);
768 priv = HILDON_DATE_EDITOR_GET_PRIVATE(date);
770 /* FIXME: The role of priv->{day,month,year} members vs. entry contents
771 is unclear. They do not neccesarily match and still the texts are
772 used as return values and members for some internal validation!!
773 At least a partly reason is to allow empty text to become
774 0 return value, while members are restricted to valid ranges?!
775 However, if we change the current way, we are likely to break
776 some applications if they rely on some specific way how this
777 widget currently handles empty values and temporarily invalid values.
779 The key issue is this: What should the _get methods return while
780 user is editing a field and the result is incomplete. The
781 partial result? The last good result? If we return partial result
782 we also need a way to inform if the date is not valid. Current
783 implementation is some kind of hybrid of these two...
786 hildon_date_editor_set_day(editor, hildon_date_editor_get_day(editor));
788 easily fails, since set_day tries to force validity while get_day
791 Proposal: Always return the same values that are shown in the
792 fields. We add a separate flag (Or use GDate) to
793 indicate if the current date is valid. This would allow
794 setters to make the date invalid as well.
796 *year = /*priv->year;*/
797 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
798 *month = /*priv->month;*/
799 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
800 *day = /*priv->day;*/
801 (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
804 static gboolean hildon_date_editor_icon_press(GtkWidget * widget,
805 GdkEventButton * event,
808 g_assert(GTK_IS_WIDGET(widget));
809 g_assert(event != NULL);
810 g_assert(HILDON_IS_DATE_EDITOR(data));
812 if (event->button == 1)
813 hildon_date_editor_set_calendar_icon_state(HILDON_DATE_EDITOR(data), TRUE);
818 static gboolean hildon_date_editor_entry_released(GtkWidget * widget,
819 GdkEventButton * event,
822 HildonDateEditor *ed;
823 HildonDateEditorPrivate *priv;
825 ed = HILDON_DATE_EDITOR(data);
826 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
828 if (event->button == 1) {
829 /* We might not get focus because of invalid values in entries */
830 if (GTK_WIDGET_HAS_FOCUS(widget))
831 g_idle_add((GSourceFunc)
832 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
838 static gboolean hildon_date_editor_entry_focusin(GtkWidget * widget,
839 GdkEventFocus * event,
842 if (!GTK_ENTRY(widget)->button)
844 g_idle_add((GSourceFunc)
845 _hildon_date_editor_entry_select_all, GTK_ENTRY(widget));
852 static void popup_calendar_dialog(HildonDateEditor *ed)
854 guint y = 0, m = 0, d = 0;
860 hildon_date_editor_get_date(ed, &y, &m, &d);
862 parent = gtk_widget_get_ancestor(GTK_WIDGET(ed), GTK_TYPE_WINDOW);
863 popup = hildon_calendar_popup_new(GTK_WINDOW(parent), y, m, d);
865 g_value_init(&val, G_TYPE_INT);
866 /* Set max/min year in calendar popup to date editor values */
867 g_object_get_property(G_OBJECT(ed), "min-year", &val);
868 g_object_set_property(G_OBJECT(popup), "min-year", &val);
869 g_object_get_property(G_OBJECT(ed), "max-year", &val);
870 g_object_set_property(G_OBJECT(popup), "max-year", &val);
872 /* Pop up calendar */
873 result = gtk_dialog_run(GTK_DIALOG(popup));
875 case GTK_RESPONSE_OK:
876 case GTK_RESPONSE_ACCEPT:
877 hildon_calendar_popup_get_date(HILDON_CALENDAR_POPUP(popup), &y,
879 hildon_date_editor_set_date(ed, y, m, d);
882 gtk_widget_destroy(popup);
885 /* button released */
886 static gboolean hildon_date_editor_released(GtkWidget * widget,
887 GdkEventButton * event,
890 HildonDateEditor *ed;
892 g_assert(GTK_IS_WIDGET(widget));
893 g_assert(event != NULL);
894 g_assert(HILDON_IS_DATE_EDITOR(data));
896 ed = HILDON_DATE_EDITOR(data);
898 if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
899 popup_calendar_dialog(ed);
904 /* This is called whenever some editor filed loses the focus and
905 when the all of the fields are filled.
906 Earlier this was called whenever an entry changed */
907 /* FIXME: Validation on focus_out is broken by concept */
909 hildon_date_editor_entry_validate(GtkWidget *widget, gpointer data)
911 HildonDateEditor *ed;
912 HildonDateEditorPrivate *priv;
913 gint d, m, y, max_days;
914 gboolean r; /* temp return values for signals */
916 gint error_code = NO_ERROR;
918 g_assert(HILDON_IS_DATE_EDITOR(data));
919 g_assert(GTK_IS_ENTRY(widget));
921 ed = HILDON_DATE_EDITOR(data);
922 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
924 if (priv->skip_validation)
927 /*check if the calling entry is empty*/
928 text = gtk_entry_get_text(GTK_ENTRY(widget));
929 if(text == NULL || text[0] == 0)
931 if (widget == priv->d_entry)
932 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_DAY, &r);
933 else if(widget == priv->m_entry)
934 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_MONTH, &r);
936 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, EMPTY_YEAR, &r);
941 /* Ok, we now check validity. Some fields can be empty */
942 text = gtk_entry_get_text(GTK_ENTRY(priv->d_entry));
943 if (text == NULL || text[0] == 0) return;
945 text = gtk_entry_get_text(GTK_ENTRY(priv->m_entry));
946 if (text == NULL || text[0] == 0) return;
948 text = gtk_entry_get_text(GTK_ENTRY(priv->y_entry));
949 if (text == NULL || text[0] == 0) return;
952 /* Did it actually change? */
953 if (d != priv->day || m != priv->month || y != priv->year)
955 /* We could/should use hildon_date_editor_set_year and such functions
956 * to set the date, instead of use gtk_entry_set_text, and then change
957 * the priv member but hildon_date_editor_set_year and such functions
958 * check if the date is valid, we do want to do date validation check
959 * here according to spec */
962 if(widget == priv->m_entry) {
964 error_code = MIN_MONTH;
968 error_code = MAX_MONTH;
974 if(widget == priv->y_entry) {
975 if (y < priv->min_year) {
976 error_code = MIN_YEAR;
979 else if (y > priv->max_year) {
980 error_code = MAX_YEAR;
985 /* Validate day. We have to do this in every case, since
986 changing month or year can make the day number to be invalid */
987 max_days = g_date_get_days_in_month(m,y);
989 error_code = MIN_DAY;
992 else if (d > max_days) {
994 error_code = MAX_DAY;
997 else { /* the date does not exist (is invalid) */
998 error_code = INVALID_DATE;
999 /* check what was changed and restore previous value */
1000 if ( widget == priv->y_entry )
1002 else if ( widget == priv->m_entry )
1009 if (error_code != NO_ERROR)
1011 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1013 g_idle_add ((GSourceFunc)
1014 _hildon_date_editor_entry_select_all,
1017 /* Set focus back to invalid entry */
1018 gtk_widget_grab_focus(widget);
1022 /* Fix and reformat the date after error signal is processed.
1023 reformatting can be needed even in a such case that numerical
1024 values of the date components are the same as earlier. */
1025 hildon_date_editor_set_date(ed, y, m, d);
1028 /* When entry becomes full, we move the focus to the next field.
1029 If we are on the last field, the whole contents are validated. */
1031 hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
1035 g_assert(GTK_IS_ENTRY(ed));
1036 g_assert(HILDON_IS_DATE_EDITOR(data));
1038 entry = GTK_ENTRY(ed);
1040 /* If day entry is full, move to next entry or validate */
1041 if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
1043 if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
1044 hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
1048 static gboolean hildon_date_editor_keyrelease(GtkWidget * widget,
1049 GdkEventKey * event,
1052 HildonDateEditor *ed;
1053 HildonDateEditorPrivate *priv;
1055 g_return_val_if_fail(data, FALSE);
1056 g_return_val_if_fail(widget, FALSE);
1058 ed = HILDON_DATE_EDITOR(data);
1059 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1061 if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return ||
1062 event->keyval == GDK_ISO_Enter) {
1063 if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
1065 popup_calendar_dialog(ed);
1068 } else if (event->keyval == GDK_Escape)
1069 priv->skip_validation = FALSE;
1074 /* keyboard handling */
1075 static gboolean hildon_date_editor_keypress(GtkWidget * widget,
1076 GdkEventKey * event,
1079 HildonDateEditor *ed;
1080 HildonDateEditorPrivate *priv;
1083 g_assert(HILDON_IS_DATE_EDITOR(data));
1084 g_assert(GTK_IS_ENTRY(widget));
1086 ed = HILDON_DATE_EDITOR(data);
1087 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1088 pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1090 switch (event->keyval) {
1092 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
1096 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
1101 /* Ignore return value, since we want to handle event at all times.
1102 otherwise vkb would popup when the keyrepeat starts. */
1103 (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
1107 priv->skip_validation = TRUE;
1116 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
1117 GdkEventFocus * event,
1120 hildon_date_editor_entry_validate(widget, data);
1125 hildon_date_editor_date_error(HildonDateEditor *editor,
1126 HildonDateEditorErrorType type)
1128 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1133 gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), 31);
1136 gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), 12);
1139 gtk_infoprintf(NULL, _("ckct_ib_maximum_value"), priv->max_year);
1143 gtk_infoprintf(NULL, _("ckct_ib_minimum_value"), 1);
1146 gtk_infoprintf(NULL, _("ckct_ib_minimum_value"), priv->min_year);
1149 gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"), 1, 31);
1152 gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"), 1, 12);
1155 gtk_infoprintf(NULL, _("ckct_ib_set_a_value_within_range"),
1156 priv->min_year, priv->max_year);
1159 gtk_infoprint(NULL, _("ckct_ib_date_does_not_exist"));
1162 /*default error message ?*/
1168 static void hildon_date_editor_size_request(GtkWidget * widget,
1169 GtkRequisition * requisition)
1171 HildonDateEditor *ed;
1172 HildonDateEditorPrivate *priv;
1173 GtkRequisition f_req, img_req;
1175 g_assert(GTK_IS_WIDGET(widget));
1176 g_assert(requisition != NULL);
1178 ed = HILDON_DATE_EDITOR(widget);
1179 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1181 /* Our own children affect our size */
1182 gtk_widget_size_request(priv->frame, &f_req);
1183 gtk_widget_size_request(priv->d_event_box_image, &img_req);
1185 /* calculate our size */
1186 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1188 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1189 doing so would break current pixel specifications, since
1190 the text entry by itself is already 30px tall + then frame takes
1192 requisition->height = DATE_EDITOR_HEIGHT;
1195 static void hildon_date_editor_size_allocate(GtkWidget * widget,
1196 GtkAllocation * allocation)
1198 HildonDateEditor *ed;
1199 HildonDateEditorPrivate *priv;
1200 GtkAllocation f_alloc, img_alloc;
1202 GtkRequisition max_req;
1205 g_assert(GTK_IS_WIDGET(widget));
1206 g_assert(allocation != NULL);
1208 ed = HILDON_DATE_EDITOR(widget);
1209 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1211 widget->allocation = *allocation;
1213 gtk_widget_get_child_requisition(widget, &max_req);
1215 /* Center vertically */
1216 f_alloc.y = img_alloc.y = allocation->y +
1217 MAX(allocation->height - max_req.height, 0) / 2;
1219 /* Center horizontally */
1220 f_alloc.x = img_alloc.x = allocation->x +
1221 MAX(allocation->width - max_req.width, 0) / 2;
1223 /* allocate frame */
1224 if (GTK_WIDGET_VISIBLE(priv->frame)) {
1225 gtk_widget_get_child_requisition(priv->frame, &req);
1227 f_alloc.width = req.width;
1228 f_alloc.height = max_req.height;
1229 gtk_widget_size_allocate(priv->frame, &f_alloc);
1233 if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
1234 gtk_widget_get_child_requisition(priv->d_event_box_image,
1237 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1238 img_alloc.width = req.width;
1239 img_alloc.height = max_req.height;
1240 gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
1243 /* FIXME: We really should not alloc delimeters by hand (since they
1244 are not our own children, but we need to force to appear
1245 higher. This ugly hack is needed to compensate the forced
1246 height in size_request. */
1247 for (iter = priv->delims; iter; iter = iter->next)
1250 GtkAllocation alloc;
1252 delim = GTK_WIDGET(iter->data);
1253 alloc = delim->allocation;
1254 alloc.height = max_req.height;
1255 alloc.y = priv->d_entry->allocation.y - 2;
1257 gtk_widget_size_allocate(delim, &alloc);
1262 * hildon_date_editor_set_year:
1263 * @editor: the @HildonDateEditor widget
1266 * Sets the year shown in the editor.
1268 * Returns: TRUE if the year is valid
1270 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
1272 HildonDateEditorPrivate *priv;
1273 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1274 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1276 if (g_date_valid_dmy(priv->day, priv->month, year))
1281 g_snprintf(buffer, sizeof(buffer), "%04d", year);
1283 /* We apply the new day, but do not want automatic focus move
1284 etc to take place */
1285 g_signal_handlers_block_by_func(priv->y_entry,
1286 (gpointer) hildon_date_editor_entry_changed, editor);
1287 gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
1288 g_signal_handlers_unblock_by_func(priv->y_entry,
1289 (gpointer) hildon_date_editor_entry_changed, editor);
1291 g_object_notify(G_OBJECT(editor), "year");
1299 * hildon_date_editor_set_month:
1300 * @editor: the @HildonDateEditor widget
1303 * Sets the month shown in the editor.
1305 * Returns: TRUE if the month is valid
1307 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
1309 HildonDateEditorPrivate *priv;
1310 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1311 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1313 if (g_date_valid_dmy(priv->day, month, priv->year))
1318 priv->month = month;
1319 g_date_set_dmy(&date, priv->day, month, priv->year);
1320 g_date_strftime(buffer, sizeof(buffer), "%m", &date);
1322 /* We apply the new day, but do not want automatic focus move
1323 etc to take place */
1324 g_signal_handlers_block_by_func(priv->m_entry,
1325 (gpointer) hildon_date_editor_entry_changed, editor);
1326 gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
1327 g_signal_handlers_unblock_by_func(priv->m_entry,
1328 (gpointer) hildon_date_editor_entry_changed, editor);
1330 g_object_notify(G_OBJECT(editor), "month");
1337 * hildon_date_editor_set_day:
1338 * @editor: the @HildonDateEditor widget
1341 * Sets the day shown in the editor.
1343 * Returns: TRUE if the day is valid
1345 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
1347 HildonDateEditorPrivate *priv;
1349 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1350 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1352 if (g_date_valid_dmy(day, priv->month, priv->year))
1358 g_date_set_dmy(&date, day, priv->month, priv->year);
1359 g_date_strftime(buffer, sizeof(buffer), "%d", &date);
1361 /* We apply the new day, but do not want automatic focus move
1362 etc to take place */
1363 g_signal_handlers_block_by_func(priv->d_entry,
1364 (gpointer) hildon_date_editor_entry_changed, editor);
1365 gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
1366 g_signal_handlers_unblock_by_func(priv->d_entry,
1367 (gpointer) hildon_date_editor_entry_changed, editor);
1369 g_object_notify(G_OBJECT(editor), "day");
1376 * hildon_date_editor_get_year:
1377 * @editor: the @HildonDateEditor widget
1379 * Returns: the current year shown in the editor.
1381 guint hildon_date_editor_get_year(HildonDateEditor *editor)
1383 HildonDateEditorPrivate *priv;
1384 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1385 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1386 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
1390 * hildon_date_editor_get_month:
1391 * @editor: the @HildonDateEditor widget
1393 * Gets the month shown in the editor.
1395 * Returns: the current month shown in the editor.
1398 guint hildon_date_editor_get_month(HildonDateEditor *editor)
1400 HildonDateEditorPrivate *priv;
1401 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1402 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1403 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
1407 * hildon_date_editor_get_day:
1408 * @editor: the @HildonDateEditor widget
1410 * Gets the day shown in the editor.
1412 * Returns: the current day shown in the editor
1415 guint hildon_date_editor_get_day(HildonDateEditor *editor)
1417 HildonDateEditorPrivate *priv;
1418 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1419 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1420 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
1425 _hildon_date_editor_entry_select_all (GtkWidget *widget)
1427 GDK_THREADS_ENTER ();
1428 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);
1429 GDK_THREADS_LEAVE ();