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 * @shortdesc: DateEditor is a widget for setting, getting and showing a
28 * @longdesc: The date editor consists of a GtkLabel that shows the current
29 * date in localized form and an icon. Clicking on either the label or the
30 * icon opens the #HildonCalendarPopup used for selecting the date.
31 * Similarly, if the editor has the focus, and space or enter key is
32 * pressed, the #HildonCalendarPopup will open.
34 * @seealso: #HildonTimeEditor, #HildonCalendarPopup
39 #include <gdk/gdkkeysyms.h>
45 #include <hildon-widgets/hildon-date-editor.h>
46 #include <hildon-widgets/hildon-calendar-popup.h>
47 #include <hildon-widgets/gtk-infoprint.h>
48 #include <hildon-widgets/hildon-defines.h>
49 #include <hildon-widgets/hildon-input-mode-hint.h>
50 #include "hildon-composite-widget.h"
51 #include "hildon-marshalers.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_DATE_EDITOR_TYPE, 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__INT,
234 G_TYPE_BOOLEAN, 1, G_TYPE_INT);
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 time editor. The current system date
671 * is shown in the editor.
673 * Return value: Pointer to a new @HildonDateEditor widget.
675 GtkWidget *hildon_date_editor_new(void)
677 return GTK_WIDGET(g_object_new(HILDON_DATE_EDITOR_TYPE, 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) {
993 /* Report range error or invalid date */
994 error_code = d > 31 ? MAX_DAY : INVALID_DATE;
996 widget = priv->d_entry; /* Note! Change param to point day entry */
999 if (error_code != NO_ERROR)
1001 g_signal_emit(ed, date_editor_signals[DATE_ERROR], 0, error_code, &r);
1003 g_idle_add ((GSourceFunc)
1004 _hildon_date_editor_entry_select_all,
1007 /* Set focus back to invalid entry */
1008 gtk_widget_grab_focus(widget);
1012 /* Fix and reformat the date after error signal is processed.
1013 reformatting can be needed even in a such case that numerical
1014 values of the date components are the same as earlier. */
1015 hildon_date_editor_set_date(ed, y, m, d);
1018 /* When entry becomes full, we move the focus to the next field.
1019 If we are on the last field, the whole contents are validated. */
1021 hildon_date_editor_entry_changed(GtkEditable *ed, gpointer data)
1025 g_assert(GTK_IS_ENTRY(ed));
1026 g_assert(HILDON_IS_DATE_EDITOR(data));
1028 entry = GTK_ENTRY(ed);
1030 /* If day entry is full, move to next entry or validate */
1031 if (g_utf8_strlen(gtk_entry_get_text(entry), -1) == gtk_entry_get_max_length(entry))
1033 if (!gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT))
1034 hildon_date_editor_entry_validate(GTK_WIDGET(entry), data);
1038 static gboolean hildon_date_editor_keyrelease(GtkWidget * widget,
1039 GdkEventKey * event,
1042 HildonDateEditor *ed;
1043 HildonDateEditorPrivate *priv;
1045 g_return_val_if_fail(data, FALSE);
1046 g_return_val_if_fail(widget, FALSE);
1048 ed = HILDON_DATE_EDITOR(data);
1049 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1051 if (event->keyval == GDK_KP_Enter || event->keyval == GDK_Return ||
1052 event->keyval == GDK_ISO_Enter) {
1053 if (hildon_date_editor_set_calendar_icon_state(ed, FALSE))
1055 popup_calendar_dialog(ed);
1058 } else if (event->keyval == GDK_Escape)
1059 priv->skip_validation = FALSE;
1064 /* keyboard handling */
1065 static gboolean hildon_date_editor_keypress(GtkWidget * widget,
1066 GdkEventKey * event,
1069 HildonDateEditor *ed;
1070 HildonDateEditorPrivate *priv;
1073 g_assert(HILDON_IS_DATE_EDITOR(data));
1074 g_assert(GTK_IS_ENTRY(widget));
1076 ed = HILDON_DATE_EDITOR(data);
1077 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1078 pos = gtk_editable_get_position(GTK_EDITABLE(widget));
1080 switch (event->keyval) {
1082 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_LEFT);
1086 (void) gtk_widget_child_focus(GTK_WIDGET(data), GTK_DIR_RIGHT);
1091 /* Ignore return value, since we want to handle event at all times.
1092 otherwise vkb would popup when the keyrepeat starts. */
1093 (void) hildon_date_editor_set_calendar_icon_state(ed, TRUE);
1097 priv->skip_validation = TRUE;
1106 static gboolean hildon_date_editor_entry_focus_out(GtkWidget * widget,
1107 GdkEventFocus * event,
1110 hildon_date_editor_entry_validate(widget, data);
1115 hildon_date_editor_date_error(HildonDateEditor *editor,
1116 HildonDateEditorErrorType type)
1118 HildonDateEditorPrivate *priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1123 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 31);
1126 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), 12);
1129 gtk_infoprintf(NULL, _("Ckct_ib_maximum_value"), priv->max_year);
1133 gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), 1);
1136 gtk_infoprintf(NULL, _("Ckct_ib_minimum_value"), priv->min_year);
1139 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 31);
1142 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"), 1, 12);
1145 gtk_infoprintf(NULL, _("Ckct_ib_set_a_value_within_range"),
1146 priv->min_year, priv->max_year);
1149 gtk_infoprint(NULL, _("Ckct_ib_date_does_not_exist"));
1152 /*default error message ?*/
1158 static void hildon_date_editor_size_request(GtkWidget * widget,
1159 GtkRequisition * requisition)
1161 HildonDateEditor *ed;
1162 HildonDateEditorPrivate *priv;
1163 GtkRequisition f_req, img_req;
1165 g_assert(GTK_IS_WIDGET(widget));
1166 g_assert(requisition != NULL);
1168 ed = HILDON_DATE_EDITOR(widget);
1169 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1171 /* Our own children affect our size */
1172 gtk_widget_size_request(priv->frame, &f_req);
1173 gtk_widget_size_request(priv->d_event_box_image, &img_req);
1175 /* calculate our size */
1176 requisition->width = f_req.width + img_req.width + HILDON_MARGIN_DEFAULT;
1178 /* FIXME: Fixed size is bad! We should use the maximum of our children, but
1179 doing so would break current pixel specifications, since
1180 the text entry by itself is already 30px tall + then frame takes
1182 requisition->height = DATE_EDITOR_HEIGHT;
1185 static void hildon_date_editor_size_allocate(GtkWidget * widget,
1186 GtkAllocation * allocation)
1188 HildonDateEditor *ed;
1189 HildonDateEditorPrivate *priv;
1190 GtkAllocation f_alloc, img_alloc;
1192 GtkRequisition max_req;
1195 g_assert(GTK_IS_WIDGET(widget));
1196 g_assert(allocation != NULL);
1198 ed = HILDON_DATE_EDITOR(widget);
1199 priv = HILDON_DATE_EDITOR_GET_PRIVATE(ed);
1201 widget->allocation = *allocation;
1203 gtk_widget_get_child_requisition(widget, &max_req);
1205 /* Center vertically */
1206 f_alloc.y = img_alloc.y = allocation->y +
1207 MAX(allocation->height - max_req.height, 0) / 2;
1209 /* Center horizontally */
1210 f_alloc.x = img_alloc.x = allocation->x +
1211 MAX(allocation->width - max_req.width, 0) / 2;
1213 /* allocate frame */
1214 if (GTK_WIDGET_VISIBLE(priv->frame)) {
1215 gtk_widget_get_child_requisition(priv->frame, &req);
1217 f_alloc.width = req.width;
1218 f_alloc.height = max_req.height;
1219 gtk_widget_size_allocate(priv->frame, &f_alloc);
1223 if (GTK_WIDGET_VISIBLE(priv->d_event_box_image)) {
1224 gtk_widget_get_child_requisition(priv->d_event_box_image,
1227 img_alloc.x += f_alloc.width + HILDON_MARGIN_DEFAULT;
1228 img_alloc.width = req.width;
1229 img_alloc.height = max_req.height;
1230 gtk_widget_size_allocate(priv->d_event_box_image, &img_alloc);
1233 /* FIXME: We really should not alloc delimeters by hand (since they
1234 are not our own children, but we need to force to appear
1235 higher. This ugly hack is needed to compensate the forced
1236 height in size_request. */
1237 for (iter = priv->delims; iter; iter = iter->next)
1240 GtkAllocation alloc;
1242 delim = GTK_WIDGET(iter->data);
1243 alloc = delim->allocation;
1244 alloc.height = max_req.height;
1245 alloc.y = priv->d_entry->allocation.y - 2;
1247 gtk_widget_size_allocate(delim, &alloc);
1252 * hildon_date_editor_set_year:
1253 * @editor: the @HildonDateEditor widget
1256 * Sets the year shown in the editor.
1258 * Return: Returns TRUE if the year is valid.
1260 gboolean hildon_date_editor_set_year(HildonDateEditor *editor, guint year)
1262 HildonDateEditorPrivate *priv;
1263 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1264 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1266 if (g_date_valid_dmy(priv->day, priv->month, year))
1271 g_snprintf(buffer, sizeof(buffer), "%04d", year);
1273 /* We apply the new day, but do not want automatic focus move
1274 etc to take place */
1275 g_signal_handlers_block_by_func(priv->y_entry,
1276 (gpointer) hildon_date_editor_entry_changed, editor);
1277 gtk_entry_set_text(GTK_ENTRY(priv->y_entry), buffer);
1278 g_signal_handlers_unblock_by_func(priv->y_entry,
1279 (gpointer) hildon_date_editor_entry_changed, editor);
1281 g_object_notify(G_OBJECT(editor), "year");
1289 * hildon_date_editor_set_month:
1290 * @editor: the @HildonDateEditor widget
1293 * Sets the month shown in the editor.
1295 * Return: Returns TRUE if the month is valid.
1297 gboolean hildon_date_editor_set_month(HildonDateEditor *editor, guint month)
1299 HildonDateEditorPrivate *priv;
1300 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1301 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1303 if (g_date_valid_dmy(priv->day, month, priv->year))
1308 priv->month = month;
1309 g_date_set_dmy(&date, priv->day, month, priv->year);
1310 g_date_strftime(buffer, sizeof(buffer), "%m", &date);
1312 /* We apply the new day, but do not want automatic focus move
1313 etc to take place */
1314 g_signal_handlers_block_by_func(priv->m_entry,
1315 (gpointer) hildon_date_editor_entry_changed, editor);
1316 gtk_entry_set_text(GTK_ENTRY(priv->m_entry), buffer);
1317 g_signal_handlers_unblock_by_func(priv->m_entry,
1318 (gpointer) hildon_date_editor_entry_changed, editor);
1320 g_object_notify(G_OBJECT(editor), "month");
1327 * hildon_date_editor_set_day:
1328 * @editor: the @HildonDateEditor widget
1331 * Sets the day shown in the editor.
1333 * Return: Returns TRUE if the day is valid.
1335 gboolean hildon_date_editor_set_day(HildonDateEditor *editor, guint day)
1337 HildonDateEditorPrivate *priv;
1339 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), FALSE );
1340 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1342 if (g_date_valid_dmy(day, priv->month, priv->year))
1348 g_date_set_dmy(&date, day, priv->month, priv->year);
1349 g_date_strftime(buffer, sizeof(buffer), "%d", &date);
1351 /* We apply the new day, but do not want automatic focus move
1352 etc to take place */
1353 g_signal_handlers_block_by_func(priv->d_entry,
1354 (gpointer) hildon_date_editor_entry_changed, editor);
1355 gtk_entry_set_text(GTK_ENTRY(priv->d_entry), buffer);
1356 g_signal_handlers_unblock_by_func(priv->d_entry,
1357 (gpointer) hildon_date_editor_entry_changed, editor);
1359 g_object_notify(G_OBJECT(editor), "day");
1366 * hildon_date_editor_get_year:
1367 * @editor: the @HildonDateEditor widget
1369 * Gets the year shown in the editor.
1371 * Return: Returns the current year shown in the editor.
1373 guint hildon_date_editor_get_year(HildonDateEditor *editor)
1375 HildonDateEditorPrivate *priv;
1376 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1377 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1378 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->y_entry)));
1382 * hildon_date_editor_get_month:
1383 * @editor: the @HildonDateEditor widget
1385 * Gets the month shown in the editor.
1387 * Return: Returns the current month shown in the editor.
1390 guint hildon_date_editor_get_month(HildonDateEditor *editor)
1392 HildonDateEditorPrivate *priv;
1393 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1394 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1395 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->m_entry)));
1399 * hildon_date_editor_get_day:
1400 * @editor: the @HildonDateEditor widget
1402 * Gets the day shown in the editor.
1404 * Return: Returns the current day shown in the editor.
1407 guint hildon_date_editor_get_day(HildonDateEditor *editor)
1409 HildonDateEditorPrivate *priv;
1410 g_return_val_if_fail( HILDON_IS_DATE_EDITOR(editor), 0 );
1411 priv = HILDON_DATE_EDITOR_GET_PRIVATE(editor);
1412 return (guint) atoi(gtk_entry_get_text(GTK_ENTRY(priv->d_entry)));
1417 _hildon_date_editor_entry_select_all (GtkWidget *widget)
1419 gtk_editable_select_region(GTK_EDITABLE(widget), 0, -1);