05334ce79ca45155e93e23fac7948d11deaa6991
[hildon] / src / hildon-time-selector.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2008 Nokia Corporation.
5  *
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.
10  *
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.
15  *
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.
19  */
20
21 /**
22  * SECTION:hildon-time-selector
23  * @short_description: A widget to select the current time.
24  *
25  * HildonTimeSelector is a time widget, equivalent to hildon-calendar, but with a multi-column
26  * approach
27  *
28  */
29
30 #define _GNU_SOURCE     /* needed for GNU nl_langinfo_l */
31 #define __USE_GNU       /* needed for locale */
32
33 #ifdef HAVE_SYS_TIME_H
34 #include <sys/time.h>
35 #endif
36
37 #include <string.h>
38 #include <stdlib.h>
39 #include <libintl.h>
40 #include <time.h>
41 #include <langinfo.h>
42 #include <locale.h>
43 #include <gconf/gconf-client.h>
44
45 #include "hildon-time-selector.h"
46
47 #define HILDON_TIME_SELECTOR_GET_PRIVATE(obj)                           \
48   (G_TYPE_INSTANCE_GET_PRIVATE ((obj), HILDON_TYPE_TIME_SELECTOR, HildonTimeSelectorPrivate))
49
50 G_DEFINE_TYPE (HildonTimeSelector, hildon_time_selector, HILDON_TYPE_TOUCH_SELECTOR)
51
52 #define INIT_YEAR 100
53 #define LAST_YEAR 50    /* since current year */
54
55 #define _(String)  dgettext("hildon-libs", String)
56 #define N_(String) String
57
58
59 /* FIXME: we should get this two props from the clock ui headers */
60 #define CLOCK_GCONF_PATH "/apps/clock"
61 #define CLOCK_GCONF_IS_24H_FORMAT CLOCK_GCONF_PATH  "/time-format"
62
63 enum {
64   COLUMN_STRING,
65   COLUMN_INT,
66   TOTAL_MODEL_COLUMNS
67 };
68
69 enum {
70   COLUMN_HOURS = 0,
71   COLUMN_MINUTES,
72   COLUMN_AMPM,
73   TOTAL_TIME_COLUMNS
74 };
75
76 struct _HildonTimeSelectorPrivate
77 {
78   GtkTreeModel *hours_model;
79   GtkTreeModel *minutes_model;
80   GtkTreeModel *ampm_model;
81
82   gboolean ampm_format;         /* if using am/pm format or 24 h one */
83
84   gboolean pm;                  /* if we are on pm (only useful if ampm_format == TRUE) */
85
86   gint creation_hours;
87   gint creation_minutes;
88 };
89
90 static void hildon_time_selector_finalize (GObject * object);
91
92 /* private functions */
93 static GtkTreeModel *_create_hours_model (HildonTimeSelector * selector);
94 static GtkTreeModel *_create_minutes_model (HildonTimeSelector * selector);
95 static GtkTreeModel *_create_ampm_model (HildonTimeSelector * selector);
96
97 static void _get_real_time (gint * hours, gint * minutes);
98 static void _manage_ampm_selection_cb (HildonTouchSelector * selector,
99                                        gint num_column, gpointer data);
100 static void _check_ampm_format (HildonTimeSelector * selector);
101 static void _set_pm (HildonTimeSelector * selector, gboolean pm);
102
103 static gchar *_custom_print_func (HildonTouchSelector * selector);
104
105 static void
106 hildon_time_selector_class_init (HildonTimeSelectorClass * class)
107 {
108   GObjectClass *gobject_class;
109   GtkObjectClass *object_class;
110   GtkWidgetClass *widget_class;
111   GtkContainerClass *container_class;
112
113   gobject_class = (GObjectClass *) class;
114   object_class = (GtkObjectClass *) class;
115   widget_class = (GtkWidgetClass *) class;
116   container_class = (GtkContainerClass *) class;
117
118   /* GObject */
119   gobject_class->finalize = hildon_time_selector_finalize;
120
121   /* GtkWidget */
122
123   /* GtkContainer */
124
125   /* signals */
126
127   g_type_class_add_private (object_class, sizeof (HildonTimeSelectorPrivate));
128 }
129
130
131 static void
132 hildon_time_selector_init (HildonTimeSelector * selector)
133 {
134   HildonTouchSelectorColumn *column = NULL;
135
136   selector->priv = HILDON_TIME_SELECTOR_GET_PRIVATE (selector);
137
138   GTK_WIDGET_SET_FLAGS (GTK_WIDGET (selector), GTK_NO_WINDOW);
139   gtk_widget_set_redraw_on_allocate (GTK_WIDGET (selector), FALSE);
140
141   hildon_touch_selector_set_print_func (HILDON_TOUCH_SELECTOR (selector),
142                                         _custom_print_func);
143
144   _get_real_time (&selector->priv->creation_hours,
145                   &selector->priv->creation_minutes);
146
147   _check_ampm_format (selector);
148
149   selector->priv->hours_model = _create_hours_model (selector);
150   selector->priv->minutes_model = _create_minutes_model (selector);
151
152   column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
153                                                      selector->priv->hours_model, TRUE);
154   g_object_set (column, "text-column", 0, NULL);
155
156   column = hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
157                                                      selector->priv->minutes_model, TRUE);
158   g_object_set (column, "text-column", 0, NULL);
159
160   if (selector->priv->ampm_format) {
161     selector->priv->ampm_model = _create_ampm_model (selector);
162
163     hildon_touch_selector_append_text_column (HILDON_TOUCH_SELECTOR (selector),
164                                               selector->priv->ampm_model, TRUE);
165
166     g_signal_connect (G_OBJECT (selector),
167                       "changed", G_CALLBACK (_manage_ampm_selection_cb),
168                       NULL);
169   }
170
171   /* By default we should select the current day */
172   hildon_time_selector_set_time (selector,
173                                  selector->priv->creation_hours,
174                                  selector->priv->creation_minutes);
175
176 }
177
178 static void
179 hildon_time_selector_finalize (GObject * object)
180 {
181   HildonTimeSelector *selector = NULL;
182
183   selector = HILDON_TIME_SELECTOR (object);
184
185   /* FIXME: FILL ME !! */
186
187   (*G_OBJECT_CLASS (hildon_time_selector_parent_class)->finalize) (object);
188 }
189
190 /* ------------------------------ PRIVATE METHODS ---------------------------- */
191
192 static gchar *
193 _custom_print_func (HildonTouchSelector * touch_selector)
194 {
195   gchar *result = NULL;
196   struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
197   HildonTimeSelector *selector = NULL;
198   static gchar string[255];
199   guint hours = 0;
200   guint minutes = 0;
201
202   selector = HILDON_TIME_SELECTOR (touch_selector);
203
204   hildon_time_selector_get_time (selector, &hours, &minutes);
205
206   tm.tm_min = minutes;
207   tm.tm_hour = hours;
208
209   if (selector->priv->ampm_format) {
210     if (selector->priv->pm) {
211       strftime (string, 255, _("wdgt_va_12h_time_pm"), &tm);
212     } else {
213       strftime (string, 255, _("wdgt_va_12h_time_am"), &tm);
214     }
215   } else {
216     strftime (string, 255, _("wdgt_va_24h_time"), &tm);
217   }
218
219
220   result = g_strdup (string);
221
222   return result;
223 }
224
225 static GtkTreeModel *
226 _create_minutes_model (HildonTimeSelector * selector)
227 {
228   GtkListStore *store_minutes = NULL;
229   gint i = 0;
230   static gchar label[255];
231   struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
232   GtkTreeIter iter;
233
234   store_minutes = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
235   for (i = 0; i <= 59; i++) {
236     tm.tm_min = i;
237     strftime (label, 255, _("wdgt_va_minutes"), &tm);
238
239     gtk_list_store_append (store_minutes, &iter);
240     gtk_list_store_set (store_minutes, &iter,
241                         COLUMN_STRING, label, COLUMN_INT, i, -1);
242   }
243
244   return GTK_TREE_MODEL (store_minutes);
245 }
246
247 static GtkTreeModel *
248 _create_hours_model (HildonTimeSelector * selector)
249 {
250   GtkListStore *store_hours = NULL;
251   gint i = 0;
252   GtkTreeIter iter;
253   struct tm tm = { 0, 0, 0, 0, 0, 0, 0, 0, 0 };
254   static gchar label[255];
255   static gint range_12h[12] = {12, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11};
256   static gint range_24h[24] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,
257                                12,13,14,15,16,17,18,19,20,21,22,23};
258   gint *range = NULL;
259   gint num_elements = 0;
260   gchar *format_string = NULL;
261
262   if (selector->priv->ampm_format) {
263     range = range_12h;
264     num_elements = 12;
265     format_string = N_("wdgt_va_12h_hours");
266   } else {
267     range = range_24h;
268     num_elements = 24;
269     format_string = N_("wdgt_va_24h_hours");
270   }
271
272   store_hours = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
273   for (i = 0; i < num_elements; i++) {
274     tm.tm_hour = range[i];
275     strftime (label, 255, _(format_string), &tm);
276
277     gtk_list_store_append (store_hours, &iter);
278     gtk_list_store_set (store_hours, &iter,
279                         COLUMN_STRING, label, COLUMN_INT, range[i], -1);
280   }
281
282   return GTK_TREE_MODEL (store_hours);
283 }
284
285 static GtkTreeModel *
286 _create_ampm_model (HildonTimeSelector * selector)
287 {
288   GtkListStore *store_ampm = NULL;
289   GtkTreeIter iter;
290   static gchar label[255];
291
292   store_ampm = gtk_list_store_new (2, G_TYPE_STRING, G_TYPE_INT);
293
294   snprintf (label, 255, _("wdgt_va_am"));
295   gtk_list_store_append (store_ampm, &iter);
296   gtk_list_store_set (store_ampm, &iter,
297                       COLUMN_STRING, label,
298                       COLUMN_INT, 0, -1);
299
300   snprintf (label, 255, _("wdgt_va_pm"));
301   gtk_list_store_append (store_ampm, &iter);
302   gtk_list_store_set (store_ampm, &iter,
303                       COLUMN_STRING, label,
304                       COLUMN_INT, 1, -1);
305
306   return GTK_TREE_MODEL (store_ampm);
307 }
308
309 static void
310 _get_real_time (gint * hours, gint * minutes)
311 {
312   time_t secs;
313   struct tm *tm = NULL;
314
315   secs = time (NULL);
316   tm = localtime (&secs);
317
318   if (hours != NULL) {
319     *hours = tm->tm_hour;
320   }
321
322   if (minutes != NULL) {
323     *minutes = tm->tm_min;
324   }
325 }
326
327 static void
328 _manage_ampm_selection_cb (HildonTouchSelector * touch_selector,
329                            gint num_column, gpointer data)
330 {
331   HildonTimeSelector *selector = NULL;
332   gint pm;
333   GtkTreeIter iter;
334
335   g_return_if_fail (HILDON_IS_TIME_SELECTOR (touch_selector));
336   selector = HILDON_TIME_SELECTOR (touch_selector);
337
338   if (num_column == COLUMN_AMPM) {
339     hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
340                                         COLUMN_AMPM, &iter);
341     gtk_tree_model_get (selector->priv->ampm_model, &iter, COLUMN_INT, &pm, -1);
342
343     selector->priv->pm = pm;
344   }
345 }
346
347 static void
348 _check_ampm_format (HildonTimeSelector * selector)
349 {
350   GConfClient *client = NULL;
351   gboolean value = TRUE;
352   GError *error = NULL;
353
354   client = gconf_client_get_default ();
355   value = gconf_client_get_bool (client, CLOCK_GCONF_IS_24H_FORMAT, &error);
356   if (error != NULL) {
357     g_warning
358       ("Error trying to get gconf variable %s, using 24h format by default",
359        CLOCK_GCONF_IS_24H_FORMAT);
360     g_error_free (error);
361   }
362
363   selector->priv->ampm_format = !value;
364   selector->priv->pm = TRUE;
365 }
366
367 static void
368 _set_pm (HildonTimeSelector * selector, gboolean pm)
369 {
370   GtkTreeIter iter;
371
372   selector->priv->pm = pm;
373
374   gtk_tree_model_iter_nth_child (selector->priv->ampm_model, &iter, NULL, pm);
375
376   hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
377                                      COLUMN_AMPM, &iter, FALSE);
378 }
379
380 /* ------------------------------ PUBLIC METHODS ---------------------------- */
381
382 /**
383  * hildon_time_selector_new:
384  *
385  * Creates a new #HildonTimeSelector
386  *
387  * Returns: a new #HildonTimeSelector
388  *
389  * Since: 2.2
390  **/
391 GtkWidget *
392 hildon_time_selector_new ()
393 {
394   return g_object_new (HILDON_TYPE_TIME_SELECTOR, NULL);
395 }
396
397 /**
398  * hildon_time_selector_set_time
399  * @selector: the #HildonTimeSelector
400  * @hours:  the current hour (0-23)
401  * @minutes: the current minute (0-59)
402  *
403  * Sets the current active hour on the #HildonTimeSelector widget
404  *
405  * The format of the hours acceptes is always 24h format, with a range
406  * (0-23):(0-59).
407  *
408  * Since: 2.2
409  **/
410 gboolean
411 hildon_time_selector_set_time (HildonTimeSelector * selector,
412                                guint hours, guint minutes)
413 {
414   GtkTreeIter iter;
415   gint hours_item = 0;
416
417   g_return_val_if_fail (hours >= 0 && hours <= 23, FALSE);
418   g_return_val_if_fail (minutes >= 0 && minutes <= 59, FALSE);
419
420   if (selector->priv->ampm_format) {
421     _set_pm (selector, hours >= 12);
422
423     hours_item = hours - selector->priv->pm * 12;
424   } else {
425     hours_item = hours;
426   }
427
428   gtk_tree_model_iter_nth_child (selector->priv->hours_model, &iter, NULL,
429                                  hours_item);
430   hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
431                                      COLUMN_HOURS, &iter, FALSE);
432
433   gtk_tree_model_iter_nth_child (selector->priv->minutes_model, &iter, NULL,
434                                  minutes);
435   hildon_touch_selector_select_iter (HILDON_TOUCH_SELECTOR (selector),
436                                      COLUMN_MINUTES, &iter, FALSE);
437
438   return TRUE;
439 }
440
441 /**
442  * hildon_time_selector_get_time
443  * @selector: the #HildonTimeSelector
444  * @hours:  to set the current hour (0-23)
445  * @minutes: to set the current minute (0-59)
446  *
447  * Gets the current active hour on the #HildonTimeSelector widget. Both @year
448  * and @minutes can be NULL.
449  *
450  * This method returns the date always in 24h format, with a range (0-23):(0-59)
451  *
452  * Since: 2.2
453  **/
454 void
455 hildon_time_selector_get_time (HildonTimeSelector * selector,
456                                guint * hours, guint * minutes)
457 {
458   GtkTreeIter iter;
459
460   if (hours != NULL) {
461     hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
462                                         COLUMN_HOURS, &iter);
463     gtk_tree_model_get (selector->priv->hours_model,
464                         &iter, COLUMN_INT, hours, -1);
465     if (selector->priv->ampm_format) {
466       *hours %= 12;
467       *hours += selector->priv->pm * 12;
468     }
469   }
470
471   if (minutes != NULL) {
472     hildon_touch_selector_get_selected (HILDON_TOUCH_SELECTOR (selector),
473                                         COLUMN_MINUTES, &iter);
474     gtk_tree_model_get (selector->priv->minutes_model,
475                         &iter, COLUMN_INT, minutes, -1);
476   }
477 }