2 * This file is a part of hildon
4 * Copyright (C) 2005, 2008 Nokia Corporation.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version. or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free
18 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
22 * SECTION:hildon-date-selector
23 * @short_description: A widget to select the current date.
25 * HildonDateSelector is a date widget, equivalent to hildon-calendar, but with a multi-column
30 #define _GNU_SOURCE /* needed for GNU nl_langinfo_l */
31 #define __USE_GNU /* needed for locale */
35 #ifdef HAVE_SYS_TIME_H
46 #include "hildon-date-selector.h"
48 #define HILDON_DATE_SELECTOR_GET_PRIVATE(obj) \
49 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_DATE_SELECTOR, HildonDateSelectorPrivate))
51 G_DEFINE_TYPE (HildonDateSelector, hildon_date_selector, HILDON_TYPE_TOUCH_SELECTOR)
54 #define LAST_YEAR 50 /* since current year */
56 #define _(String) dgettext("hildon-libs", String)
58 /* #define _(String) "%A %e. %B %Y" debug purposes */
74 struct _HildonDateSelectorPrivate
76 GtkTreeModel *year_model;
77 GtkTreeModel *month_model;
78 GtkTreeModel *day_model;
83 gint year_column; /* it depends on the locale */
85 gchar *format; /* day/month/year format, depends on locale */
89 gint creation_year; /* date at creation time */
94 static void hildon_date_selector_finalize (GObject * object);
96 /* private functions */
97 static GtkTreeModel *_create_day_model (HildonDateSelector * selector);
98 static GtkTreeModel *_create_year_model (HildonDateSelector * selector);
99 static GtkTreeModel *_create_month_model (HildonDateSelector * selector);
101 static void _get_real_date (gint * year, gint * month, gint * day);
102 static void _locales_init (HildonDateSelectorPrivate * priv);
104 static void _manage_selector_change_cb (HildonTouchSelector * selector,
105 gint num_column, gpointer data);
107 static GtkTreeModel *_update_day_model (HildonDateSelector * selector);
109 static gint _month_days (gint month, gint year);
110 static void _init_column_order (HildonDateSelector * selector);
112 static gchar *_custom_print_func (HildonTouchSelector * selector);
114 /***************************************************************************/
115 /* The following date routines are taken from the lib_date package. Keep
116 * them separate in case we want to update them if a newer lib_date comes
119 typedef unsigned int N_int;
121 typedef unsigned long N_long;
123 typedef signed long Z_long;
126 { false = FALSE, true = TRUE } boolean;
128 #define and && /* logical (boolean) operators: lower case */
132 static const N_int month_length[2][13] = {
133 {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
134 {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
137 static const N_int days_in_months[2][14] = {
138 {0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365},
139 {0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366}
142 static Z_long _calc_days (N_int year, N_int mm, N_int dd);
144 static N_int _day_of_week (N_int year, N_int mm, N_int dd);
146 static boolean _leap (N_int year);
152 return ((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
156 _day_of_week (N_int year, N_int mm, N_int dd)
160 days = _calc_days (year, mm, dd);
166 return ((N_int) days);
170 _year_to_days (N_int year)
172 return (year * 365L + (year / 4) - (year / 100) + (year / 400));
176 _calc_days (N_int year, N_int mm, N_int dd)
182 if ((mm < 1) or (mm > 12))
184 if ((dd < 1) or (dd > month_length[(lp = _leap (year))][mm]))
186 return (_year_to_days (--year) + days_in_months[lp][mm] + dd);
190 hildon_date_selector_class_init (HildonDateSelectorClass * class)
192 GObjectClass *gobject_class;
193 GtkObjectClass *object_class;
194 GtkWidgetClass *widget_class;
195 GtkContainerClass *container_class;
197 gobject_class = (GObjectClass *) class;
198 object_class = (GtkObjectClass *) class;
199 widget_class = (GtkWidgetClass *) class;
200 container_class = (GtkContainerClass *) class;
203 gobject_class->finalize = hildon_date_selector_finalize;
211 g_type_class_add_private (object_class, sizeof (HildonDateSelectorPrivate));
215 hildon_date_selector_init (HildonDateSelector * selector)
218 gint current_item = 0;
220 selector->priv = HILDON_DATE_SELECTOR_GET_PRIVATE (selector);
222 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
223 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
225 hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (selector),
228 _locales_init (selector->priv);
230 _init_column_order (selector);
232 _get_real_date (&selector->priv->creation_year,
233 &selector->priv->creation_month, &selector->priv->creation_day);
235 selector->priv->year_model = _create_year_model (selector);
236 selector->priv->month_model = _create_month_model (selector);
237 selector->priv->day_model = _create_day_model (selector);
239 /* We add the columns: FIXME: check the locale order */
240 iter = selector->priv->column_order;
241 for (iter = selector->priv->column_order; iter; iter = g_slist_next (iter)) {
242 current_item = GPOINTER_TO_INT (iter->data);
244 switch (current_item) {
246 hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
247 selector->priv->day_model, TRUE);
250 hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
251 selector->priv->month_model, TRUE);
254 hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
255 selector->priv->year_model, TRUE);
258 g_error ("Current column order incorrect");
263 g_signal_connect (G_OBJECT (selector),
264 "changed", G_CALLBACK (_manage_selector_change_cb), NULL);
266 /* By default we should select the current day */
267 hildon_date_selector_select_current_date (selector, selector->priv->creation_year,
268 selector->priv->creation_month,
269 selector->priv->creation_day);
273 hildon_date_selector_finalize (GObject * object)
275 HildonDateSelector *selector = NULL;
278 selector = HILDON_DATE_SELECTOR (object);
280 for (i = 0; i < 12; i++) {
281 g_free (selector->priv->monthname[i]);
284 g_slist_free (selector->priv->column_order);
286 g_free (selector->priv);
288 (*G_OBJECT_CLASS (hildon_date_selector_parent_class)->finalize) (object);
291 /* ------------------------------ PRIVATE METHODS ---------------------------- */
293 _custom_print_func (HildonTouchSelector * touch_selector)
295 HildonDateSelector *selector = NULL;
296 gchar *result = NULL;
297 guint year, month, day;
298 gint day_of_week = 0;
299 static gchar string[255];
300 struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
302 selector = HILDON_DATE_SELECTOR (touch_selector);
304 hildon_date_selector_get_date (selector, &year, &month, &day);
305 day_of_week = _day_of_week (year, month + 1, day) % 7;
309 tm.tm_year = year - 1900;
310 tm.tm_wday = day_of_week;
312 strftime (string, 255, _("wdgt_va_date_long"), &tm);
314 result = g_strdup (string);
319 /* This was copied from hildon-calendar */
321 _locales_init (HildonDateSelectorPrivate * priv)
323 /* Hildon: This is not exactly portable, see
324 * http://bugzilla.gnome.org/show_bug.cgi?id=343415
325 * The labels need to be instance variables as the startup wizard changes
330 l = newlocale (LC_TIME_MASK, setlocale (LC_MESSAGES, NULL), NULL);
332 priv->monthname[0] = g_locale_to_utf8 (nl_langinfo_l (MON_1, l),
333 -1, NULL, NULL, NULL);
334 priv->monthname[1] = g_locale_to_utf8 (nl_langinfo_l (MON_2, l),
335 -1, NULL, NULL, NULL);
336 priv->monthname[2] = g_locale_to_utf8 (nl_langinfo_l (MON_3, l),
337 -1, NULL, NULL, NULL);
338 priv->monthname[3] = g_locale_to_utf8 (nl_langinfo_l (MON_4, l),
339 -1, NULL, NULL, NULL);
340 priv->monthname[4] = g_locale_to_utf8 (nl_langinfo_l (MON_5, l),
341 -1, NULL, NULL, NULL);
342 priv->monthname[5] = g_locale_to_utf8 (nl_langinfo_l (MON_6, l),
343 -1, NULL, NULL, NULL);
344 priv->monthname[6] = g_locale_to_utf8 (nl_langinfo_l (MON_7, l),
345 -1, NULL, NULL, NULL);
346 priv->monthname[7] = g_locale_to_utf8 (nl_langinfo_l (MON_8, l),
347 -1, NULL, NULL, NULL);
348 priv->monthname[8] = g_locale_to_utf8 (nl_langinfo_l (MON_9, l),
349 -1, NULL, NULL, NULL);
350 priv->monthname[9] = g_locale_to_utf8 (nl_langinfo_l (MON_10, l),
351 -1, NULL, NULL, NULL);
352 priv->monthname[10] = g_locale_to_utf8 (nl_langinfo_l (MON_11, l),
353 -1, NULL, NULL, NULL);
354 priv->monthname[11] = g_locale_to_utf8 (nl_langinfo_l (MON_12, l),
355 -1, NULL, NULL, NULL);
357 priv->format = g_locale_to_utf8 (nl_langinfo_l (D_FMT, l),
358 -1, NULL, NULL, NULL);
364 _init_column_order (HildonDateSelector * selector)
366 gchar *current_order[3] = { NULL, NULL, NULL };
367 gchar *day_pos = NULL;
368 gchar *month_pos = NULL;
369 gchar *year_pos = NULL;
373 g_debug ("Current format: %s", selector->priv->format);
375 /* search each token on the format */
376 day_pos = g_strrstr (selector->priv->format, "%d");
378 month_pos = g_strrstr (selector->priv->format, "%m");
379 year_pos = g_strrstr (selector->priv->format, "%y");
380 if (year_pos == NULL) {
381 year_pos = g_strrstr (selector->priv->format, "%Y");
385 if ((day_pos == NULL) || (month_pos == NULL) || (year_pos == NULL)) {
386 g_error ("Wrong date format"); /* so default values */
388 selector->priv->day_column = 0;
389 selector->priv->month_column = 1;
390 selector->priv->year_column = 2;
391 selector->priv->column_order = g_slist_append (NULL, GINT_TO_POINTER (DAY));
392 selector->priv->column_order =
393 g_slist_append (selector->priv->column_order, GINT_TO_POINTER (MONTH));
394 selector->priv->column_order =
395 g_slist_append (selector->priv->column_order, GINT_TO_POINTER (YEAR));
398 /* sort current_order with this values (bubble sort) */
399 current_order[0] = day_pos;
400 current_order[1] = month_pos;
401 current_order[2] = year_pos;
403 for (c = 1; c <= 2; c++) {
404 for (i = 0; i < 3 - c; i++) {
405 if (current_order[i] > current_order[i + 1]) {
406 aux = current_order[i];
407 current_order[i] = current_order[i + 1];
408 current_order[i + 1] = aux;
413 /* fill the column positions */
414 selector->priv->column_order = NULL;
416 for (i = 0; i < 3; i++) {
417 if (current_order[i] == day_pos) {
418 selector->priv->column_order =
419 g_slist_append (selector->priv->column_order, GINT_TO_POINTER (DAY));
420 selector->priv->day_column = c++;
422 if (current_order[i] == month_pos) {
423 selector->priv->column_order =
424 g_slist_append (selector->priv->column_order, GINT_TO_POINTER (MONTH));
425 selector->priv->month_column = c++;
427 if (current_order[i] == year_pos) {
428 selector->priv->column_order =
429 g_slist_append (selector->priv->column_order, GINT_TO_POINTER (YEAR));
430 selector->priv->year_column = c++;
436 static GtkTreeModel *
437 _create_day_model (HildonDateSelector * selector)
439 GtkListStore *store_days = NULL;
444 store_days = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
445 for (i = 1; i < 32; i++) {
446 label = g_strdup_printf ("%d", i);
448 gtk_list_store_append (store_days, &iter);
449 gtk_list_store_set (store_days, &iter,
450 COLUMN_STRING, label, COLUMN_INT, i, -1);
454 return GTK_TREE_MODEL (store_days);
457 static GtkTreeModel *
458 _create_year_model (HildonDateSelector * selector)
460 GtkListStore *store_years = NULL;
466 real_year = selector->priv->creation_year;
468 store_years = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
469 for (i = real_year - INIT_YEAR; i < real_year + LAST_YEAR; i++) {
470 label = g_strdup_printf ("%d", i);
472 gtk_list_store_append (store_years, &iter);
473 gtk_list_store_set (store_years, &iter,
474 COLUMN_STRING, label, COLUMN_INT, i, -1);
478 return GTK_TREE_MODEL (store_years);
481 static GtkTreeModel *
482 _create_month_model (HildonDateSelector * selector)
486 GtkListStore *store_months = NULL;
489 store_months = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
490 for (i = 0; i < 12; i++) {
491 label = g_strdup_printf ("%s", selector->priv->monthname[i]);
493 gtk_list_store_append (store_months, &iter);
494 gtk_list_store_set (store_months, &iter, COLUMN_STRING, label, /* the label with the month */
495 COLUMN_INT, i, /* the month number */
500 return GTK_TREE_MODEL (store_months);
503 static GtkTreeModel *
504 _update_day_model (HildonDateSelector * selector)
506 GtkListStore *store_days = NULL;
510 guint current_day = 0;
511 guint current_year = 0;
512 guint current_month = 0;
515 hildon_date_selector_get_date (selector, NULL, NULL, ¤t_day);
517 hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
518 selector->priv->month_column, &iter);
519 gtk_tree_model_get (selector->priv->month_model,
520 &iter, COLUMN_INT, ¤t_month, -1);
522 hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
523 selector->priv->year_column, &iter);
524 gtk_tree_model_get (selector->priv->year_model,
525 &iter, COLUMN_INT, ¤t_year, -1);
527 num_days = _month_days (current_month, current_year);
529 store_days = GTK_LIST_STORE (selector->priv->day_model);
530 gtk_list_store_clear (store_days);
532 for (i = 1; i <= num_days; i++) {
533 label = g_strdup_printf ("%d", i);
535 gtk_list_store_append (store_days, &iter);
536 gtk_list_store_set (store_days, &iter,
537 COLUMN_STRING, label, COLUMN_INT, i, -1);
541 /* now we select a day */
542 if (current_day >= num_days) {
543 current_day = num_days;
546 hildon_date_selector_select_day (selector, current_day);
548 return GTK_TREE_MODEL (store_days);
553 _get_real_date (gint * year, gint * month, gint * day)
556 struct tm *tm = NULL;
559 tm = localtime (&secs);
562 *year = 1900 + tm->tm_year;
576 _manage_selector_change_cb (HildonTouchSelector * touch_selector,
577 gint num_column, gpointer data)
579 HildonDateSelector *selector = NULL;
581 g_return_if_fail (HILDON_IS_DATE_SELECTOR (touch_selector));
582 selector = HILDON_DATE_SELECTOR (touch_selector);
584 if ((num_column == selector->priv->month_column) ||
585 (num_column == selector->priv->year_column)) /* it is required to check that with
586 * the years too,remember: leap years
589 _update_day_model (selector);
594 _month_days (gint month, gint year)
596 gint month_days[2][12] = {
597 {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
598 {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
601 g_return_val_if_fail (month >= 0 && month <= 12, -1);
603 return month_days[_leap (year)][month];
607 /* ------------------------------ PUBLIC METHODS ---------------------------- */
610 * hildon_date_selector_new:
612 * Creates a new #HildonDateSelector
614 * Returns: a new #HildonDateSelector
617 hildon_date_selector_new ()
619 return g_object_new (HILDON_TYPE_DATE_SELECTOR, NULL);
624 * hildon_date_selector_select_date:
625 * @selector: the #HildonDateSelector
626 * @year: the current year
627 * @month: the current month (0-11)
628 * @day: the current day (1-31, 1-30, 1-29, 1-28) depends on the month
630 * Sets the current active date on the #HildonDateSelector widget
634 hildon_date_selector_select_current_date (HildonDateSelector * selector,
635 guint year, guint month, guint day)
642 min_year = selector->priv->creation_year - INIT_YEAR;
643 max_year = selector->priv->creation_year + LAST_YEAR;
645 g_return_val_if_fail (year > min_year && year < max_year, FALSE);
646 g_return_val_if_fail (month >= 0 && month < 12, FALSE);
648 num_days = _month_days (month, year);
649 g_return_val_if_fail (day > 0 && day <= num_days, FALSE);
652 gtk_tree_model_iter_nth_child (selector->priv->year_model, &iter, NULL,
653 year - selector->priv->creation_year +
655 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
656 selector->priv->year_column, &iter,
659 gtk_tree_model_iter_nth_child (selector->priv->month_model, &iter, NULL,
661 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
662 selector->priv->month_column, &iter,
665 gtk_tree_model_iter_nth_child (selector->priv->day_model, &iter, NULL,
667 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
668 selector->priv->day_column, &iter,
676 * hildon_date_selector_get_date:
677 * @selector: the #HildonDateSelector
678 * @year: to set the current year
679 * @month: to set the current month (0-11)
680 * @day: to the current day (1-31, 1-30, 1-29, 1-28) depends on the month
682 * Gets the current active date on the #HildonDateSelector widget
687 hildon_date_selector_get_date (HildonDateSelector * selector,
688 guint * year, guint * month, guint * day)
693 hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
694 selector->priv->year_column, &iter);
695 gtk_tree_model_get (selector->priv->year_model,
696 &iter, COLUMN_INT, year, -1);
700 hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
701 selector->priv->month_column, &iter);
702 gtk_tree_model_get (selector->priv->month_model,
703 &iter, COLUMN_INT, month, -1);
708 if (hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
709 selector->priv->day_column, &iter))
711 gtk_tree_model_get (selector->priv->day_model,
712 &iter, COLUMN_INT, day, -1);
714 /* *day = *day - 1; */
721 * hildon_date_selector_select_month:
722 * @selector: the #HildonDateSelector
723 * @month: the current month (0-11)
724 * @year: the current year
726 * Modify the current month and year on the current active date
728 * Utility function, too keep this API more similar to the previously existing
729 * hildon-calendar widget.
733 gboolean hildon_date_selector_select_month (HildonDateSelector *selector,
734 guint month, guint year)
738 hildon_date_selector_get_date (selector, NULL, NULL, &day);
740 return hildon_date_selector_select_current_date (selector, year, month, day);
744 * hildon_date_selector_select_day:
745 * @selector: the #HildonDateSelector
746 * @day: the current day (1-31, 1-30, 1-29, 1-28) depends on the month
748 * Modify the current day on the current active date
750 * Utility function, too keep this API more similar to the previously existing
751 * hildon-calendar widget.
756 hildon_date_selector_select_day (HildonDateSelector *selector, guint day)
761 hildon_date_selector_get_date (selector, &year, &month, NULL);
763 hildon_date_selector_select_current_date (selector, year, month, day);