latest update
[hildon] / hildon-widgets / hildon-weekday-picker.c
1 /*
2  * This file is part of hildon-libs
3  *
4  * Copyright (C) 2005 Nokia Corporation.
5  *
6  * Contact: Luc Pionchon <luc.pionchon@nokia.com>
7  *
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.
12  *
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.
17  *
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
21  * 02110-1301 USA
22  *
23  */
24
25 /*
26  * TODO:
27  * - Must check if graphics should be added to widgets weekday
28  *   buttons
29  *
30  */
31
32 /* HILDON DOC
33  * @shortdesc: Weekday Picker is a widget for selecting weekdays
34  * @longdesc: Weekday Picker is a widget for selecting weekdays. 
35  * It shows seven toggle buttons that can have state on or off.
36  *
37  * @seealso: #HildonTimePicker
38  */
39
40  /* GDate numbers days from 1 to 7 and G_DATE_MONDAY is 1st day. However
41     according to locale settings first day is sunday. To get around this
42     problem, we addjust GDate days numbering to be same as locale
43     numbering */
44
45 #ifdef HAVE_CONFIG_H
46 #include <config.h>
47 #endif
48
49 #include <stdio.h>
50 #include <stdarg.h>
51 #include <sys/types.h>
52 #include <unistd.h>
53 #include <libintl.h>
54 #include <langinfo.h>
55 #include <time.h>
56 #include <gtk/gtksignal.h>
57 #include <gdk/gdkkeysyms.h>
58 #include <gtk/gtktogglebutton.h>
59 #include <gtk/gtksizegroup.h>
60 #include <gtk/gtkwindow.h>
61 #include "hildon-weekday-picker.h"
62 #include "hildon-composite-widget.h"
63
64 #define _(string) dgettext(PACKAGE, string)
65
66 #define HILDON_WEEKDAY_PICKER_GET_PRIVATE(obj) \
67     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
68      HILDON_WEEKDAY_PICKER_TYPE, HildonWeekdayPickerPrivate));
69
70 static GtkContainerClass *parent_class;
71
72 typedef struct _HildonWeekdayPickerPrivate HildonWeekdayPickerPrivate;
73
74 static void
75 hildon_weekday_picker_class_init(HildonWeekdayPickerClass * picker_class);
76
77 static void 
78 hildon_weekday_picker_init(HildonWeekdayPicker * picker);
79
80 static void
81 hildon_weekday_picker_size_allocate(GtkWidget * widget,
82                                     GtkAllocation * allocation);
83
84 static void
85 hildon_weekday_picker_size_request(GtkWidget * widget,
86                                    GtkRequisition * requisition);
87
88 static void
89 hildon_weekday_picker_forall(GtkContainer * container,
90                              gboolean include_internals,
91                              GtkCallback callback, gpointer callback_data);
92
93 static void
94 hildon_weekday_picker_destroy(GtkObject * self);
95
96 static gboolean
97 hildon_weekday_picker_mnemonic_activate(GtkWidget *widget,
98                                         gboolean group_cycling);
99
100 static void
101 button_toggle(GtkToggleButton * togglebutton, gpointer data);
102
103 struct _HildonWeekdayPickerPrivate {
104     guint dayorder[8];  /* Lookup table for weekdays */
105     GtkWidget *buttons[8];      /* weekday buttons */
106     guint days;
107     guint last_index;
108 };
109
110 enum {
111   SELECTION_CHANGED_SIGNAL,
112   LAST_SIGNAL
113 };
114
115 static guint signals[LAST_SIGNAL] = { 0 };
116
117 GType hildon_weekday_picker_get_type(void)
118 {
119     static GType picker_type = 0;
120
121     if (!picker_type) {
122         static const GTypeInfo picker_info = {
123             sizeof(HildonWeekdayPickerClass),
124             NULL,       /* base_init */
125             NULL,       /* base_finalize */
126             (GClassInitFunc) hildon_weekday_picker_class_init,
127             NULL,       /* class_finalize */
128             NULL,       /* class_data */
129             sizeof(HildonWeekdayPicker),
130             0,  /* n_preallocs */
131             (GInstanceInitFunc) hildon_weekday_picker_init,
132         };
133         picker_type = g_type_register_static(GTK_TYPE_CONTAINER,
134                                              "HildonWeekdayPicker",
135                                              &picker_info, 0);
136     }
137     return picker_type;
138 }
139
140 static void
141 hildon_weekday_picker_class_init(HildonWeekdayPickerClass * picker_class)
142 {
143     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(picker_class);
144     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(picker_class);
145     GObjectClass *object_class = G_OBJECT_CLASS(picker_class);
146
147     parent_class = g_type_class_peek_parent(picker_class);
148
149     g_type_class_add_private(picker_class,
150                              sizeof(HildonWeekdayPickerPrivate));
151
152     widget_class->mnemonic_activate = hildon_weekday_picker_mnemonic_activate;
153     widget_class->size_request = hildon_weekday_picker_size_request;
154     widget_class->size_allocate = hildon_weekday_picker_size_allocate;
155     widget_class->focus = hildon_composite_widget_focus;
156     container_class->forall = hildon_weekday_picker_forall;
157     GTK_OBJECT_CLASS(picker_class)->destroy =
158         hildon_weekday_picker_destroy;
159
160     signals[SELECTION_CHANGED_SIGNAL] = g_signal_new("selection_changed",
161                                                 G_OBJECT_CLASS_TYPE
162                                                 (object_class),
163                                                 G_SIGNAL_RUN_LAST |
164                                                 G_SIGNAL_ACTION,
165                                                 G_STRUCT_OFFSET
166                                                 (HildonWeekdayPickerClass,
167                                                  selection_changed), NULL, NULL,
168                                                 gtk_marshal_VOID__INT,
169                                                 G_TYPE_NONE, 1, G_TYPE_INT);
170 }
171
172 static void
173 hildon_weekday_picker_init(HildonWeekdayPicker * picker)
174 {
175     HildonWeekdayPickerPrivate *priv;
176     gint i, day;
177     const gchar *wdays[8];
178     GtkSizeGroup *sgroup;
179
180     sgroup = gtk_size_group_new(GTK_SIZE_GROUP_BOTH);
181
182     /* NOTE: we query locales with "first_weekday" parameter, but this
183        seems to be broken on some installations. e.g. on my system I
184        receive "1" (Sunday) to be first day of the week on "fi_FI"
185        locale, when it should be "2" (Monday). */
186
187     /* Check our first weekday */
188     day = *nl_langinfo(_NL_TIME_FIRST_WEEKDAY);
189
190     wdays[1] = nl_langinfo(ABDAY_1);
191     wdays[2] = nl_langinfo(ABDAY_2);
192     wdays[3] = nl_langinfo(ABDAY_3);
193     wdays[4] = nl_langinfo(ABDAY_4);
194     wdays[5] = nl_langinfo(ABDAY_5);
195     wdays[6] = nl_langinfo(ABDAY_6);
196     wdays[7] = nl_langinfo(ABDAY_7);
197
198     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
199
200     for (i = 1; i <= 7; i++) {
201         priv->buttons[i] = gtk_toggle_button_new_with_label(wdays[day]);
202         priv->dayorder[i] = day++;
203
204         if (day > 7)
205             day = 1;
206
207         g_signal_connect(GTK_WIDGET(priv->buttons[i]),
208                          "toggled", G_CALLBACK(button_toggle), picker);
209
210         priv->last_index = i;
211         gtk_size_group_add_widget(sgroup, priv->buttons[i]);
212
213         gtk_widget_set_parent(priv->buttons[i], GTK_WIDGET(picker));
214         gtk_widget_show(priv->buttons[i]);
215     }
216
217     GTK_WIDGET_SET_FLAGS(picker, GTK_NO_WINDOW);
218 }
219
220 /**
221  * hildon_weekday_picker_new:
222  *
223  * Creates a new #HildonWeekdayPicker.
224  *
225  * Return value: Pointer to a new @HildonWeekdayPicker widget.
226  */
227 GtkWidget *hildon_weekday_picker_new(void)
228 {
229     return g_object_new(HILDON_WEEKDAY_PICKER_TYPE, NULL);
230 }
231
232 static gboolean
233 hildon_weekday_picker_mnemonic_activate(GtkWidget *widget,
234                                         gboolean group_cycling)
235 {
236   HildonWeekdayPickerPrivate *priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(widget);
237   gtk_widget_grab_focus(priv->buttons[1]);
238   return TRUE;
239 }
240
241 static void 
242 hildon_weekday_picker_forall(GtkContainer * container,
243                              gboolean include_internals, GtkCallback callback,
244                              gpointer callback_data)
245 {
246     HildonWeekdayPicker *picker;
247     HildonWeekdayPickerPrivate *priv;
248     gint i;
249
250     g_return_if_fail(container);
251     g_return_if_fail(callback);
252
253     picker = HILDON_WEEKDAY_PICKER(container);
254     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
255
256     if (!include_internals)
257         return;
258
259     for (i = 1; i <= 7; ++i) {
260         (*callback) (priv->buttons[i], callback_data);
261     }
262
263 }
264
265 static void 
266 hildon_weekday_picker_destroy(GtkObject * self)
267 {
268     HildonWeekdayPickerPrivate *priv;
269     gint i;
270
271     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(self);
272
273     for (i = 1; i <= 7; ++i) {
274       if (priv->buttons[i])
275       {
276         gtk_widget_unparent(priv->buttons[i]);
277         priv->buttons[i] = NULL;
278       }
279     }
280
281     if (GTK_OBJECT_CLASS(parent_class)->destroy)
282         GTK_OBJECT_CLASS(parent_class)->destroy(self);
283
284 }
285
286 static void 
287 hildon_weekday_picker_size_request(GtkWidget * widget,
288                                    GtkRequisition *requisition)
289 {
290     HildonWeekdayPicker *picker;
291     HildonWeekdayPickerPrivate *priv;
292     gint i;
293     GtkRequisition req;
294
295     picker = HILDON_WEEKDAY_PICKER(widget);
296     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
297     requisition->width = 0;
298     requisition->height = 0;
299
300     for (i = 1; i <= 7; ++i) {
301         gtk_widget_size_request(priv->buttons[i], &req);
302         requisition->width += req.width;
303         if (req.height > requisition->height)
304             requisition->height = req.height;
305
306     }
307 }
308
309 static void 
310 hildon_weekday_picker_size_allocate(GtkWidget * widget,
311                                                 GtkAllocation * allocation)
312 {
313     HildonWeekdayPicker *picker;
314     HildonWeekdayPickerPrivate *priv;
315     gint i;
316     GtkAllocation alloc;
317     GtkRequisition child_requisition;
318     gint header_x;
319     guint sval;
320     GtkTextDirection direction;
321
322     g_return_if_fail(widget);
323     g_return_if_fail(allocation);
324
325     /* Check orientation */
326     direction = gtk_widget_get_direction(widget);
327
328     picker = HILDON_WEEKDAY_PICKER(widget);
329     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
330     header_x = allocation->x;
331     widget->allocation = *allocation;
332
333     if (direction == GTK_TEXT_DIR_LTR || direction == GTK_TEXT_DIR_NONE)
334         sval = 1;
335     else
336         sval = 7;
337     for (i = 1; i <= 7; ++i) {
338         gtk_widget_get_child_requisition(priv->buttons[sval],
339                                          &child_requisition);
340
341         alloc.x = header_x;
342         alloc.y = allocation->y;
343         alloc.width = child_requisition.width;
344         alloc.height = child_requisition.height;
345         header_x += alloc.width;
346         gtk_widget_size_allocate(priv->buttons[sval], &alloc);
347         if (direction == GTK_TEXT_DIR_RTL)
348             sval--;
349         else
350             sval++;
351     }
352 }
353
354 static void
355 button_toggle(GtkToggleButton * button, gpointer data)
356 {
357     HildonWeekdayPicker *picker;
358     HildonWeekdayPickerPrivate *priv;
359     gint i;
360     static guint wdays[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
361
362     g_return_if_fail(button);
363     g_return_if_fail(data);
364
365     picker = HILDON_WEEKDAY_PICKER(data);
366     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
367
368     for (i = 1; i <= 7; ++i) {
369         if (GTK_WIDGET(button) == priv->buttons[i]) {
370             priv->days ^= wdays[i];
371             g_signal_emit (GTK_WIDGET(picker), signals[SELECTION_CHANGED_SIGNAL],
372                    0, priv->dayorder[i]);
373             break;
374         }
375     }
376 }
377
378 /**
379  * hildon_weekday_picker_set_day:
380  * @picker: the @HildonWeekdayPicker widget
381  * @day: Day to be set
382  *
383  * Select specified weekday.
384  */
385 void 
386 hildon_weekday_picker_set_day(HildonWeekdayPicker * picker,
387                                    GDateWeekday day)
388 {
389     HildonWeekdayPickerPrivate *priv;
390     static guint wdays[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
391     guint i;
392
393     g_return_if_fail(picker);
394     g_return_if_fail(day >= 1 && day <= 7);
395
396     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
397
398     if (day == G_DATE_SUNDAY)
399         day = G_DATE_MONDAY;
400     else
401         day++;
402
403     for (i = 1; i <= 7; i++) {
404         if (priv->dayorder[i] == day) {
405             if (!(priv->days & wdays[i]))
406                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
407                                              (priv->buttons[i]), TRUE);
408         }
409     }
410 }
411
412 /**
413  * hildon_weekday_picker_unset_day:
414  * @picker: the @HildonWeekdayPicker widget
415  * @day: Day to be unset
416  *
417  * Unselect specified weekday.
418  */
419 void 
420 hildon_weekday_picker_unset_day(HildonWeekdayPicker * picker,
421                                      GDateWeekday day)
422 {
423     HildonWeekdayPickerPrivate *priv;
424     static guint wdays[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
425     guint i;
426
427     g_return_if_fail(picker);
428     g_return_if_fail(day >= 1 && day <= 7);
429
430     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
431
432     if (day == G_DATE_SUNDAY)
433         day = G_DATE_MONDAY;
434     else
435         day++;
436
437     for (i = 1; i <= 7; i++) {
438         if (priv->dayorder[i] == day) {
439             if (priv->days & wdays[i])
440                 gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
441                                              (priv->buttons[i]), FALSE);
442         }
443     }
444 }
445
446 /**
447  * hildon_weekday_picker_toggle_day:
448  * @picker: the @HildonWeekdayPicker widget
449  * @day: Day to be toggled
450  *
451  * Toggle current status of the specified weekday.
452  */
453 void 
454 hildon_weekday_picker_toggle_day(HildonWeekdayPicker * picker,
455                                       GDateWeekday day)
456 {
457     HildonWeekdayPickerPrivate *priv;
458     guint i;
459     gboolean is_active;
460    
461     g_return_if_fail(picker);
462     g_return_if_fail(day >= 1 && day <= 7);
463
464     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
465
466     if (day == G_DATE_SUNDAY)
467         day = G_DATE_MONDAY;
468     else
469         day++;
470
471     for (i = 1; i <= 7; i++) {
472         if (priv->dayorder[i] == day) {
473             is_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
474                                         (priv->buttons[i]));
475             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
476                                          (priv->buttons[i]),
477                                          is_active ? FALSE : TRUE);
478         }
479     }
480 }
481
482 /**
483  * hildon_weekday_picker_set_all:
484  * @picker: the @HildonWeekdayPicker widget
485  *
486  * Set all weekdays.
487  */
488 void 
489 hildon_weekday_picker_set_all(HildonWeekdayPicker * picker)
490 {
491     HildonWeekdayPickerPrivate *priv;
492     gint i;
493     static guint wdays[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
494
495     g_return_if_fail(picker);
496
497     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
498
499     for (i = 1; i <= 7; i++) {
500         if (!(priv->days & wdays[i]))
501             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
502                                          (priv->buttons[i]), TRUE);
503     }
504 }
505
506 /**
507  * hildon_weekday_picker_unset_all:
508  * @picker: the @HildonWeekdayPicker widget
509  *
510  * Unset all weekdays.
511  */
512 void 
513 hildon_weekday_picker_unset_all(HildonWeekdayPicker * picker)
514 {
515     HildonWeekdayPickerPrivate *priv;
516     gint i;
517     static guint wdays[] = { 1, 2, 4, 8, 16, 32, 64, 128 };
518
519     g_return_if_fail(picker);
520
521     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
522
523     for (i = 1; i <= 7; i++) {
524         if (priv->days & wdays[i])
525             gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
526                                          (priv->buttons[i]), FALSE);
527     }
528 }
529
530 /**
531  * hildon_weekday_picker_set_all:
532  * @picker: the @HildonWeekdayPicker widget
533  * @day: Day to be checked.
534  *
535  * Check if the specified weekday is set.
536  *
537  * Return value: TRUE if the day is set. 
538  */
539 gboolean 
540 hildon_weekday_picker_isset_day(HildonWeekdayPicker * picker,
541                                          GDateWeekday day)
542 {
543     HildonWeekdayPickerPrivate *priv;
544     guint i;
545
546     g_return_val_if_fail(picker, FALSE);
547     g_return_val_if_fail(day >= 1 && day <= 7, FALSE);
548
549     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
550
551     if (day == G_DATE_SUNDAY)
552         day = G_DATE_MONDAY;
553     else
554         day++;
555
556     for (i = 1; i <= 7; i++) {
557         if ( priv->dayorder[i] == day && 
558              gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON
559                                          (priv->buttons[i]))) {
560              return TRUE;
561         }
562     }
563     return FALSE;
564 }