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-time-selector
23 * @short_description: A widget to select the current time.
25 * #HildonTimeSelector allows users to choose a time by selecting hour
26 * and minute. It also allows choosing between AM or PM format.
28 * The currently selected time can be altered with
29 * hildon_time_selector_set_time(), and retrieved using
30 * hildon_time_selector_get_time().
32 * Use this widget instead of deprecated HildonTimeEditor widget.
35 #define _GNU_SOURCE /* needed for GNU nl_langinfo_l */
36 #define __USE_GNU /* needed for locale */
38 #ifdef HAVE_SYS_TIME_H
48 #include <gconf/gconf-client.h>
50 #include "hildon-enum-types.h"
51 #include "hildon-time-selector.h"
52 #include "hildon-touch-selector-private.h"
54 #define HILDON_TIME_SELECTOR_GET_PRIVATE(obj) \
55 (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TIME_SELECTOR, HildonTimeSelectorPrivate))
57 G_DEFINE_TYPE (HildonTimeSelector, hildon_time_selector, HILDON_TYPE_TOUCH_SELECTOR)
60 #define LAST_YEAR 50 /* since current year */
62 #define _(String) dgettext("hildon-libs", String)
63 #define N_(String) String
66 /* FIXME: we should get this two props from the clock ui headers */
67 #define CLOCK_GCONF_PATH "/apps/clock"
68 #define CLOCK_GCONF_IS_24H_FORMAT CLOCK_GCONF_PATH "/time-format"
87 PROP_TIME_FORMAT_POLICY
90 struct _HildonTimeSelectorPrivate
92 GtkTreeModel *hours_model;
93 GtkTreeModel *minutes_model;
94 GtkTreeModel *ampm_model;
97 HildonTimeSelectorFormatPolicy format_policy;
98 gboolean ampm_format; /* if using am/pm format or 24 h one */
100 gboolean pm; /* if we are on pm (only useful if ampm_format == TRUE) */
103 gint creation_minutes;
106 static void hildon_time_selector_finalize (GObject * object);
107 static GObject* hildon_time_selector_constructor (GType type,
108 guint n_construct_properties,
109 GObjectConstructParam *construct_params);
110 static void hildon_time_selector_get_property (GObject *object,
114 static void hildon_time_selector_set_property (GObject *object,
119 /* private functions */
120 static GtkTreeModel *_create_hours_model (HildonTimeSelector * selector);
121 static GtkTreeModel *_create_minutes_model (guint minutes_step);
122 static GtkTreeModel *_create_ampm_model (HildonTimeSelector * selector);
124 static void _get_real_time (gint * hours, gint * minutes);
125 static void _manage_ampm_selection_cb (HildonTouchSelector * selector,
126 gint num_column, gpointer data);
127 static void _set_pm (HildonTimeSelector * selector, gboolean pm);
129 static gchar *_custom_print_func (HildonTouchSelector * selector,
133 check_automatic_ampm_format (HildonTimeSelector * selector);
136 update_format_policy (HildonTimeSelector *selector,
137 HildonTimeSelectorFormatPolicy new_policy);
139 update_format_dependant_columns (HildonTimeSelector *selector,
144 hildon_time_selector_class_init (HildonTimeSelectorClass * class)
146 GObjectClass *gobject_class;
147 GtkObjectClass *object_class;
148 GtkWidgetClass *widget_class;
149 GtkContainerClass *container_class;
151 gobject_class = (GObjectClass *) class;
152 object_class = (GtkObjectClass *) class;
153 widget_class = (GtkWidgetClass *) class;
154 container_class = (GtkContainerClass *) class;
157 gobject_class->get_property = hildon_time_selector_get_property;
158 gobject_class->set_property = hildon_time_selector_set_property;
159 gobject_class->constructor = hildon_time_selector_constructor;
160 gobject_class->finalize = hildon_time_selector_finalize;
162 g_object_class_install_property (gobject_class,
164 g_param_spec_uint ("minutes-step",
165 "Step between minutes in the model",
166 "Step between the minutes in the list of"
167 " options of the widget ",
169 G_PARAM_READWRITE|G_PARAM_CONSTRUCT_ONLY));
172 * HildonTimeSelector:time-format-policy:
174 * The visual policy of the time format
178 g_object_class_install_property (gobject_class,
179 PROP_TIME_FORMAT_POLICY,
180 g_param_spec_enum ("time_format_policy",
181 "time format policy",
182 "Visual policy of the time format",
183 HILDON_TYPE_TIME_SELECTOR_FORMAT_POLICY,
184 HILDON_TIME_SELECTOR_FORMAT_POLICY_AUTOMATIC,
185 G_PARAM_READWRITE|G_PARAM_CONSTRUCT));
193 g_type_class_add_private (object_class, sizeof (HildonTimeSelectorPrivate));
196 /* FIXME: the constructor was required because as we need the initial values
197 of the properties passed on g_object_new. But, probably use the method
198 constructed could be easier */
200 hildon_time_selector_constructor (GType type,
201 guint n_construct_properties,
202 GObjectConstructParam *construct_params)
205 HildonTimeSelector *selector;
206 HildonTouchSelectorColumn *column;
208 object = (* G_OBJECT_CLASS (hildon_time_selector_parent_class)->constructor)
209 (type, n_construct_properties, construct_params);
211 selector = HILDON_TIME_SELECTOR (object);
213 selector->priv->hours_model = _create_hours_model (selector);
215 column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
216 selector->priv->hours_model, TRUE);
217 g_object_set (column, "text-column", 0, NULL);
220 /* we need initialization parameters in order to create minute models*/
221 selector->priv->minutes_step = selector->priv->minutes_step ? selector->priv->minutes_step : 1;
223 selector->priv->minutes_model = _create_minutes_model (selector->priv->minutes_step);
225 column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
226 selector->priv->minutes_model, TRUE);
227 g_object_set (column, "text-column", 0, NULL);
229 if (selector->priv->ampm_format) {
230 selector->priv->ampm_model = _create_ampm_model (selector);
232 hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
233 selector->priv->ampm_model, TRUE);
235 g_signal_connect (G_OBJECT (selector),
236 "changed", G_CALLBACK (_manage_ampm_selection_cb),
241 /* By default we should select the current day */
242 hildon_time_selector_set_time (selector,
243 selector->priv->creation_hours,
244 selector->priv->creation_minutes);
251 hildon_time_selector_init (HildonTimeSelector * selector)
253 selector->priv = HILDON_TIME_SELECTOR_GET_PRIVATE (selector);
255 GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
256 gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
258 hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (selector),
261 /* By default we use the automatic ampm format */
262 selector->priv->pm = TRUE;
263 check_automatic_ampm_format (selector);
265 _get_real_time (&selector->priv->creation_hours,
266 &selector->priv->creation_minutes);
270 hildon_time_selector_get_property (GObject *object,
275 HildonTimeSelectorPrivate *priv = HILDON_TIME_SELECTOR_GET_PRIVATE (object);
279 case PROP_MINUTES_STEP:
280 g_value_set_uint (value, priv->minutes_step);
282 case PROP_TIME_FORMAT_POLICY:
283 g_value_set_enum (value, priv->format_policy);
286 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
292 hildon_time_selector_set_property (GObject *object,
297 HildonTimeSelectorPrivate *priv = HILDON_TIME_SELECTOR_GET_PRIVATE (object);
301 case PROP_MINUTES_STEP:
302 priv->minutes_step = g_value_get_uint (value);
304 case PROP_TIME_FORMAT_POLICY:
305 update_format_policy (HILDON_TIME_SELECTOR (object),
306 g_value_get_enum (value));
309 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
315 hildon_time_selector_finalize (GObject * object)
317 /* Note: we don't require to free the models. We don't manage it using own
318 references, so will be freed on the hildon-touch-selector finalize code.
319 See the implementation notes related to that on the touch selector
322 (*G_OBJECT_CLASS (hildon_time_selector_parent_class)->finalize) (object);
325 /* ------------------------------ PRIVATE METHODS ---------------------------- */
328 _custom_print_func (HildonTouchSelector * touch_selector,
331 gchar *result = NULL;
332 struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
333 HildonTimeSelector *selector = NULL;
334 static gchar string[255];
338 selector = HILDON_TIME_SELECTOR (touch_selector);
340 hildon_time_selector_get_time (selector, &hours, &minutes);
345 if (selector->priv->ampm_format) {
346 if (selector->priv->pm) {
347 strftime (string, 255, _("wdgt_va_12h_time_pm"), &tm);
349 strftime (string, 255, _("wdgt_va_12h_time_am"), &tm);
352 strftime (string, 255, _("wdgt_va_24h_time"), &tm);
356 result = g_strdup (string);
361 static GtkTreeModel *
362 _create_minutes_model (guint minutes_step)
364 GtkListStore *store_minutes = NULL;
366 static gchar label[255];
367 struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
370 store_minutes = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
371 for (i = 0; i <= 59; i=i+minutes_step) {
373 strftime (label, 255, _("wdgt_va_minutes"), &tm);
375 gtk_list_store_append (store_minutes, &iter);
376 gtk_list_store_set (store_minutes, &iter,
377 COLUMN_STRING, label, COLUMN_INT, i, -1);
380 return GTK_TREE_MODEL (store_minutes);
383 static GtkTreeModel *
384 _create_hours_model (HildonTimeSelector * selector)
386 GtkListStore *store_hours = NULL;
389 struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
390 static gchar label[255];
391 static gint range_12h[12] = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11};
392 static gint range_24h[24] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
393 12,13,14,15,16,17,18,19,20,21,22,23};
395 gint num_elements = 0;
396 gchar *format_string = NULL;
398 if (selector->priv->ampm_format) {
401 format_string = N_("wdgt_va_12h_hours");
405 format_string = N_("wdgt_va_24h_hours");
408 store_hours = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
409 for (i = 0; i < num_elements; i++) {
410 tm.tm_hour = range[i];
411 strftime (label, 255, _(format_string), &tm);
413 gtk_list_store_append (store_hours, &iter);
414 gtk_list_store_set (store_hours, &iter,
415 COLUMN_STRING, label, COLUMN_INT, range[i], -1);
418 return GTK_TREE_MODEL (store_hours);
421 static GtkTreeModel *
422 _create_ampm_model (HildonTimeSelector * selector)
424 GtkListStore *store_ampm = NULL;
426 static gchar label[255];
428 store_ampm = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
430 snprintf (label, 255, _("wdgt_va_am"));
431 gtk_list_store_append (store_ampm, &iter);
432 gtk_list_store_set (store_ampm, &iter,
433 COLUMN_STRING, label,
436 snprintf (label, 255, _("wdgt_va_pm"));
437 gtk_list_store_append (store_ampm, &iter);
438 gtk_list_store_set (store_ampm, &iter,
439 COLUMN_STRING, label,
442 return GTK_TREE_MODEL (store_ampm);
446 _get_real_time (gint * hours, gint * minutes)
449 struct tm *tm = NULL;
452 tm = localtime (&secs);
455 *hours = tm->tm_hour;
458 if (minutes != NULL) {
459 *minutes = tm->tm_min;
464 _manage_ampm_selection_cb (HildonTouchSelector * touch_selector,
465 gint num_column, gpointer data)
467 HildonTimeSelector *selector = NULL;
471 g_return_if_fail (HILDON_IS_TIME_SELECTOR (touch_selector));
472 selector = HILDON_TIME_SELECTOR (touch_selector);
474 if (num_column == COLUMN_AMPM &&
475 hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
476 COLUMN_AMPM, &iter)) {
477 gtk_tree_model_get (selector->priv->ampm_model, &iter, COLUMN_INT, &pm, -1);
479 selector->priv->pm = pm;
484 check_automatic_ampm_format (HildonTimeSelector * selector)
486 GConfClient *client = NULL;
487 gboolean value = TRUE;
488 GError *error = NULL;
490 client = gconf_client_get_default ();
491 value = gconf_client_get_bool (client, CLOCK_GCONF_IS_24H_FORMAT, &error);
494 ("Error trying to get gconf variable %s, using 24h format by default",
495 CLOCK_GCONF_IS_24H_FORMAT);
496 g_error_free (error);
499 selector->priv->ampm_format = !value;
503 _set_pm (HildonTimeSelector * selector, gboolean pm)
507 selector->priv->pm = pm;
509 if (selector->priv->ampm_model != NULL) {
510 gtk_tree_model_iter_nth_child (selector->priv->ampm_model, &iter, NULL, pm);
512 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
513 COLUMN_AMPM, &iter, FALSE);
518 update_format_policy (HildonTimeSelector *selector,
519 HildonTimeSelectorFormatPolicy new_policy)
521 gboolean prev_ampm_format = FALSE;
522 gint num_columns = -1;
524 num_columns = hildon_touch_selector_get_num_columns (HILDON_TOUCH_SELECTOR (selector));
525 prev_ampm_format = selector->priv->ampm_format;
527 if (new_policy != selector->priv->format_policy) {
531 selector->priv->format_policy = new_policy;
533 /* We get the hour previous all the changes, to avoid problems with the
534 changing widget structure */
535 if (num_columns >= 2) {/* we are on the object construction */
536 hildon_time_selector_get_time (selector, &hours, &minutes);
541 case HILDON_TIME_SELECTOR_FORMAT_POLICY_AMPM:
542 selector->priv->ampm_format = TRUE;
544 case HILDON_TIME_SELECTOR_FORMAT_POLICY_24H:
545 selector->priv->ampm_format = FALSE;
547 case HILDON_TIME_SELECTOR_FORMAT_POLICY_AUTOMATIC:
548 check_automatic_ampm_format (selector);
552 if (prev_ampm_format != selector->priv->ampm_format) {
553 update_format_dependant_columns (selector, hours, minutes);
559 update_format_dependant_columns (HildonTimeSelector *selector,
563 gint num_columns = -1;
565 num_columns = hildon_touch_selector_get_num_columns (HILDON_TOUCH_SELECTOR (selector));
566 if (num_columns < 2) {/* we are on the object construction */
570 /* To avoid an extra and wrong VALUE_CHANGED signal on the model update */
571 hildon_touch_selector_block_changed (HILDON_TOUCH_SELECTOR(selector));
573 selector->priv->hours_model = _create_hours_model (selector);
574 hildon_touch_selector_set_model (HILDON_TOUCH_SELECTOR (selector),
576 selector->priv->hours_model);
578 /* We need to set NOW the correct hour on the hours column, because the number of
579 columns will be updated too, so a signal COLUMNS_CHANGED will be emitted. Some
580 other widget could have connected to this signal and ask for the hour, so this
581 emission could have a wrong hour. We could use a custom func to only modify the
582 hours selection, but hildon_time_selector_time manage yet all the ampm issues
583 to select the correct one */
584 hildon_time_selector_set_time (selector, hours, minutes);
586 /* if we are at this function, we are sure that a change on the number of columns
587 will happen, so check the column number is not required */
588 if (selector->priv->ampm_format) {
589 selector->priv->ampm_model = _create_ampm_model (selector);
591 hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
592 selector->priv->ampm_model, TRUE);
594 g_signal_connect (G_OBJECT (selector),
595 "changed", G_CALLBACK (_manage_ampm_selection_cb),
598 selector->priv->ampm_model = NULL;
599 hildon_touch_selector_remove_column (HILDON_TOUCH_SELECTOR (selector), 2);
602 _set_pm (selector, hours >= 12);
604 hildon_touch_selector_unblock_changed (HILDON_TOUCH_SELECTOR (selector));
606 /* ------------------------------ PUBLIC METHODS ---------------------------- */
609 * hildon_time_selector_new:
611 * Creates a new #HildonTimeSelector
613 * Returns: a new #HildonTimeSelector
618 hildon_time_selector_new ()
620 return g_object_new (HILDON_TYPE_TIME_SELECTOR, NULL);
625 * hildon_time_selector_new_step:
627 * Creates a new #HildonTimeSelector
628 * @minutes_step: step between the minutes we are going to show in the
631 * Returns: a new #HildonTimeSelector
636 hildon_time_selector_new_step (guint minutes_step)
638 return g_object_new (HILDON_TYPE_TIME_SELECTOR, "minutes-step",
643 * hildon_time_selector_set_time
644 * @selector: the #HildonTimeSelector
645 * @hours: the current hour (0-23)
646 * @minutes: the current minute (0-59)
648 * Sets the current active hour on the #HildonTimeSelector widget
650 * The format of the hours accepted is always 24h format, with a range
655 * Returns: %TRUE on success, %FALSE otherwise
658 hildon_time_selector_set_time (HildonTimeSelector * selector,
659 guint hours, guint minutes)
664 g_return_val_if_fail (HILDON_IS_TIME_SELECTOR (selector), FALSE);
665 g_return_val_if_fail (hours <= 23, FALSE);
666 g_return_val_if_fail (minutes <= 59, FALSE);
668 _set_pm (selector, hours >= 12);
670 if (selector->priv->ampm_format) {
671 hours_item = hours - selector->priv->pm * 12;
676 gtk_tree_model_iter_nth_child (selector->priv->hours_model, &iter, NULL,
678 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
679 COLUMN_HOURS, &iter, FALSE);
681 g_assert (selector->priv->minutes_step>0);
682 minutes = minutes/selector->priv->minutes_step;
683 gtk_tree_model_iter_nth_child (selector->priv->minutes_model, &iter, NULL,
685 hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
686 COLUMN_MINUTES, &iter, FALSE);
692 * hildon_time_selector_get_time
693 * @selector: the #HildonTimeSelector
694 * @hours: to set the current hour (0-23)
695 * @minutes: to set the current minute (0-59)
697 * Gets the current active hour on the #HildonTimeSelector widget. Both @year
698 * and @minutes can be NULL.
700 * This method returns the date always in 24h format, with a range (0-23):(0-59)
705 hildon_time_selector_get_time (HildonTimeSelector * selector,
706 guint * hours, guint * minutes)
710 g_return_if_fail (HILDON_IS_TIME_SELECTOR (selector));
713 if (hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
714 COLUMN_HOURS, &iter)) {
716 gtk_tree_model_get (selector->priv->hours_model,
717 &iter, COLUMN_INT, hours, -1);
719 if (selector->priv->ampm_format) {
721 *hours += selector->priv->pm * 12;
725 if (minutes != NULL) {
726 if (hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
727 COLUMN_MINUTES, &iter)) {
728 gtk_tree_model_get (selector->priv->minutes_model,
729 &iter, COLUMN_INT, minutes, -1);