* doc/tmpl/* * hildon-widgets/* moved widget descriptions to respective source file...
[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  * 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 <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 HILDON_WEEKDAY_PICKER_GET_PRIVATE(obj) \
65     (G_TYPE_INSTANCE_GET_PRIVATE ((obj), \
66      HILDON_WEEKDAY_PICKER_TYPE, HildonWeekdayPickerPrivate));
67
68 static GtkContainerClass *parent_class;
69
70 typedef struct _HildonWeekdayPickerPrivate HildonWeekdayPickerPrivate;
71
72 static void
73 hildon_weekday_picker_class_init(HildonWeekdayPickerClass * picker_class);
74 static void 
75 hildon_weekday_picker_init(HildonWeekdayPicker * picker);
76 static void
77 hildon_weekday_picker_size_allocate(GtkWidget * widget,
78                                     GtkAllocation * allocation);
79 static void
80 hildon_weekday_picker_size_request(GtkWidget * widget,
81                                    GtkRequisition * requisition);
82 static void
83 hildon_weekday_picker_forall(GtkContainer * container,
84                              gboolean include_internals,
85                              GtkCallback callback, gpointer callback_data);
86 static void
87 hildon_weekday_picker_destroy(GtkObject * self);
88
89 static void
90 button_toggle(GtkToggleButton * togglebutton, gpointer wpicker);
91
92 struct _HildonWeekdayPickerPrivate {
93     GtkWidget *buttons[8];           /* weekday buttons in show order */
94     GtkWidget *day_order_buttons[8]; /* weekday buttons in glib day order */
95 };
96
97 enum {
98   SELECTION_CHANGED_SIGNAL,
99   LAST_SIGNAL
100 };
101
102 static guint signals[LAST_SIGNAL] = { 0 };
103
104 GType hildon_weekday_picker_get_type(void)
105 {
106     static GType picker_type = 0;
107
108     if (!picker_type) {
109         static const GTypeInfo picker_info = {
110             sizeof(HildonWeekdayPickerClass),
111             NULL,       /* base_init */
112             NULL,       /* base_finalize */
113             (GClassInitFunc) hildon_weekday_picker_class_init,
114             NULL,       /* class_finalize */
115             NULL,       /* class_data */
116             sizeof(HildonWeekdayPicker),
117             0,  /* n_preallocs */
118             (GInstanceInitFunc) hildon_weekday_picker_init,
119         };
120         picker_type = g_type_register_static(GTK_TYPE_CONTAINER,
121                                              "HildonWeekdayPicker",
122                                              &picker_info, 0);
123     }
124     return picker_type;
125 }
126
127 static void
128 hildon_weekday_picker_class_init(HildonWeekdayPickerClass * picker_class)
129 {
130     GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(picker_class);
131     GtkContainerClass *container_class = GTK_CONTAINER_CLASS(picker_class);
132     GObjectClass *object_class = G_OBJECT_CLASS(picker_class);
133
134     parent_class = g_type_class_peek_parent(picker_class);
135
136     g_type_class_add_private(picker_class,
137                              sizeof(HildonWeekdayPickerPrivate));
138
139     /* Override virtual methods */
140     widget_class->size_request = hildon_weekday_picker_size_request;
141     widget_class->size_allocate = hildon_weekday_picker_size_allocate;
142     widget_class->focus = hildon_composite_widget_focus;
143     container_class->forall = hildon_weekday_picker_forall;
144     GTK_OBJECT_CLASS(picker_class)->destroy =
145         hildon_weekday_picker_destroy;
146
147     /* Create a signal for reporting user actions */
148     signals[SELECTION_CHANGED_SIGNAL] = g_signal_new("selection_changed",
149                                                 G_OBJECT_CLASS_TYPE
150                                                 (object_class),
151                                                 G_SIGNAL_RUN_LAST |
152                                                 G_SIGNAL_ACTION,
153                                                 G_STRUCT_OFFSET
154                                                 (HildonWeekdayPickerClass,
155                                                  selection_changed), NULL, NULL,
156                                                 gtk_marshal_VOID__INT,
157                                                 G_TYPE_NONE, 1, G_TYPE_INT);
158 }
159
160 static void
161 hildon_weekday_picker_init(HildonWeekdayPicker * picker)
162 {
163     HildonWeekdayPickerPrivate *priv;
164     gint i, day;
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     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
184     
185     /* Shift the days by one. This is done because GDateWeekday 
186      * starts with Monday(1) and langinfo's first day is Sunday */
187     day--;
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 *hildon_weekday_picker_new(void)
223 {
224     return g_object_new(HILDON_WEEKDAY_PICKER_TYPE, NULL);
225 }
226
227 static void 
228 hildon_weekday_picker_forall(GtkContainer * container,
229                              gboolean include_internals, GtkCallback callback,
230                              gpointer callback_data)
231 {
232     HildonWeekdayPicker *picker;
233     HildonWeekdayPickerPrivate *priv;
234     gint i;
235
236     g_assert(container);
237     g_assert(callback);
238
239     picker = HILDON_WEEKDAY_PICKER(container);
240     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
241
242     /* We only have internal children */
243     if (!include_internals)
244         return;
245
246     /* Activate callback for each day button */
247     for (i = 1; i <= 7; ++i) {
248         (*callback) (priv->buttons[i], callback_data);
249     }
250
251 }
252
253 static void 
254 hildon_weekday_picker_destroy(GtkObject * self)
255 {
256     HildonWeekdayPickerPrivate *priv;
257     gint i;
258
259     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(self);
260
261     /* Destroy internal children... */
262     for (i = 1; i <= 7; ++i) {
263       if (priv->buttons[i])
264       {
265         gtk_widget_unparent(priv->buttons[i]);
266         priv->buttons[i] = NULL;
267       }
268     }
269
270     /* ... and chain to parent. */
271     if (GTK_OBJECT_CLASS(parent_class)->destroy)
272         GTK_OBJECT_CLASS(parent_class)->destroy(self);
273
274 }
275
276 static void 
277 hildon_weekday_picker_size_request(GtkWidget * widget,
278                                    GtkRequisition *requisition)
279 {
280     HildonWeekdayPicker *picker;
281     HildonWeekdayPickerPrivate *priv;
282     gint i;
283     GtkRequisition req;
284
285     picker = HILDON_WEEKDAY_PICKER(widget);
286     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
287     requisition->width = 0;
288     requisition->height = 0;
289
290     /* Request an area that is as wide as all of the buttons
291        together and tall enough to hold heightest button */
292     for (i = 1; i <= 7; ++i) {
293         gtk_widget_size_request(priv->buttons[i], &req);
294         requisition->width += req.width;
295         if (req.height > requisition->height)
296             requisition->height = req.height;
297
298     }
299 }
300
301 static void 
302 hildon_weekday_picker_size_allocate(GtkWidget * widget,
303                                                 GtkAllocation * allocation)
304 {
305     HildonWeekdayPicker *picker;
306     HildonWeekdayPickerPrivate *priv;
307     gint i;
308     GtkAllocation alloc;
309     GtkRequisition child_requisition;
310     gint header_x;
311     guint sval;
312     GtkTextDirection direction;
313
314     g_assert(widget);
315     g_assert(allocation);
316
317     /* Check orientation */
318     direction = gtk_widget_get_direction(widget);
319
320     picker = HILDON_WEEKDAY_PICKER(widget);
321     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
322     header_x = allocation->x;
323     widget->allocation = *allocation;
324
325     if (direction == GTK_TEXT_DIR_LTR || direction == GTK_TEXT_DIR_NONE)
326         sval = 1;
327     else
328         sval = 7;
329
330     /* Allocate day buttons side by side honouring the text direction */
331     for (i = 1; i <= 7; ++i) {
332         gtk_widget_get_child_requisition(priv->buttons[sval],
333                                          &child_requisition);
334
335         alloc.x = header_x;
336         alloc.y = allocation->y;
337         alloc.width = child_requisition.width;
338         alloc.height = child_requisition.height;
339         header_x += alloc.width;
340         gtk_widget_size_allocate(priv->buttons[sval], &alloc);
341         if (direction == GTK_TEXT_DIR_RTL)
342             sval--;
343         else
344             sval++;
345     }
346 }
347
348 static void
349 button_toggle(GtkToggleButton * button, gpointer wpicker)
350 {
351     HildonWeekdayPicker *picker;
352     HildonWeekdayPickerPrivate *priv;
353     gint i;
354
355     g_assert(button);
356     g_assert(wpicker);
357
358     picker = HILDON_WEEKDAY_PICKER(wpicker);
359     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
360
361     for (i = 1; i <= 7; ++i) {
362         if (GTK_WIDGET(button) == priv->day_order_buttons[i]) {
363             g_signal_emit (GTK_WIDGET(picker), 
364                            signals[SELECTION_CHANGED_SIGNAL], 0, i);
365             break;
366         }
367     }
368 }
369
370 /**
371  * hildon_weekday_picker_set_day:
372  * @picker: the #HildonWeekdayPicker widget
373  * @day: day to be set active
374  *
375  * Sets specified weekday active.
376  */
377 void 
378 hildon_weekday_picker_set_day(HildonWeekdayPicker * picker,
379                               GDateWeekday day)
380 {
381     HildonWeekdayPickerPrivate *priv;
382
383     g_return_if_fail(picker);
384     g_return_if_fail(g_date_valid_weekday(day));
385
386     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
387
388     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
389                                  (priv->day_order_buttons[day]), TRUE);
390 }
391
392 /**
393  * hildon_weekday_picker_unset_day:
394  * @picker: the #HildonWeekdayPicker widget
395  * @day: day to be set inactive 
396  *
397  * Sets specified weekday inactive.
398  */
399 void 
400 hildon_weekday_picker_unset_day(HildonWeekdayPicker * picker,
401                                 GDateWeekday day)
402 {
403     HildonWeekdayPickerPrivate *priv;
404
405     g_return_if_fail(picker);
406     g_return_if_fail(g_date_valid_weekday(day));
407
408     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
409
410     gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON
411                                  (priv->day_order_buttons[day]), FALSE);
412 }
413
414 /**
415  * hildon_weekday_picker_toggle_day:
416  * @picker: the #HildonWeekdayPicker widget
417  * @day: day to be toggled
418  *
419  * Toggles current status of the specified weekday.
420  */
421 void 
422 hildon_weekday_picker_toggle_day(HildonWeekdayPicker * picker,
423                                  GDateWeekday day)
424 {
425     HildonWeekdayPickerPrivate *priv;
426    
427     g_return_if_fail(picker);
428     g_return_if_fail(g_date_valid_weekday(day));
429
430     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
431
432     gtk_toggle_button_set_active(
433         GTK_TOGGLE_BUTTON(priv->day_order_buttons[day]), 
434         !gtk_toggle_button_get_active(
435             GTK_TOGGLE_BUTTON(priv->day_order_buttons[day])));
436 }
437
438 /**
439  * hildon_weekday_picker_set_all:
440  * @picker: the #HildonWeekdayPicker widget
441  *
442  * Sets all weekdays active.
443  */
444 void 
445 hildon_weekday_picker_set_all(HildonWeekdayPicker * picker)
446 {
447     HildonWeekdayPickerPrivate *priv;
448     gint i;
449
450     g_return_if_fail(picker);
451
452     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
453
454     for (i = 1; i <= 7; i++)
455         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->buttons[i]), 
456                                      TRUE);
457 }
458
459 /**
460  * hildon_weekday_picker_unset_all:
461  * @picker: the #HildonWeekdayPicker widget
462  *
463  * Sets all weekdays inactive.
464  */
465 void 
466 hildon_weekday_picker_unset_all(HildonWeekdayPicker * picker)
467 {
468     HildonWeekdayPickerPrivate *priv;
469     gint i;
470
471     g_return_if_fail(picker);
472
473     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
474
475     for (i = 1; i <= 7; i++)
476         gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->buttons[i]),
477                                      FALSE);
478 }
479
480 /**
481  * hildon_weekday_picker_isset_day:
482  * @picker: the #HildonWeekdayPicker widget
483  * @day: day to be checked.
484  *
485  * Checks if the specified weekday is set active.
486  *
487  * Returns: TRUE if the day is set, FALSE if the day is not set
488  */
489 gboolean 
490 hildon_weekday_picker_isset_day(HildonWeekdayPicker * picker,
491                                 GDateWeekday day)
492 {
493     HildonWeekdayPickerPrivate *priv;
494
495     g_return_val_if_fail(picker, FALSE);
496     g_return_val_if_fail(g_date_valid_weekday(day), FALSE);
497
498     priv = HILDON_WEEKDAY_PICKER_GET_PRIVATE(picker);
499
500     return gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(
501                                             priv->day_order_buttons[day]));
502 }