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