1 /* ************************************************************************** */
2 /*                                                                            */
3 /*     Copyright (C)	2000-2008 Cédric Auger (cedric@grisbi.org)	      */
4 /*			2003-2008 Benjamin Drieu (bdrieu@april.org)	      */
5 /*			https://www.grisbi.org/  			      */
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 
23 /**
24  * \file gsb_calendar_entry.c
25  * this is a new widget, an entry for containing a date
26  * that entry controls the calendar, the check of the date...
27  *
28  * */
29 
30 #ifdef HAVE_CONFIG_H
31 #include "config.h"
32 #endif
33 
34 #include "include.h"
35 #include <ctype.h>
36 #include <gdk/gdkkeysyms.h>
37 #include <glib/gi18n.h>
38 
39 /*START_INCLUDE*/
40 #include "gsb_calendar_entry.h"
41 #include "grisbi_app.h"
42 #include "gsb_form_widget.h"
43 #include "structures.h"
44 #include "utils_dates.h"
45 /*END_INCLUDE*/
46 
47 /*START_STATIC*/
48 static gboolean gsb_calendar_entry_button_press ( GtkWidget *entry,
49 					   GdkEventButton *event,
50 					   gpointer null );
51 static gboolean gsb_calendar_entry_calendar_key_press ( GtkCalendar *pCalendar,
52 						 GdkEventKey *ev,
53 						 GtkWidget *entry );
54 static gboolean gsb_calendar_entry_changed ( GtkWidget *entry,
55 				      gpointer null );
56 static gboolean gsb_calendar_entry_focus_out ( GtkWidget *entry,
57 					GdkEventFocus *event,
58 					gint *set_today );
59 static gboolean gsb_calendar_entry_key_press ( GtkWidget *entry,
60 					GdkEventKey  *ev,
61 					gpointer null );
62 static GtkWidget *gsb_calendar_entry_popup ( GtkWidget *entry );
63 static gboolean gsb_calendar_entry_select_date ( GtkCalendar *pCalendar,
64 					  GtkWidget *entry );
65 static void gsb_calendar_entry_step_date ( GtkWidget *entry,
66 				    gint movement );
67 /*END_STATIC*/
68 
69 
70 
71 /*START_EXTERN*/
72 /*END_EXTERN*/
73 
74 #define ENTRY_NORMAL 0
75 #define ENTRY_RED 1
76 
77 
78 /**
79  * create a new entry for contain a date
80  * if double-click on that entry, popup a calendar
81  * when leave the entry, check the date and complete it
82  *
83  * \param set_today TRUE if we want to set the current day if the entry is left empty
84  *
85  * \return a GtkEntry widget
86  * */
gsb_calendar_entry_new(gint set_today)87 GtkWidget *gsb_calendar_entry_new (gint set_today)
88 {
89     GtkWidget *entry;
90 
91     entry = gtk_entry_new ();
92 	gsb_calendar_entry_new_from_ui (entry, set_today);
93 
94     return entry;
95 }
96 
97 /**
98  * init a new entry for contain a date
99  * if double-click on that entry, popup a calendar
100  * when leave the entry, check the date and complete it
101  *
102  * \param entry		entry from ui
103  * \param set_today TRUE if we want to set the current day if the entry is left empty
104  *
105  * \return
106  **/
gsb_calendar_entry_new_from_ui(GtkWidget * entry,gint set_today)107 void gsb_calendar_entry_new_from_ui (GtkWidget *entry,
108 									 gint set_today)
109 {
110     gtk_widget_set_size_request (entry, ENTRY_MIN_WIDTH_1, -1);
111 
112     g_signal_connect ( G_OBJECT (entry),
113 					  "button-press-event",
114 					  G_CALLBACK (gsb_calendar_entry_button_press),
115 					  NULL);
116     g_signal_connect_after (G_OBJECT (entry),
117 							"focus-out-event",
118 							G_CALLBACK (gsb_calendar_entry_focus_out),
119 							GINT_TO_POINTER (set_today));
120     g_signal_connect (G_OBJECT (entry),
121 					  "key-press-event",
122 					  G_CALLBACK (gsb_calendar_entry_key_press),
123 					  NULL);
124     g_signal_connect (G_OBJECT (entry),
125 					  "changed",
126 					  G_CALLBACK (gsb_calendar_entry_changed),
127 					  NULL);
128 }
129 
130 /**
131  * set the date in the date entry
132  *
133  * \param entry
134  * \param date a GDate or NULL to set nothing
135  *
136  * \return FALSE if problem, TRUE if ok
137  * */
gsb_calendar_entry_set_date(GtkWidget * entry,const GDate * date)138 gboolean gsb_calendar_entry_set_date ( GtkWidget *entry,
139 				       const GDate *date )
140 {
141     gchar *string;
142 
143     if (!entry
144 	||
145 	!GTK_IS_ENTRY (entry))
146         return FALSE;
147 
148     if (!date
149 	||
150 	!g_date_valid (date))
151     {
152         gtk_entry_set_text ( GTK_ENTRY (entry), "" );
153         return FALSE;
154     }
155 
156     string = gsb_format_gdate ( date );
157     gtk_entry_set_text ( GTK_ENTRY ( entry ), string );
158     g_free ( string );
159     return TRUE;
160 }
161 
162 
163 /**
164  * get the date in the date entry
165  *
166  * \param entry
167  *
168  * \return a newly allocated GDate or NULL if invalid date
169  * */
gsb_calendar_entry_get_date(GtkWidget * entry)170 GDate *gsb_calendar_entry_get_date ( GtkWidget *entry )
171 {
172     GDate *date;
173 
174     if (!entry
175 	||
176 	!GTK_IS_ENTRY (entry)
177 	||
178 	!gsb_date_check_entry (entry))
179 	return NULL;
180 
181     date = gsb_date_get_last_entry_date (gtk_entry_get_text (GTK_ENTRY (entry)));
182     return date;
183 }
184 
185 
186 
187 /**
188  * manual set of the color of the entry
189  * used for example in reconciliation, if cancel it, to set back the good color
190  * of the entry
191  *
192  * \param entry
193  * \param normal_color TRUE if we want to set the normal color, FALSE to set to red
194  *
195  * \return FALSE
196  * */
gsb_calendar_entry_set_color(GtkWidget * entry,gboolean normal_color)197 gboolean gsb_calendar_entry_set_color ( GtkWidget *entry,
198 					gboolean normal_color )
199 {
200     if (!entry)
201         return FALSE;
202 
203     if (normal_color)
204     {
205 		if (gsb_form_widget_check_empty (entry))
206 			gtk_widget_set_name (entry, "form_entry_empty");
207 		else
208         	gtk_widget_set_name (entry, "form_entry");
209     }
210     else
211     {
212         gtk_widget_set_name (entry, "form_entry_error");
213     }
214 
215     return FALSE;
216 }
217 
218 
219 
220 /**
221  * callback called when press button on the date entry
222  * used to popup a calendar if double click
223  *
224  * \param entry the entry which receive the signal
225  * \param event
226  * \param null not used
227  *
228  * \return FALSE
229  * */
gsb_calendar_entry_button_press(GtkWidget * entry,GdkEventButton * event,gpointer null)230 gboolean gsb_calendar_entry_button_press ( GtkWidget *entry,
231 					   GdkEventButton *event,
232 					   gpointer null )
233 {
234     if ( event -> type == GDK_2BUTTON_PRESS )
235 	gsb_calendar_entry_popup (entry);
236 
237     return FALSE;
238 }
239 
240 
241 /**
242  * callback called when press a key on the date entry
243  * used to increase/decrease the date and popup the calendar
244  *
245  * \param entry the entry which receive the signal
246  * \param event
247  * \param null not used
248  *
249  * \return FALSE
250  * */
gsb_calendar_entry_key_press(GtkWidget * entry,GdkEventKey * ev,gpointer null)251 gboolean gsb_calendar_entry_key_press ( GtkWidget *entry,
252 					GdkEventKey  *ev,
253 					gpointer null )
254 {
255     switch ( ev -> keyval )
256     {
257 	case GDK_KEY_KP_Enter :
258 	case GDK_KEY_Return :
259 
260 	    /* CONTROL ENTER opens the calendar */
261 	    if ( ( ev -> state & GDK_CONTROL_MASK ) == GDK_CONTROL_MASK)
262 		gsb_calendar_entry_popup (entry);
263 	    break;
264 
265 	case GDK_KEY_KP_Add:
266 	case GDK_KEY_plus:
267 	case GDK_KEY_equal:		/* This should make all our US users happy */
268 
269 	    /* increase the date of 1 day/week */
270 	    if ( ( ev -> state & GDK_CONTROL_MASK ) != GDK_CONTROL_MASK ||
271 		 ev -> keyval != GDK_KEY_KP_Add )
272 		gsb_calendar_entry_step_date ( entry, ONE_DAY );
273 	    else
274 		gsb_calendar_entry_step_date ( entry, ONE_WEEK );
275 	    return TRUE;
276 	    break;
277 
278 	case GDK_KEY_KP_Subtract:
279 	case GDK_KEY_minus:
280 		{
281 			const gchar *date_format;
282 
283 			date_format = gsb_date_get_format_date ();
284 			if (g_strcmp0 (date_format, "%Y-%m-%d"))
285 			{
286 				/* decrease the date of 1 day/week, or the check of 1 */
287 				if ( ( ev -> state & GDK_CONTROL_MASK ) != GDK_CONTROL_MASK ||
288 				 ev -> keyval != GDK_KEY_KP_Subtract  )
289 					gsb_calendar_entry_step_date ( entry, -ONE_DAY );
290 				else
291 					gsb_calendar_entry_step_date ( entry, -ONE_WEEK );
292 				return TRUE;
293 			}
294 			break;
295 		}
296 
297 	case GDK_KEY_Page_Up :
298 	case GDK_KEY_KP_Page_Up :
299 
300 	    /* increase the date of 1 month/year */
301 	    if ( ( ev -> state & GDK_CONTROL_MASK ) != GDK_CONTROL_MASK )
302 		gsb_calendar_entry_step_date ( entry,
303 			       ONE_MONTH );
304 	    else
305 		gsb_calendar_entry_step_date ( entry,
306 			       ONE_YEAR );
307 
308 	    return TRUE;
309 	    break;
310 
311 	case GDK_KEY_Page_Down :
312 	case GDK_KEY_KP_Page_Down :
313 
314 	    /* decrease the date of 1 month/year */
315 	    if ( ( ev -> state & GDK_CONTROL_MASK ) != GDK_CONTROL_MASK )
316 		gsb_calendar_entry_step_date ( entry,
317 			       -ONE_MONTH );
318 	    else
319 		gsb_calendar_entry_step_date ( entry,
320 			       -ONE_YEAR );
321 
322 	    return TRUE;
323 	    break;
324     }
325     return FALSE;
326 }
327 
328 
329 /**
330  * increase or decrease the date in the entry date
331  *
332  * \param entry
333  * \param movement + or - ONE_DAY, ONE_WEEK, ONE_MONTH, ONE_YEAR
334  *
335  * \return
336  * */
gsb_calendar_entry_step_date(GtkWidget * entry,gint movement)337 void gsb_calendar_entry_step_date ( GtkWidget *entry,
338 				    gint movement )
339 {
340     GDate *date;
341     gchar *string;
342 
343     /* on commence par vérifier que la date est valide */
344 
345     if ( !gsb_date_check_and_complete_entry ( entry, TRUE ) )
346 	return;
347 
348     date = gsb_date_get_last_entry_date ( gtk_entry_get_text ( GTK_ENTRY ( entry )));
349 
350     switch ( movement )
351     {
352 	case ONE_DAY :
353 	case ONE_WEEK :
354 
355 	    g_date_add_days ( date, movement ) ;
356 	    break ;
357 
358 	case -ONE_DAY :
359 	case -ONE_WEEK :
360 
361 	    g_date_subtract_days ( date, -movement ) ;
362 	    break ;
363 
364 	case ONE_MONTH :
365 
366 	    g_date_add_months ( date, 1 ) ;
367 	    break ;
368 
369 	case -ONE_MONTH :
370 
371 	    g_date_subtract_months ( date, 1 ) ;
372 	    break ;
373 
374 	case ONE_YEAR :
375 
376 	    g_date_add_years ( date, 1 ) ;
377 	    break ;
378 
379 	case -ONE_YEAR :
380 
381 	    g_date_subtract_years ( date, 1 ) ;
382 	    break ;
383 
384 	default :
385 	    break ;
386     }
387 
388     string = gsb_format_gdate (date);
389     gtk_entry_set_text ( GTK_ENTRY ( entry ), string );
390     g_date_free (date);
391     g_free (string);
392 }
393 
394 
395 /**
396  * callback called on focus-out on the date entry
397  * complete and check the date
398  *
399  * \param entry the entry which receive the signal
400  * \param event
401  * \param set_today TRUE (but pointer) if we want to set the current day if the entry is left empty
402  *
403  * \return FALSE
404  * */
gsb_calendar_entry_focus_out(GtkWidget * entry,GdkEventFocus * event,gint * set_today)405 gboolean gsb_calendar_entry_focus_out ( GtkWidget *entry,
406 					GdkEventFocus *event,
407 					gint *set_today )
408 {
409     gint valid;
410 
411     valid = gsb_date_check_and_complete_entry (entry, GPOINTER_TO_INT (set_today));
412     gsb_calendar_entry_set_color ( entry, valid ) ;
413 
414     return FALSE;
415 }
416 
417 /**
418  * called when date changed
419  * check the date and set the entry red/invalid if not a good date
420  *
421  * \param entry
422  * \param null
423  *
424  * \return FALSE
425  * */
gsb_calendar_entry_changed(GtkWidget * entry,gpointer null)426 gboolean gsb_calendar_entry_changed ( GtkWidget *entry,
427 				      gpointer null )
428 {
429     GDate *date;
430 
431     /* if we are in the form and the entry is empty, do nothing
432      * because it's a special style too */
433     if (gsb_form_widget_check_empty (entry))
434 	return FALSE;
435 
436     /* if nothing in the entry, keep the normal style */
437     if (!strlen (gtk_entry_get_text (GTK_ENTRY (entry))))
438     {
439 	gsb_calendar_entry_set_color ( entry, TRUE );
440 	return FALSE;
441     }
442 
443     /* to check the date, we just try to see if can have a dote from the entry */
444     date = gsb_date_get_last_entry_date (gtk_entry_get_text (GTK_ENTRY (entry)));
445 
446     if (date)
447     {
448 	/* the date is valid, make it normal */
449 	gsb_calendar_entry_set_color ( entry, TRUE );
450 	g_date_free (date);
451     }
452     else
453     {
454 	/* the date is not valid, make it red */
455 	gsb_calendar_entry_set_color ( entry, FALSE );
456     }
457 
458     return FALSE;
459 }
460 
461 
462 /** popup a calendar next to the entry
463  *
464  * \param entry the date entry
465  *
466  * \return a GtkWindow which contains the calendar
467  * */
gsb_calendar_entry_popup(GtkWidget * entry)468 GtkWidget *gsb_calendar_entry_popup ( GtkWidget *entry )
469 {
470     GtkWidget *popup, *pVBox, *pCalendar, *button, *frame;
471 	GdkWindow *window;
472     GtkRequisition popup_size;
473     gint x, y;
474     GDate * date;
475 
476     /* make the popup */
477     popup = gtk_window_new ( GTK_WINDOW_TOPLEVEL );
478     gtk_window_set_modal ( GTK_WINDOW ( popup ), TRUE );
479     gtk_window_set_transient_for ( GTK_WINDOW ( popup ),
480                         GTK_WINDOW ( grisbi_app_get_active_window (NULL) ) );
481     gtk_window_set_decorated ( GTK_WINDOW ( popup ), FALSE );
482 
483     /* set the decoration */
484     frame = gtk_frame_new ( NULL );
485     gtk_container_add ( GTK_CONTAINER ( popup ), frame );
486     gtk_widget_show ( frame );
487 
488     pVBox = gtk_box_new ( GTK_ORIENTATION_VERTICAL, MARGIN_BOX );
489     gtk_container_set_border_width ( GTK_CONTAINER ( pVBox ), 5 );
490     gtk_container_add ( GTK_CONTAINER ( frame ), pVBox );
491     gtk_widget_show ( pVBox );
492 
493     /* get the date */
494     date = gsb_calendar_entry_get_date (entry);
495     if (!date)
496 	date = gdate_today ();
497 
498     /* set the calendar */
499     pCalendar = gtk_calendar_new();
500     gtk_calendar_select_month ( GTK_CALENDAR ( pCalendar ),
501 				g_date_get_month ( date ) - 1,
502 				g_date_get_year ( date ) );
503     gtk_calendar_select_day ( GTK_CALENDAR ( pCalendar ), g_date_get_day ( date ) );
504 
505     g_signal_connect ( G_OBJECT ( pCalendar ),
506 		       "day_selected_double_click",
507 		       G_CALLBACK ( gsb_calendar_entry_select_date ),
508 		       entry );
509     g_signal_connect ( G_OBJECT ( pCalendar ),
510 		       "key-press-event",
511 		       G_CALLBACK ( gsb_calendar_entry_calendar_key_press ),
512 		       entry );
513     gtk_box_pack_start ( GTK_BOX ( pVBox ),
514 			 pCalendar,
515 			 TRUE,
516 			 TRUE,
517 			 0 );
518     gtk_widget_show ( pCalendar );
519 
520     /* cancel button */
521     button = gtk_button_new_with_mnemonic ( _("_Cancel") );
522     g_signal_connect_swapped ( G_OBJECT ( button ),
523 			       "clicked",
524 			       G_CALLBACK ( gtk_widget_destroy ),
525 			       G_OBJECT ( popup ));
526     gtk_box_pack_start ( GTK_BOX ( pVBox ),
527 			 button,
528 			 TRUE,
529 			 TRUE,
530 			 0 );
531     gtk_widget_show ( button );
532 
533     /* set the position */
534 	window = gtk_widget_get_window (GTK_WIDGET (entry));
535     gdk_window_get_origin (window, &x, &y );
536 
537     /* on récupère la taille de la popup */
538     gtk_widget_get_preferred_size (GTK_WIDGET (popup), &popup_size, NULL);
539 
540     /* pour la soustraire à la position de l'entrée date */
541     y -= popup_size.height;
542 
543 #if GTK_CHECK_VERSION (3,22,0)
544 	GdkDisplay *display;
545 	GdkMonitor *monitor;
546 	GdkRectangle rectangle;
547 
548 	display = gdk_window_get_display (window);
549 	monitor = gdk_display_get_monitor_at_point (display, x, y);
550 	gdk_monitor_get_geometry (monitor, &rectangle);
551 
552     /* on décale le popup si on est trop près de bord droit de l'écran */
553     if (x > (rectangle.width - popup_size.width))
554         x = rectangle.width - popup_size.width - 10;
555 #else
556     gint screen_width = gdk_screen_width ( );
557 
558     /* on décale le popup si on est trop près de bord droit de l'écran */
559     if ( x > ( screen_width - popup_size.width ) )
560         x = screen_width - popup_size.width - 10;
561 #endif
562 
563     /* si une des coordonnées est négative, alors la fonction
564        gtk_window_move échoue et affiche la popup en 0,0 */
565     if ( x < 0 )
566 	x = 0 ;
567 
568     if ( y < 0 )
569 	y = 0 ;
570 
571     gtk_window_move ( GTK_WINDOW ( popup ), x, y );
572     gtk_widget_show ( popup );
573     gtk_widget_grab_focus ( GTK_WIDGET ( pCalendar ) );
574     return ( popup );
575 }
576 
577 
578 /**
579  * called with a double-click on a date on a calendar
580  * set the choosen date in the entry
581  *
582  * \param pCalendar
583  * \param entry
584  *
585  * \return FALSE
586  * */
gsb_calendar_entry_select_date(GtkCalendar * pCalendar,GtkWidget * entry)587 gboolean gsb_calendar_entry_select_date ( GtkCalendar *pCalendar,
588 					  GtkWidget *entry )
589 {
590     guint year, month, day;
591     GtkWidget *pTopLevelWidget;
592 
593     /* get the popup to destroy it if we are in a popup */
594     pTopLevelWidget = gtk_widget_get_toplevel ( GTK_WIDGET ( pCalendar ) );
595 
596     gtk_calendar_get_date ( pCalendar, &year, &month, &day);
597 
598     gtk_entry_set_text ( GTK_ENTRY ( entry ),
599 			 gsb_format_date ( day, month + 1, year ));
600     gsb_form_widget_set_empty ( entry, FALSE );
601     if ( gtk_widget_is_toplevel ( pTopLevelWidget ) )
602         gtk_widget_destroy ( pTopLevelWidget );
603 
604     return FALSE;
605 }
606 
607 
608 /**
609  * called when a calendar receive a key-press-event
610  *
611  * \param pCalendar
612  * \param ev
613  * \param entry
614  *
615  * \return TRUE to block the signal
616  * */
gsb_calendar_entry_calendar_key_press(GtkCalendar * pCalendar,GdkEventKey * ev,GtkWidget * entry)617 gboolean gsb_calendar_entry_calendar_key_press ( GtkCalendar *pCalendar,
618 						 GdkEventKey *ev,
619 						 GtkWidget *entry )
620 {
621     guint day, month, year;
622     GDate *date;
623     GtkWidget *pTopLevelWidget;
624 
625     /* get the popup to destroy it if need */
626     pTopLevelWidget = gtk_widget_get_toplevel ( GTK_WIDGET ( pCalendar ) );
627 
628     /* most of the time, we will use date so can get it here,
629      * think about free it if not used */
630     gtk_calendar_get_date ( pCalendar, &year, &month, &day );
631     month++;
632     date = g_date_new_dmy (day, month, year);
633 
634     switch ( ev -> keyval )
635     {
636 	case GDK_KEY_Escape :
637 	    /* just close the calendar if it's a popup */
638 	    if ( gtk_widget_is_toplevel ( pTopLevelWidget ) )
639 		gtk_widget_destroy ( pTopLevelWidget );
640 	    g_date_free (date);
641 	    return TRUE;
642 	    break ;
643 
644 	case GDK_KEY_Return :
645 	case GDK_KEY_KP_Enter :
646 	    /* get the date an close the calendar */
647 	    gtk_entry_set_text ( GTK_ENTRY ( entry ),
648 				 gsb_format_date ( day, month, year ));
649 	    if ( gtk_widget_is_toplevel ( pTopLevelWidget ) )
650 		gtk_widget_destroy ( pTopLevelWidget );
651 	    g_date_free (date);
652 	    return TRUE;
653 	    break ;
654 
655 	    /* from now, it will change date so just use date, modify it and fill day, month, year
656 	     * we will set the calendar at the end of that function
657 	     * so after now, only keys which change the date */
658 	case GDK_KEY_Left :
659 	case GDK_KEY_KP_Left:
660 	case GDK_KEY_minus:
661 	case GDK_KEY_KP_Subtract:
662 	    /* day before */
663 	    g_date_subtract_days (date, 1);
664 	    break ;
665 
666 	case GDK_KEY_Right :
667 	case GDK_KEY_KP_Right:
668 	case GDK_KEY_plus:
669 	case GDK_KEY_KP_Add:
670 	    /* day after */
671 	    g_date_add_days (date, 1);
672 	    break ;
673 
674 	case GDK_KEY_Up :
675 	case GDK_KEY_KP_Up :
676 	    /* prev week */
677 	    g_date_subtract_days (date, 7);
678 	    break ;
679 
680 	case GDK_KEY_Down :
681 	case GDK_KEY_KP_Down :
682 	    /* next week */
683 	    g_date_add_days (date, 7);
684 	    break ;
685 
686 	case GDK_KEY_Home :
687 	case GDK_KEY_KP_Home :
688 	    /* go to first day of the month */
689 	    g_date_set_day (date, 1);
690 	    break ;
691 
692 	case GDK_KEY_End :
693 	case GDK_KEY_KP_End :
694 	    /* go to last day of the month */
695 	    g_date_set_day (date,
696 			    g_date_get_days_in_month (month, year));
697 	    break ;
698 
699 	case GDK_KEY_Page_Up :
700 	case GDK_KEY_KP_Page_Up :
701 	    /* prev month */
702 	    g_date_subtract_months (date, 1);
703 	    break ;
704 
705 	case GDK_KEY_Page_Down :
706 	case GDK_KEY_KP_Page_Down :
707 	    /* next month */
708 	    g_date_add_months (date, 1);
709 	    break ;
710 
711 	default:
712 	    return TRUE;
713     }
714 
715     day = g_date_get_day (date);
716     month = g_date_get_month (date);
717     year = g_date_get_year (date);
718     g_date_free (date);
719 
720     /* to avoid a warning */
721     gtk_calendar_select_day( pCalendar , 15 );
722 
723     month--;
724     gtk_calendar_select_month ( pCalendar , month, year );
725     gtk_calendar_select_day( pCalendar , day );
726     return TRUE;
727 }
728 
729 
730 /* Local Variables: */
731 /* c-basic-offset: 4 */
732 /* End: */
733