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