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