b23bc26ffa978086c4284057cbdfd9803d7580fa
[hildon] / hildon-widgets / hildon-calendar-popup.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 or 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-calendar-popup
27  * @shortdesc: CalendarPopup allows choosing a date from a popup calendar.
28  * @longdesc: The Calendar popup is a dialog that contains a GtkCalendar 
29  * widget. The pop-up is cancelled by pressing the ESC key.
30  * </para><para>
31  * 
32  * @seealso: #HildonDateEditor, #HildonTimeEditor
33  * 
34  * HildonCalendarPopup is a dialog which contains a GtkCalendar.  It
35  * also contains arrow buttons for changing the month/year. If an
36  * entered date is invalid, an information message will be shown.
37  */
38
39 #ifdef HAVE_CONFIG_H
40 #include <config.h>
41 #endif
42
43 #include <gtk/gtk.h>
44 #include <gtk/gtkcalendar.h>
45 #include <gdk/gdk.h>
46 #include <gdk/gdkkeysyms.h>
47 #include <langinfo.h>
48 #include <time.h>
49 #include <libintl.h>
50 #include <hildon-widgets/hildon-calendar-popup.h>
51
52 #define _(String) dgettext(PACKAGE, String)
53
54 #define HILDON_CALENDAR_POPUP_GET_PRIVATE(obj) \
55         (G_TYPE_INSTANCE_GET_PRIVATE\
56         ((obj), HILDON_TYPE_CALENDAR_POPUP, HildonCalendarPopupPrivate));
57
58 static GtkDialog *parent_class;
59
60 typedef struct _HildonCalendarPopupPrivate HildonCalendarPopupPrivate;
61
62 static void init_dmy(guint year, guint month, guint day, guint * d,
63                      guint * m, guint * y);
64
65 static void
66 hildon_calendar_popup_class_init(HildonCalendarPopupClass * cal_class);
67
68 static void hildon_calendar_popup_init(HildonCalendarPopup * cal);
69
70 static void hildon_calendar_selected_date(GtkWidget * self, gpointer cal_popup);
71
72 static gboolean hildon_key_pressed(GtkWidget * widget, GdkEventKey * event,
73                                    gpointer cal_popup);
74
75 static void hildon_calendar_popup_set_property(GObject * object, guint property_id,
76                                     const GValue * value, GParamSpec * pspec);
77 static void hildon_calendar_popup_get_property(GObject * object, guint property_id,
78                                     GValue * value, GParamSpec * pspec);
79
80 enum {
81   PROP_0,
82   PROP_DAY,
83   PROP_MONTH,
84   PROP_YEAR,
85   PROP_MIN_YEAR,
86   PROP_MAX_YEAR
87 };
88
89 struct _HildonCalendarPopupPrivate {
90     GtkWidget *cal;
91 };
92
93 GType hildon_calendar_popup_get_type(void)
94 {
95     static GType popup_type = 0;
96
97     if (!popup_type) {
98         static const GTypeInfo popup_info = {
99             sizeof(HildonCalendarPopupClass),
100             NULL,       /* base_init */
101             NULL,       /* base_finalize */
102             (GClassInitFunc) hildon_calendar_popup_class_init,
103             NULL,       /* class_finalize */
104             NULL,       /* class_data */
105             sizeof(HildonCalendarPopup),
106             0,  /* n_preallocs */
107             (GInstanceInitFunc) hildon_calendar_popup_init,
108         };
109         popup_type = g_type_register_static(GTK_TYPE_DIALOG,
110                                             "HildonCalendarPopup",
111                                             &popup_info, 0);
112     }
113
114     return popup_type;
115 }
116
117 /**
118  * hildon_calendar_popup_new:
119  * @parent: parent window for dialog
120  * @year: initial year
121  * @month: initial month
122  * @day: initial day
123  *
124  * This function returns a new HildonCalendarPopup. The initially
125  * selected date is specified by the parameters (year, month, day).
126  * If the specified date is invalid, the current date is used. 
127  *
128  * Returns: new @HildonCalendarPopup widget
129  */
130 GtkWidget *hildon_calendar_popup_new(GtkWindow * parent, guint year,
131                                      guint month, guint day)
132 {
133     HildonCalendarPopup *cal = NULL;
134
135     /* Create new HildonCalendarPopup */
136     cal = HILDON_CALENDAR_POPUP(g_object_new(HILDON_TYPE_CALENDAR_POPUP,
137                                              "year", year, "month", month, "day", day,
138                                              NULL));
139
140     if (parent) {
141         gtk_window_set_transient_for(GTK_WINDOW(cal), parent);
142     }
143
144     return GTK_WIDGET(cal);
145 }
146
147 /**
148  * hildon_calendar_popup_set_date:
149  * @cal: the @HildonCalendarPopup widget
150  * @year: year
151  * @month: month
152  * @day: day
153  *
154  * activates a new date on the calendar popup.
155  **/
156 void
157 hildon_calendar_popup_set_date(HildonCalendarPopup * cal,
158                                guint year, guint month, guint day)
159 {
160     guint dtmp, mtmp, ytmp = 0;
161     HildonCalendarPopupPrivate *priv;
162
163     g_return_if_fail(HILDON_IS_CALENDAR_POPUP(cal));
164
165     priv = HILDON_CALENDAR_POPUP_GET_PRIVATE(cal);
166
167     /* Choose current date if the date is invalid: 
168      */
169     init_dmy(year, month, day, &dtmp, &mtmp, &ytmp);
170
171     /* Remove all visual markers */
172     gtk_calendar_clear_marks(GTK_CALENDAR(priv->cal));
173
174     /* Set a new date */
175     gtk_calendar_select_month(GTK_CALENDAR(priv->cal), mtmp - 1, ytmp);
176     gtk_calendar_select_day(GTK_CALENDAR(priv->cal), dtmp);
177 }
178
179 /**
180  * hildon_calendar_popup_get_date:
181  * @cal: the @HildonCalendarPopup widget
182  * @year: year
183  * @month: month
184  * @day: day
185  *
186  * Gets the currently selected year, month, and day. 
187  */
188 void
189 hildon_calendar_popup_get_date(HildonCalendarPopup * cal,
190                                guint * year, guint * month, guint * day)
191 {
192     HildonCalendarPopupPrivate *priv;
193
194     g_return_if_fail(HILDON_IS_CALENDAR_POPUP(cal));
195
196     priv = HILDON_CALENDAR_POPUP_GET_PRIVATE(cal);
197     gtk_calendar_get_date(GTK_CALENDAR(priv->cal), year, month, day);
198     *month = *month + 1;
199
200     if (!g_date_valid_dmy(*day, *month, *year)) {
201         *day = g_date_get_days_in_month(*month, *year);
202     }
203 }
204
205 static void
206 hildon_calendar_popup_class_init(HildonCalendarPopupClass * cal_class)
207 {
208     GObjectClass *object_class = G_OBJECT_CLASS(cal_class);
209     parent_class = g_type_class_peek_parent(cal_class);
210     
211     object_class->set_property = hildon_calendar_popup_set_property;
212     object_class->get_property = hildon_calendar_popup_get_property;
213
214     g_type_class_add_private(cal_class,
215                              sizeof(HildonCalendarPopupPrivate));
216
217     /* Install new properties for the GObject_class */
218     g_object_class_install_property(object_class, PROP_MIN_YEAR,
219                                     g_param_spec_uint("min-year",
220                                                       "Minimum valid year",
221                                                       "Minimum valid year",
222                                                       1, 2100,
223                                                       1970,
224                                                       G_PARAM_WRITABLE));
225
226     g_object_class_install_property(object_class, PROP_MAX_YEAR,
227                                     g_param_spec_uint("max-year",
228                                                       "Maximum valid year",
229                                                       "Maximum valid year",
230                                                       1, 2100,
231                                                       2037,
232                                                       G_PARAM_WRITABLE));
233
234     g_object_class_install_property(object_class, PROP_DAY,
235                                     g_param_spec_int ("day",
236                                                       "Day",
237                                                       "currently selected day",
238                                                       G_MININT,
239                                                       G_MAXINT,
240                                                       0,
241                                                       G_PARAM_READWRITE));
242
243     g_object_class_install_property(object_class, PROP_MONTH,
244                                     g_param_spec_int ("month",
245                                                       "Month",
246                                                       "currently selected month",
247                                                       G_MININT,
248                                                       G_MAXINT,
249                                                       0,
250                                                       G_PARAM_READWRITE));
251
252     g_object_class_install_property(object_class, PROP_YEAR,
253                                     g_param_spec_int ("year",
254                                                       "Year",
255                                                       "the currently selected year",
256                                                       G_MININT,
257                                                       G_MAXINT,
258                                                       0,
259                                                       G_PARAM_READWRITE));
260
261 }
262
263 static void hildon_calendar_popup_init(HildonCalendarPopup * cal)
264 {
265     HildonCalendarPopupPrivate *priv;
266     static int set_domain = 1;
267
268     priv = HILDON_CALENDAR_POPUP_GET_PRIVATE(cal);
269
270     /* set the domain directory for different language */
271     if (set_domain) {
272         (void) bindtextdomain(PACKAGE, LOCALEDIR);
273         set_domain = 0;
274     }
275
276     priv->cal = gtk_calendar_new();
277
278     /* dialog options and packing */
279     gtk_calendar_set_display_options(GTK_CALENDAR(priv->cal),
280                                      GTK_CALENDAR_SHOW_HEADING |
281                                      GTK_CALENDAR_SHOW_DAY_NAMES |
282                                      GTK_CALENDAR_SHOW_WEEK_NUMBERS);
283
284     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(cal)->vbox), priv->cal,
285                        TRUE, TRUE, 0);
286     gtk_dialog_set_has_separator(GTK_DIALOG(cal), FALSE);
287     gtk_dialog_add_button(GTK_DIALOG(cal), _("ecdg_bd_calendar_popout_done"),
288                             GTK_RESPONSE_OK);
289     gtk_widget_show(priv->cal);
290
291     /* Connect signals */
292     g_signal_connect(G_OBJECT(priv->cal), "key-press-event",
293                      G_CALLBACK(hildon_key_pressed), cal);
294
295     g_signal_connect(G_OBJECT(priv->cal), "selected_date",
296                      G_CALLBACK(hildon_calendar_selected_date), cal);
297
298     /* set decorations, needs realizing first*/
299     gtk_widget_realize(GTK_WIDGET(cal));
300     gdk_window_set_decorations(GTK_WIDGET(cal)->window, GDK_DECOR_BORDER);
301
302 }
303
304
305 /*
306  * Signal handler for key-press-event. Closes the dialog for some
307  * special keys.
308  */
309 static gboolean
310 hildon_key_pressed(GtkWidget * widget, GdkEventKey * event, gpointer cal_popup)
311 {
312     g_assert(HILDON_IS_CALENDAR_POPUP(cal_popup));
313
314     /* Handle Return key press as OK response */
315     if (event->keyval == GDK_Return)
316     {
317         gtk_dialog_response(GTK_DIALOG(cal_popup), GTK_RESPONSE_OK);
318         return TRUE;
319     }
320
321     /* Handle Esc key press as CANCEL response */
322     if ((event->keyval == GDK_Escape))
323     {
324         gtk_dialog_response(GTK_DIALOG(cal_popup), GTK_RESPONSE_CANCEL);
325         return TRUE;
326     }
327
328     return FALSE;
329 }
330
331
332 /*
333  * Validates the given date or initializes it with the current date
334  */
335 static void
336 init_dmy(guint year, guint month, guint day, guint *d, guint *m, guint *y)
337 {
338     GDate date;
339
340     /* Initialize the date with a valid selected date */ 
341     if (g_date_valid_dmy(day, month, year)) {
342         *d = day;
343         *m = month;
344         *y = year;
345     } else { 
346
347       /* If selected date is invalid initialize the date with current date */ 
348         g_date_clear(&date, 1);
349         g_date_set_time(&date, time(NULL));
350
351         *d = g_date_get_day(&date);
352         *m = g_date_get_month(&date);
353         *y = g_date_get_year(&date);
354     }
355 }
356
357 /*
358  * Exits the dialog when "selected_date" signal is emmited. The
359  * "selected_date" signal is a Hildon addition to GtkCalendar and is
360  * emitted on button-release.
361  */
362 static void
363 hildon_calendar_selected_date(GtkWidget * self, gpointer cal_popup)
364 {
365     g_assert(GTK_IS_WIDGET (self));
366     g_assert(HILDON_IS_CALENDAR_POPUP (cal_popup));
367  
368     gtk_dialog_response(GTK_DIALOG(cal_popup), GTK_RESPONSE_OK);
369 }
370
371
372 static void hildon_calendar_popup_set_property(GObject * object, guint property_id,
373                                     const GValue * value, GParamSpec * pspec)
374 {
375     HildonCalendarPopup *popup = HILDON_CALENDAR_POPUP (object);
376     HildonCalendarPopupPrivate *priv = 
377         HILDON_CALENDAR_POPUP_GET_PRIVATE(HILDON_CALENDAR_POPUP (object));
378
379     switch (property_id) {
380     case PROP_DAY:
381     {
382         guint year, month, day = 0;
383         hildon_calendar_popup_get_date(popup, &year, &month, &day);
384
385         /*Verifies that the date is valid: */
386         hildon_calendar_popup_set_date(popup, year, month, g_value_get_int(value));
387         break;
388     }
389     case PROP_MONTH:
390     {
391         guint year, month, day = 0;
392         hildon_calendar_popup_get_date(popup, &year, &month, &day);
393
394         /*Verifies that the date is valid: */
395         hildon_calendar_popup_set_date(popup, year, g_value_get_int(value), day);
396         break;
397     }
398     case PROP_YEAR:
399     {
400         guint year, month, day = 0;
401         hildon_calendar_popup_get_date(popup, &year, &month, &day);
402
403         /*Verifies that the date is valid: */
404         hildon_calendar_popup_set_date(popup, g_value_get_int(value), month, day);
405         break;
406     }
407     case PROP_MIN_YEAR:
408         g_object_set_property(G_OBJECT(priv->cal), "min-year", value);
409         break;
410     case PROP_MAX_YEAR:
411         g_object_set_property(G_OBJECT(priv->cal), "max-year", value);
412         break;
413     default:
414         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
415         break;
416     }
417 }
418
419 static void hildon_calendar_popup_get_property(GObject * object, guint property_id,
420                                     GValue * value, GParamSpec * pspec)
421 {
422     HildonCalendarPopupPrivate *priv = 
423         HILDON_CALENDAR_POPUP_GET_PRIVATE(HILDON_CALENDAR_POPUP (object));
424
425     switch (property_id) {
426     case PROP_DAY:
427         g_object_get_property(G_OBJECT(priv->cal), pspec->name, value);
428         break;
429     case PROP_MONTH:
430         g_object_get_property(G_OBJECT(priv->cal), pspec->name, value);
431         break;
432     case PROP_YEAR:
433         g_object_get_property(G_OBJECT(priv->cal), pspec->name, value);
434         break;
435     default:
436         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
437         break;
438     }
439 }