1 /*
2 * RAL -- Rubrica Addressbook Library
3 * file: ref.c
4 *
5 * Copyright (C) Nicola Fragale <nicolafragale@libero.it>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 */
21
22 #include <gtk/gtk.h>
23 #include <glib/gi18n-lib.h>
24 #include <gdk/gdkkeysyms.h>
25
26 #include "calendar.h"
27
28 #define BUFSIZE 64
29
30 enum {
31 PROP_0,
32 CALENDAR_DAY,
33 CALENDAR_MONTH,
34 CALENDAR_YEAR
35 };
36
37
38 /* signals enumeration
39 */
40 enum {
41 CALENDAR_CHANGE,
42 LAST_SIGNAL
43 };
44
45
46 struct _RubricaCalendarPrivate {
47 GtkWidget* entry;
48 GtkWidget* button;
49 GtkWidget* popup;
50 GtkWidget* calendar;
51
52 GDate* date;
53
54 gint day;
55 gint month;
56 gint year;
57
58 gboolean dispose_has_run;
59 };
60
61
62 static guint calendar_signals[LAST_SIGNAL] = {0};
63
64
65 #define RUBRICA_CALENDAR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE((o), \
66 RUBRICA_CALENDAR_TYPE, \
67 RubricaCalendarPrivate))
68
69
70 static GObjectClass *parent_class = NULL;
71
72
73 static void rubrica_calendar_class_init (RubricaCalendarClass* klass);
74 static void rubrica_calendar_init (RubricaCalendar* obj);
75
76 static void rubrica_calendar_finalize (RubricaCalendar* self);
77 static void rubrica_calendar_dispose (RubricaCalendar* self);
78
79
80 static void rubrica_calendar_set_property (GObject* obj, guint property_id,
81 GValue* value, GParamSpec* spec);
82 static void rubrica_calendar_get_property (GObject* obj, guint property_id,
83 GValue* value, GParamSpec* spec);
84
85
86 static gboolean date_is_valid (RubricaCalendar* calendar);
87
88 static void update_calendar (RubricaCalendar *calendar);
89 static void hide_popup (RubricaCalendar *calendar);
90 static gboolean popup_grab_on_window (GdkWindow *window,
91 guint32 activate_time);
92
93 static void on_day_selected (GtkCalendar *calendar, gpointer data);
94 static void on_day_selected_double (GtkCalendar *calendar, gpointer data);
95 static gint on_popup_key_press (GtkWidget *widget, GdkEventKey *event,
96 gpointer data);
97 static gint on_popup_button_press (GtkWidget *widget,
98 GdkEventButton *event, gpointer data);
99 static void on_button_toggled_cb (GtkToggleButton *togglebutton,
100 gpointer data);
101
102
103 static gboolean
date_is_valid(RubricaCalendar * calendar)104 date_is_valid(RubricaCalendar* calendar)
105 {
106 RubricaCalendarPrivate *priv;
107
108 g_return_val_if_fail(IS_RUBRICA_CALENDAR(calendar), FALSE);
109
110 priv = RUBRICA_CALENDAR_GET_PRIVATE (calendar);
111
112 if (priv->day && (priv->month) && priv->year)
113 return TRUE;
114
115 return FALSE;
116 }
117
118
119 static void
update_calendar(RubricaCalendar * calendar)120 update_calendar (RubricaCalendar *calendar)
121 {
122 RubricaCalendarPrivate *priv = RUBRICA_CALENDAR_GET_PRIVATE (calendar);
123 gchar buffer[BUFSIZE];
124
125 if (date_is_valid(calendar))
126 {
127 g_date_set_dmy(priv->date, priv->day, priv->month, priv->year);
128
129 g_date_strftime(buffer, BUFSIZE, "%Ex", priv->date);
130 gtk_entry_set_text(GTK_ENTRY(priv->entry), buffer);
131
132 g_signal_emit_by_name(calendar, "calendar-change",
133 priv->date, G_TYPE_POINTER);
134 }
135 else
136 {
137 gtk_entry_set_text(GTK_ENTRY(priv->entry), _("unknown"));
138 }
139 }
140
141
142 static void
hide_popup(RubricaCalendar * calendar)143 hide_popup (RubricaCalendar *calendar)
144 {
145 RubricaCalendarPrivate *priv;
146
147 priv = RUBRICA_CALENDAR_GET_PRIVATE (calendar);
148
149 gtk_widget_hide (priv->popup);
150 gtk_grab_remove (priv->popup);
151 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (priv->button), FALSE);
152 }
153
154
155 static gboolean
popup_grab_on_window(GdkWindow * window,guint32 activate_time)156 popup_grab_on_window (GdkWindow *window, guint32 activate_time)
157 {
158 if ((gdk_pointer_grab(window, TRUE,
159 GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK |
160 GDK_POINTER_MOTION_MASK,
161 NULL, NULL, activate_time) == 0))
162 {
163 if (gdk_keyboard_grab (window, TRUE, activate_time) == 0)
164 return TRUE;
165 else
166 {
167 gdk_pointer_ungrab (activate_time);
168 return FALSE;
169 }
170 }
171
172 return FALSE;
173 }
174
175
176 static void
on_day_selected(GtkCalendar * cal,gpointer data)177 on_day_selected (GtkCalendar *cal, gpointer data)
178 {
179 RubricaCalendar *calendar = (RubricaCalendar *) data;
180 RubricaCalendarPrivate *priv = RUBRICA_CALENDAR_GET_PRIVATE (data);
181 guint day, month, year;
182
183 gtk_calendar_get_date (cal, &year, &month, &day);
184
185 priv->day = day;
186 priv->month = month + 1;
187 priv->year = year;
188
189 update_calendar(RUBRICA_CALENDAR(calendar));
190 }
191
192
193 static void
on_day_selected_double(GtkCalendar * calendar,gpointer data)194 on_day_selected_double (GtkCalendar *calendar, gpointer data)
195 {
196 hide_popup(RUBRICA_CALENDAR(data));
197 }
198
199
200 static gint
on_delete_popup(GtkWidget * widget,gpointer data)201 on_delete_popup (GtkWidget *widget, gpointer data)
202 {
203 hide_popup(RUBRICA_CALENDAR(data));
204
205 return TRUE;
206 }
207
208
209 static gint
on_popup_key_press(GtkWidget * widget,GdkEventKey * event,gpointer data)210 on_popup_key_press (GtkWidget *widget, GdkEventKey *event, gpointer data)
211 {
212 /* evaluate event so calendar is navigable with keyboard */
213 switch (event->keyval)
214 {
215 case GDK_Escape:
216 break;
217
218 case GDK_Return:
219 case GDK_KP_Enter:
220 break;
221
222 default:
223 return FALSE;
224 }
225
226 g_signal_stop_emission_by_name (widget, "key_press_event");
227 hide_popup (RUBRICA_CALENDAR(data));
228
229 return TRUE;
230 }
231
232
233
234 /* This function is yanked from gtkcombo.c
235 */
236 static gint
on_popup_button_press(GtkWidget * widget,GdkEventButton * event,gpointer data)237 on_popup_button_press (GtkWidget *widget, GdkEventButton *event, gpointer data)
238 {
239 GtkWidget *child;
240
241 /* We don't ask for button press events on the grab widget, so
242 * if an event is reported directly to the grab widget, it must
243 * be on a window outside the application (and thus we remove
244 * the popup window). Otherwise, we check if the widget is a child
245 * of the grab widget, and only remove the popup window if it
246 * is not.
247 */
248 child = gtk_get_event_widget ((GdkEvent *)event);
249
250 if (child != widget)
251 {
252 while (child)
253 {
254 if (child == widget) return FALSE;
255 child = child->parent;
256 }
257 }
258
259 hide_popup (RUBRICA_CALENDAR(data));
260
261 return TRUE;
262 }
263
264 /*
265 gtk_calendar_select_month (GtkCalendar *calendar, guint month, guint year);
266
267 Shifts the calendar to a different month.
268 ...
269
270 month : a month number between 0 and 11. <==
271
272 typedef enum
273 {
274 G_DATE_BAD_MONTH = 0,
275 G_DATE_JANUARY = 1,
276 G_DATE_FEBRUARY = 2,
277 G_DATE_MARCH = 3,
278 G_DATE_APRIL = 4,
279 G_DATE_MAY = 5,
280 G_DATE_JUNE = 6,
281 G_DATE_JULY = 7,
282 G_DATE_AUGUST = 8,
283 G_DATE_SEPTEMBER = 9,
284 G_DATE_OCTOBER = 10,
285 G_DATE_NOVEMBER = 11,
286 G_DATE_DECEMBER = 12
287 } GDateMonth;
288 */
289 static void
on_button_toggled_cb(GtkToggleButton * togglebutton,gpointer data)290 on_button_toggled_cb (GtkToggleButton *togglebutton, gpointer data)
291 {
292 RubricaCalendar *calendar = (RubricaCalendar *)data;
293 RubricaCalendarPrivate *priv = RUBRICA_CALENDAR_GET_PRIVATE (calendar);
294
295 gint x, y, width, height;
296 GtkRequisition req;
297
298 if (gtk_toggle_button_get_active (togglebutton))
299 {
300 if (date_is_valid(calendar))
301 {
302 /* subtract 1 from priv->month (GDateMonth) and align to
303 gtk_calendar_select_month needs
304 */
305 gtk_calendar_select_month (GTK_CALENDAR (priv->calendar),
306 priv->month - 1, priv->year);
307 gtk_calendar_select_day (GTK_CALENDAR (priv->calendar), priv->day);
308 }
309
310 /* show calendar
311 */
312 gtk_widget_size_request (priv->popup, &req);
313 gdk_window_get_origin (GDK_WINDOW (priv->button->window), &x, &y);
314 x += priv->button->allocation.x;
315 y += priv->button->allocation.y;
316 width = priv->button->allocation.width;
317 height = priv->button->allocation.height;
318 x += width - req.width;
319 y += height;
320 if (x < 0) x = 0;
321 if (y < 0) y = 0;
322
323 gtk_grab_add (priv->popup);
324 gtk_window_move (GTK_WINDOW (priv->popup), x, y);
325 gtk_widget_show (priv->popup);
326 gtk_widget_grab_focus (priv->calendar);
327 popup_grab_on_window (priv->popup->window,
328 gtk_get_current_event_time ());
329 }
330 }
331
332
333
334
335 GType
rubrica_calendar_get_type()336 rubrica_calendar_get_type()
337 {
338 static GType calendar_type = 0;
339
340 if (!calendar_type)
341 {
342 static const GTypeInfo calendar_info =
343 {
344 sizeof(RubricaCalendarClass),
345 NULL,
346 NULL,
347 (GClassInitFunc) rubrica_calendar_class_init,
348 NULL,
349 NULL,
350 sizeof(RubricaCalendar),
351 0,
352 (GInstanceInitFunc) rubrica_calendar_init
353 };
354
355 calendar_type = g_type_register_static (GTK_TYPE_HBOX,
356 "RubricaCalendar",
357 &calendar_info, 0);
358 }
359
360 return calendar_type;
361 }
362
363
364 static void
rubrica_calendar_dispose(RubricaCalendar * self)365 rubrica_calendar_dispose (RubricaCalendar* self)
366 {
367 RubricaCalendarPrivate* priv;
368
369 g_return_if_fail(IS_RUBRICA_CALENDAR(self));
370
371 priv = RUBRICA_CALENDAR_GET_PRIVATE(self);
372
373 if (priv->dispose_has_run)
374 return;
375
376 priv->dispose_has_run = TRUE;
377 }
378
379
380 static void
rubrica_calendar_finalize(RubricaCalendar * self)381 rubrica_calendar_finalize (RubricaCalendar* self)
382 {
383 g_return_if_fail(IS_RUBRICA_CALENDAR(self));
384 }
385
386
387 static void
rubrica_calendar_class_init(RubricaCalendarClass * klass)388 rubrica_calendar_class_init(RubricaCalendarClass* klass)
389 {
390 GObjectClass *object_class = G_OBJECT_CLASS (klass);
391 GParamSpec* pspec;
392
393 parent_class = g_type_class_peek_parent (klass);
394
395 object_class->finalize = (GObjectFinalizeFunc) rubrica_calendar_finalize;
396 object_class->dispose = (GObjectFinalizeFunc) rubrica_calendar_dispose;
397
398 object_class->set_property = (gpointer) rubrica_calendar_set_property;
399 object_class->get_property = (gpointer) rubrica_calendar_get_property;
400
401 g_type_class_add_private (klass, sizeof(RubricaCalendarPrivate));
402
403 /**
404 */
405 calendar_signals[CALENDAR_CHANGE] =
406 g_signal_new("calendar-change",
407 RUBRICA_CALENDAR_TYPE,
408 G_SIGNAL_RUN_LAST,
409 G_STRUCT_OFFSET(RubricaCalendarClass, calendar_change),
410 NULL,
411 NULL,
412 g_cclosure_marshal_VOID__POINTER,
413 G_TYPE_NONE, /* return type */
414 1, /* params */
415 G_TYPE_POINTER); /* params type: error code */
416
417 pspec = g_param_spec_int("calendar-day",
418 "day",
419 "day",
420 0, // min
421 31, // max
422 0, // default, invalid day
423 G_PARAM_READWRITE);
424 g_object_class_install_property(object_class, CALENDAR_DAY, pspec);
425
426 pspec = g_param_spec_int("calendar-month",
427 "month",
428 "month",
429 0, // min
430 12, // max
431 0, // default, invalid month
432 G_PARAM_READWRITE);
433 g_object_class_install_property(object_class, CALENDAR_MONTH, pspec);
434
435 pspec = g_param_spec_int("calendar-year",
436 "year",
437 "year",
438 0, // min
439 10000, // max
440 0, // default, invalid year
441 G_PARAM_READWRITE);
442 g_object_class_install_property(object_class, CALENDAR_YEAR, pspec);
443 }
444
445
446 static void
rubrica_calendar_init(RubricaCalendar * self)447 rubrica_calendar_init(RubricaCalendar* self)
448 {
449 GtkWidget* frame;
450 GtkWidget* arrow;
451 GtkIconTheme* theme;
452 GtkWidget* image;
453 GdkPixbuf* pixbuf;
454 RubricaCalendarPrivate* priv;
455
456 priv = RUBRICA_CALENDAR_GET_PRIVATE(self);
457
458 gtk_box_set_homogeneous(GTK_BOX(self), FALSE);
459
460 theme = gtk_icon_theme_get_default();
461 pixbuf = gtk_icon_theme_load_icon(theme, "stock_calendar",
462 GTK_ICON_SIZE_BUTTON,
463 GTK_ICON_LOOKUP_USE_BUILTIN, NULL);
464
465 priv->date = g_date_new();
466 g_date_clear (priv->date, 1);
467 g_date_set_time_t(priv->date, time(NULL));
468
469 priv->day = 0;
470 priv->month = 0;
471 priv->year = 0;
472 priv->entry = gtk_entry_new();
473 priv->button = gtk_toggle_button_new ();
474 priv->popup = gtk_window_new (GTK_WINDOW_POPUP);
475 priv->calendar = gtk_calendar_new ();
476
477 gtk_entry_set_editable(GTK_ENTRY(priv->entry), FALSE);
478 gtk_entry_set_text(GTK_ENTRY(priv->entry), _("unknown"));
479 gtk_box_pack_start (GTK_BOX (self), priv->entry, TRUE, TRUE, 0);
480 gtk_widget_show (priv->entry);
481
482 gtk_box_pack_start (GTK_BOX (self), priv->button, FALSE, FALSE, 0);
483 if (pixbuf)
484 {
485 image = gtk_image_new_from_pixbuf(pixbuf);
486 gtk_button_set_image(GTK_BUTTON(priv->button), image);
487 gtk_button_set_relief(GTK_BUTTON(priv->button), GTK_RELIEF_NONE);
488 gtk_widget_show (image);
489
490 gdk_pixbuf_unref (pixbuf);
491 }
492 else
493 {
494 arrow = (GtkWidget *) gtk_arrow_new (GTK_ARROW_DOWN, GTK_SHADOW_NONE);
495 gtk_container_add (GTK_CONTAINER (priv->button), arrow);
496 gtk_widget_show (arrow);
497 }
498 gtk_widget_show (priv->button);
499
500 frame = gtk_frame_new(NULL);
501 gtk_frame_set_shadow_type(GTK_FRAME(frame), GTK_SHADOW_ETCHED_IN);
502 gtk_widget_show(frame);
503
504 gtk_window_set_resizable (GTK_WINDOW (priv->popup), FALSE);
505 gtk_container_add(GTK_CONTAINER(priv->popup), frame);
506 gtk_widget_set_events (priv->popup, gtk_widget_get_events (priv->popup) |
507 GDK_KEY_PRESS_MASK);
508
509 gtk_container_add (GTK_CONTAINER (frame), priv->calendar);
510 gtk_widget_show (priv->calendar);
511
512 g_signal_connect (G_OBJECT(priv->button), "toggled",
513 G_CALLBACK (on_button_toggled_cb), self);
514
515 g_signal_connect (priv->popup, "delete_event",
516 G_CALLBACK (on_delete_popup), self);
517 g_signal_connect (priv->popup, "key_press_event",
518 G_CALLBACK (on_popup_key_press), self);
519 g_signal_connect (priv->popup, "button_press_event",
520 G_CALLBACK (on_popup_button_press), self);
521
522 g_signal_connect (G_OBJECT (priv->calendar), "day-selected",
523 G_CALLBACK (on_day_selected), self);
524 g_signal_connect (G_OBJECT (priv->calendar), "day-selected-double-click",
525 G_CALLBACK (on_day_selected_double), self);
526
527 priv->dispose_has_run = FALSE;
528 }
529
530
531 static void
rubrica_calendar_set_property(GObject * obj,guint property_id,GValue * value,GParamSpec * spec)532 rubrica_calendar_set_property (GObject* obj, guint property_id,
533 GValue* value, GParamSpec* spec)
534 {
535 RubricaCalendar* self = (RubricaCalendar*) obj;
536 RubricaCalendarPrivate* priv = RUBRICA_CALENDAR_GET_PRIVATE(self);
537
538 switch (property_id)
539 {
540 case CALENDAR_DAY:
541 priv->day = g_value_get_int(value);
542 break;
543
544 case CALENDAR_MONTH:
545 priv->month = g_value_get_int(value);
546 break;
547
548 case CALENDAR_YEAR:
549 priv->year = g_value_get_int(value);
550 break;
551
552 default:
553 break;
554 }
555
556 update_calendar(self);
557 }
558
559
560 static void
rubrica_calendar_get_property(GObject * obj,guint property_id,GValue * value,GParamSpec * spec)561 rubrica_calendar_get_property (GObject* obj, guint property_id,
562 GValue* value, GParamSpec* spec)
563 {
564 RubricaCalendar* self = (RubricaCalendar*) obj;
565 RubricaCalendarPrivate* priv = RUBRICA_CALENDAR_GET_PRIVATE(self);
566
567 switch (property_id)
568 {
569 case CALENDAR_DAY:
570 g_value_set_int(value, priv->day);
571 break;
572
573 case CALENDAR_MONTH:
574 g_value_set_int(value, priv->month);
575 break;
576
577 case CALENDAR_YEAR:
578 g_value_set_int(value, priv->year);
579 break;
580
581 default:
582 break;
583 }
584 }
585
586
587
588
589 /* Public
590 */
591 /**
592 * rubrica_calendar_new
593 *
594 * create a #RubricaCalendar
595 *
596 * returns: a new #RubricaCalendar*
597 */
598 GtkWidget*
rubrica_calendar_new()599 rubrica_calendar_new()
600 {
601 GtkWidget* calendar;
602
603 calendar = GTK_WIDGET(g_object_new(RUBRICA_CALENDAR_TYPE, NULL));
604
605 return calendar;
606 }
607
608
609 GtkWidget*
rubrica_calendar_get_button(RubricaCalendar * cal)610 rubrica_calendar_get_button (RubricaCalendar *cal)
611 {
612 RubricaCalendarPrivate* priv;
613
614 g_return_val_if_fail(IS_RUBRICA_CALENDAR(cal), NULL);
615
616 priv = RUBRICA_CALENDAR_GET_PRIVATE(cal);
617
618 return priv->button;
619 }
620
621 gboolean
rubrica_calendar_has_date(RubricaCalendar * cal)622 rubrica_calendar_has_date (RubricaCalendar *cal)
623 {
624 g_return_val_if_fail(IS_RUBRICA_CALENDAR(cal), FALSE);
625
626 return date_is_valid(cal);
627 }
628
629
630 GDate*
rubrica_calendar_get_gdate(RubricaCalendar * cal)631 rubrica_calendar_get_gdate (RubricaCalendar *cal)
632 {
633 RubricaCalendarPrivate *priv;
634
635 g_return_val_if_fail(IS_RUBRICA_CALENDAR(cal), NULL);
636
637 priv = RUBRICA_CALENDAR_GET_PRIVATE (cal);
638
639 if (date_is_valid(cal))
640 return priv->date;
641
642 return NULL;
643 }
644
645
646 gchar*
rubrica_calendar_get_date(RubricaCalendar * cal)647 rubrica_calendar_get_date (RubricaCalendar *cal)
648 {
649 RubricaCalendarPrivate *priv;
650 gchar buffer[BUFSIZE];
651
652 g_return_val_if_fail(IS_RUBRICA_CALENDAR(cal), NULL);
653
654 priv = RUBRICA_CALENDAR_GET_PRIVATE (cal);
655
656 if (date_is_valid(cal))
657 {
658 g_date_set_dmy(priv->date, priv->day, priv->month, priv->year);
659 g_date_strftime(buffer, BUFSIZE, "%Ex", priv->date);
660
661 return g_strdup_printf("%s", buffer);
662 }
663
664 return NULL;
665 }
666
667
668 void
rubrica_calendar_clean(RubricaCalendar * calendar)669 rubrica_calendar_clean(RubricaCalendar* calendar)
670 {
671 RubricaCalendarPrivate *priv;
672
673 g_return_if_fail(IS_RUBRICA_CALENDAR(calendar));
674
675 priv = RUBRICA_CALENDAR_GET_PRIVATE (calendar);
676
677 priv->day = 0;
678 priv->month = 0;
679 priv->year = 0;
680
681 update_calendar (calendar);
682 }
683