2008-09-15 Alberto Garcia <agarcia@igalia.com>
[hildon] / src / hildon-weekday-picker.c
1 /*
2  * This file is a part of hildon
3  *
4  * Copyright (C) 2005, 2006 Nokia Corporation, all rights reserved.
5  *
6  * Contact: Michael Dominic Kostrzewa <michael.kostrzewa@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; 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  * SECTION:hildon-weekday-picker
27  * @short_description: A widget for picking days on which a certain event 
28  * should take place.
29  * @see_also: #HildonWeekdayPicker 
30  *
31  * #HildonWeekdayPicker supports non-mutually exclusive selection of days of 
32  * the week. Selected days of the week are shown with a pushed-in effect.
33  * 
34  * #HildonWeekdayPicker is used where users are required to pick days on which 
35  * a certain event should take place, for example, which days a Calendar event 
36  * should be repeated on. It is used in Calendar in the Repeat dialog, in Tasks 
37  * in the Repeat dialog and in the Email set-up wizard.
38  * 
39  * <example>
40  * <title>HildonWeekdayPicker example</title>
41  * <programlisting>
42  * gint i;
43  * HildonWeekdayPicker *picker = hildon_weekday_picker_new ();
44  * <!-- -->
45  * hildon_weekday_picker_set_day (picker, i);
46  * hildon_weekday_picker_unset_day (picker, i);
47  * hildon_weekday_picker_toggle_day (picker, i);
48  * hildon_weekday_picker_set_all (picker);
49  * <!-- -->
50  * hildon_weekday_picker_unset_all( picker );
51  * </programlisting>
52  * </example>
53  *
54  */  
55  
56  /* GDate numbers days from 1 to 7 and G_DATE_MONDAY is 1st day. However
57     according to locale settings first day is sunday. To get around this
58     problem, we addjust GDate days numbering to be same as locale
59     numbering */
60
61 #ifdef                                          HAVE_CONFIG_H
62 #include                                        <config.h>
63 #endif
64
65 #include                                        <stdio.h>
66 #include                                        <stdarg.h>
67 #include                                        <sys/types.h>
68 #include                                        <unistd.h>
69 #include                                        <libintl.h>
70 #include                                        <langinfo.h>
71 #include                                        <time.h>
72 #include                                        <gdk/gdkkeysyms.h>
73
74 #include                                        "hildon-weekday-picker.h"
75 #include                                        "hildon-private.h"
76 #include                                        "hildon-weekday-picker-private.h"
77
78 static GtkContainerClass*                       parent_class;
79
80 static void
81 hildon_weekday_picker_class_init                (HildonWeekdayPickerClass *picker_class);
82
83 static void 
84 hildon_weekday_picker_init                      (HildonWeekdayPicker *picker);
85
86 static void
87 hildon_weekday_picker_size_allocate             (GtkWidget *widget,
88                                                  GtkAllocation *allocation);
89 static gboolean
90 hildon_weekday_picker_focus                     (GtkWidget *widget,
91                                                  GtkDirectionType direction);
92 static void
93 hildon_weekday_picker_size_request              (GtkWidget *widget,
94                                                  GtkRequisition *requisition);
95 static void
96 hildon_weekday_picker_forall                    (GtkContainer *container,
97                                                  gboolean include_internals,
98                                                  GtkCallback callback, 
99                                                  gpointer callback_data);
100
101 static void
102 hildon_weekday_picker_destroy                   (GtkObject *self);
103
104 static void
105 button_toggle                                   (GtkToggleButton *togglebutton, 
106                                                  gpointer wpicker);
107
108 enum 
109 {
110     SELECTION_CHANGED_SIGNAL,
111     LAST_SIGNAL
112 };
113
114 static guint                                    signals [LAST_SIGNAL] = { 0 } ;
115
116 /**
117  * hildon_weekday_picker_get_type:
118  *
119  * Initializes and returns the type of a hildon weekday picker.
120  *
121  * @Returns: GType of #HildonWeekdayPicker
122  */
123 GType G_GNUC_CONST
124 hildon_weekday_picker_get_type                  (void)
125 {
126     static GType picker_type = 0;
127
128     if (! picker_type) {
129         static const GTypeInfo picker_info = {
130             sizeof (HildonWeekdayPickerClass),
131             NULL,       /* base_init */
132             NULL,       /* base_finalize */
133             (GClassInitFunc) hildon_weekday_picker_class_init,
134             NULL,       /* class_finalize */
135             NULL,       /* class_data */
136             sizeof (HildonWeekdayPicker),
137             0,  /* n_preallocs */
138             (GInstanceInitFunc) hildon_weekday_picker_init,
139         };
140         picker_type = g_type_register_static (GTK_TYPE_CONTAINER,
141                 "HildonWeekdayPicker",
142                 &picker_info, 0);
143     }
144
145     return picker_type;
146 }
147
148 static void
149 hildon_weekday_picker_class_init                (HildonWeekdayPickerClass *picker_class)
150 {
151     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (picker_class);
152     GtkContainerClass *container_class = GTK_CONTAINER_CLASS (picker_class);
153     GObjectClass *object_class = G_OBJECT_CLASS (picker_class);
154
155     parent_class = g_type_class_peek_parent (picker_class);
156
157     g_type_class_add_private (picker_class,
158             sizeof (HildonWeekdayPickerPrivate));
159
160     /* Override virtual methods */
161     widget_class->size_request                  = hildon_weekday_picker_size_request;
162     widget_class->size_allocate                 = hildon_weekday_picker_size_allocate;
163     widget_class->focus                         = hildon_weekday_picker_focus;
164     container_class->forall                     = hildon_weekday_picker_forall;
165     GTK_OBJECT_CLASS (picker_class)->destroy    = hildon_weekday_picker_destroy;
166
167     /* Create a signal for reporting user actions */
168     signals [SELECTION_CHANGED_SIGNAL] = g_signal_new ("selection_changed",
169             G_OBJECT_CLASS_TYPE
170             (object_class),
171             G_SIGNAL_RUN_LAST |
172             G_SIGNAL_ACTION,
173             G_STRUCT_OFFSET (HildonWeekdayPickerClass, selection_changed), 
174             NULL, NULL,
175             gtk_marshal_VOID__INT,
176             G_TYPE_NONE, 1, G_TYPE_INT);
177 }
178
179 static void
180 hildon_weekday_picker_init                      (HildonWeekdayPicker *picker)
181 {
182     HildonWeekdayPickerPrivate *priv;
183     gint i, day;
184     
185     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
186     g_assert (priv);
187
188     /* weekday indexes to be used with nl_langinfo. These are shifted
189      * by one for glib compability */
190     int wdays[] = {
191         -1,        /* 0 = invalid date */
192         ABDAY_2,   /* 1 = monday    in glib */
193         ABDAY_3,   /* 2 = tuesday   in glib */
194         ABDAY_4,   /* 3 = wednesday in glib */
195         ABDAY_5,   /* 4 = thursday  in glib */
196         ABDAY_6,   /* 5 = friday    in glib */
197         ABDAY_7,   /* 6 = saturday  in glib */
198         ABDAY_1 }; /* 7 = sunday    in glib */
199     GtkSizeGroup *sgroup;
200
201     sgroup = gtk_size_group_new (GTK_SIZE_GROUP_BOTH);
202
203     /* Check our first weekday */
204     day = *nl_langinfo (_NL_TIME_FIRST_WEEKDAY); 
205
206
207     /* Shift the days by one. This is done because GDateWeekday 
208      * starts with Monday(1) and langinfo's first day is Sunday */
209     day--;
210
211     if (day < 1)
212         day = 7;
213
214     /* Initialize and pack day buttons */
215     for (i = 1; i <= 7; i++) {
216         priv->buttons [i] = 
217             gtk_toggle_button_new_with_label (nl_langinfo (wdays[day]));
218         priv->day_order_buttons [day] = priv->buttons [i];
219         day++;
220
221         if (day > 7)
222             day = 1;
223
224         g_signal_connect (GTK_WIDGET (priv->buttons [i]),
225                 "toggled", G_CALLBACK (button_toggle), picker);
226
227         gtk_size_group_add_widget (sgroup, priv->buttons [i]);
228
229         gtk_widget_set_parent (priv->buttons [i], GTK_WIDGET (picker));
230         gtk_widget_show (priv->buttons[i]);
231     }
232
233     GTK_WIDGET_SET_FLAGS (picker, GTK_NO_WINDOW);
234
235     g_object_unref (sgroup);
236 }
237
238 /**
239  * hildon_weekday_picker_new:
240  *
241  * Creates a new #HildonWeekdayPicker.
242  *
243  * Returns: pointer to a new #HildonWeekdayPicker widget.
244  */
245 GtkWidget*
246 hildon_weekday_picker_new                       (void)
247 {
248     return g_object_new (HILDON_TYPE_WEEKDAY_PICKER, NULL);
249 }
250
251 static void 
252 hildon_weekday_picker_forall                    (GtkContainer *container,
253                                                  gboolean include_internals, 
254                                                  GtkCallback callback,
255                                                  gpointer callback_data)
256 {
257     HildonWeekdayPicker *picker;
258     HildonWeekdayPickerPrivate *priv;
259     gint i;
260
261     g_assert (container);
262     g_assert (callback);
263
264     picker = HILDON_WEEKDAY_PICKER (container);
265     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
266     g_assert (priv);
267
268     /* We only have internal children */
269     if (! include_internals)
270         return;
271
272     /* Activate callback for each day button */
273     for (i = 1; i <= 7; ++i) {
274         (*callback) (priv->buttons [i], callback_data);
275     }
276 }
277
278 static void 
279 hildon_weekday_picker_destroy                   (GtkObject *self)
280 {
281     HildonWeekdayPickerPrivate *priv;
282     gint i;
283
284     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (self);
285     g_assert (priv);
286
287     /* Destroy internal children... */
288     for (i = 1; i <= 7; ++i) {
289         if (priv->buttons [i])
290         {
291             gtk_widget_unparent (priv->buttons [i]);
292             priv->buttons [i] = NULL;
293         }
294     }
295
296     /* ... and chain to parent. */
297     if (GTK_OBJECT_CLASS (parent_class)->destroy)
298         GTK_OBJECT_CLASS (parent_class)->destroy (self);
299
300 }
301
302 static void 
303 hildon_weekday_picker_size_request              (GtkWidget *widget,
304                                                  GtkRequisition *requisition)
305 {
306     HildonWeekdayPicker *picker;
307     HildonWeekdayPickerPrivate *priv;
308     gint i;
309     GtkRequisition req;
310
311     picker = HILDON_WEEKDAY_PICKER (widget);
312     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
313     g_assert (priv);
314
315     requisition->width = 0;
316     requisition->height = 0;
317
318     /* Request an area that is as wide as all of the buttons
319        together and tall enough to hold heightest button */
320     for (i = 1; i <= 7; ++i) {
321         gtk_widget_size_request (priv->buttons [i], &req);
322         requisition->width += req.width;
323         if (req.height > requisition->height)
324             requisition->height = req.height;
325
326     }
327 }
328
329 static void 
330 hildon_weekday_picker_size_allocate             (GtkWidget *widget,
331                                                  GtkAllocation *allocation)
332 {
333     HildonWeekdayPicker *picker;
334     HildonWeekdayPickerPrivate *priv;
335     gint i;
336     GtkAllocation alloc;
337     GtkRequisition child_requisition;
338     gint header_x;
339     guint sval;
340     GtkTextDirection direction;
341
342     g_assert (widget);
343     g_assert (allocation);
344
345     /* Check orientation */
346     direction = gtk_widget_get_direction (widget);
347
348     picker = HILDON_WEEKDAY_PICKER (widget);
349     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
350     g_assert (priv);
351
352     header_x = allocation->x;
353     widget->allocation = *allocation;
354
355     if (direction == GTK_TEXT_DIR_LTR || direction == GTK_TEXT_DIR_NONE)
356         sval = 1;
357     else
358         sval = 7;
359
360     /* Allocate day buttons side by side honouring the text direction */
361     for (i = 1; i <= 7; ++i) {
362         gtk_widget_get_child_requisition (priv->buttons[sval], &child_requisition);
363
364         alloc.x = header_x;
365         alloc.y = allocation->y;
366         alloc.width = child_requisition.width;
367         alloc.height = child_requisition.height;
368         header_x += alloc.width;
369         gtk_widget_size_allocate (priv->buttons [sval], &alloc);
370         if (direction == GTK_TEXT_DIR_RTL)
371             sval--;
372         else
373             sval++;
374     }
375 }
376
377 static gboolean
378 hildon_weekday_picker_focus                      (GtkWidget *widget,
379                                                   GtkDirectionType direction)
380 {
381   gboolean retval;
382   GtkDirectionType effective_direction;
383
384   g_assert (HILDON_IS_WEEKDAY_PICKER (widget));
385
386   retval = hildon_private_composite_focus (widget, direction, &effective_direction);
387
388   if (retval == TRUE)
389     return GTK_WIDGET_CLASS (parent_class)->focus (widget, effective_direction);
390   else
391     return FALSE;
392 }
393
394 static void
395 button_toggle                                   (GtkToggleButton *button, 
396                                                  gpointer wpicker)
397 {
398     HildonWeekdayPicker *picker;
399     HildonWeekdayPickerPrivate *priv;
400     gint i;
401
402     g_assert(button);
403     g_assert(wpicker);
404
405     picker = HILDON_WEEKDAY_PICKER (wpicker);
406     g_assert (picker);
407     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
408     g_assert (priv);
409
410     for (i = 1; i <= 7; ++i) {
411         if (GTK_WIDGET (button) == priv->day_order_buttons [i]) {
412             g_signal_emit (GTK_WIDGET (picker), 
413                     signals [SELECTION_CHANGED_SIGNAL], 0, i);
414             break;
415         }
416     }
417 }
418
419 /**
420  * hildon_weekday_picker_set_day:
421  * @picker: the #HildonWeekdayPicker widget
422  * @day: day to be set active
423  *
424  * Sets specified weekday active.
425  */
426 void 
427 hildon_weekday_picker_set_day                   (HildonWeekdayPicker *picker,
428                                                  GDateWeekday day)
429 {
430     HildonWeekdayPickerPrivate *priv;
431
432     g_return_if_fail (HILDON_IS_WEEKDAY_PICKER (picker));
433     g_return_if_fail (g_date_valid_weekday(day));
434
435     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
436
437     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
438             (priv->day_order_buttons[day]), TRUE);
439 }
440
441 /**
442  * hildon_weekday_picker_unset_day:
443  * @picker: the #HildonWeekdayPicker widget
444  * @day: day to be set inactive 
445  *
446  * Unselect specified weekday.
447  */
448 void 
449 hildon_weekday_picker_unset_day                 (HildonWeekdayPicker *picker,
450                                                  GDateWeekday day)
451 {
452     HildonWeekdayPickerPrivate *priv;
453
454     g_return_if_fail (HILDON_IS_WEEKDAY_PICKER (picker));
455     g_return_if_fail (g_date_valid_weekday (day));
456
457     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
458     g_assert (priv);
459
460     gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON
461             (priv->day_order_buttons [day]), FALSE);
462 }
463
464 /**
465  * hildon_weekday_picker_toggle_day:
466  * @picker: the #HildonWeekdayPicker widget
467  * @day: day to be toggled
468  *
469  * Toggles current status of the specified weekday.
470  */
471 void 
472 hildon_weekday_picker_toggle_day                (HildonWeekdayPicker *picker,
473                                                  GDateWeekday day)
474 {
475     HildonWeekdayPickerPrivate *priv;
476     
477     g_return_if_fail (HILDON_IS_WEEKDAY_PICKER (picker));
478     g_return_if_fail (g_date_valid_weekday (day));
479
480     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
481     g_assert (priv);
482
483     gtk_toggle_button_set_active (
484             GTK_TOGGLE_BUTTON (priv->day_order_buttons [day]), 
485             ! gtk_toggle_button_get_active(
486                 GTK_TOGGLE_BUTTON (priv->day_order_buttons[day])));
487 }
488
489 /**
490  * hildon_weekday_picker_set_all:
491  * @picker: the #HildonWeekdayPicker widget
492  *
493  * Sets all weekdays active.
494  */
495 void 
496 hildon_weekday_picker_set_all                   (HildonWeekdayPicker *picker)
497 {
498     HildonWeekdayPickerPrivate *priv;
499     gint i;
500
501     g_return_if_fail (HILDON_IS_WEEKDAY_PICKER (picker));
502
503     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
504     g_assert (priv);
505
506     for (i = 1; i <= 7; i++)
507         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->buttons [i]), TRUE);
508 }
509
510 /**
511  * hildon_weekday_picker_unset_all:
512  * @picker: the #HildonWeekdayPicker widget
513  *
514  * Sets all weekdays inactive.
515  */
516 void 
517 hildon_weekday_picker_unset_all                 (HildonWeekdayPicker *picker)
518 {
519     HildonWeekdayPickerPrivate *priv;
520     gint i;
521
522     g_return_if_fail (HILDON_IS_WEEKDAY_PICKER (picker));
523
524     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
525     g_assert (priv);
526
527     for (i = 1; i <= 7; i++)
528         gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->buttons [i]), FALSE);
529 }
530
531 /**
532  * hildon_weekday_picker_isset_day:
533  * @picker: the #HildonWeekdayPicker widget
534  * @day: day to be checked.
535  *
536  * Checks if the specified weekday is set active.
537  *
538  * Returns: TRUE if the day is set, FALSE if the day is not set
539  */
540 gboolean 
541 hildon_weekday_picker_isset_day                 (HildonWeekdayPicker *picker,
542                                                  GDateWeekday day)
543 {
544     HildonWeekdayPickerPrivate *priv;
545
546     g_return_val_if_fail (HILDON_IS_WEEKDAY_PICKER (picker), FALSE);
547     g_return_val_if_fail (g_date_valid_weekday (day), FALSE);
548
549     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE (picker);
550     g_assert (picker);
551
552     return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (priv->day_order_buttons[day]));
553 }
554