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