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