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