]> Pileus Git - ~andy/gtk/blob - gtk/gtkcalendar.c
Get the information about the first day of the week from the locale using
[~andy/gtk] / gtk / gtkcalendar.c
1 /* GTK - The GIMP Toolkit
2  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
3  *
4  * GTK Calendar Widget
5  * Copyright (C) 1998 Cesar Miquel, Shawn T. Amundson and Mattias Grönlund
6  * 
7  * lib_date routines
8  * Copyright (c) 1995, 1996, 1997, 1998 by Steffen Beyer
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free
22  * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23  */
24
25 /*
26  * Modified by the GTK+ Team and others 1997-2000.  See the AUTHORS
27  * file for a list of people on the GTK+ Team.  See the ChangeLog
28  * files for a list of changes.  These files are distributed with
29  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
30  */
31
32 #include "config.h"
33
34 #ifdef HAVE_SYS_TIME_H
35 #include <sys/time.h>
36 #endif
37 #include <string.h>
38 #include <stdlib.h>
39 #include <time.h>
40 #include <glib/gprintf.h>
41
42 #include "gtkcalendar.h"
43 #include "gtkintl.h"
44 #include "gtkmain.h"
45 #include "gtkmarshalers.h"
46 #include "gtkintl.h"
47 #include "gdk/gdkkeysyms.h"
48
49 /***************************************************************************/
50 /* The following date routines are taken from the lib_date package.  Keep
51  * them seperate in case we want to update them if a newer lib_date comes
52  * out with fixes.  */
53
54 typedef  unsigned   int     N_int;
55 typedef  unsigned   long    N_long;
56 typedef  signed     long    Z_long;
57 typedef enum { false = FALSE , true = TRUE } boolean;
58
59 #define and         &&      /* logical (boolean) operators: lower case */
60 #define or          ||
61
62 static const N_int month_length[2][13] =
63 {
64   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
65   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
66 };
67
68 static const N_int days_in_months[2][14] =
69 {
70   { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
71   { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
72 };
73
74 static Z_long  calc_days(N_int year, N_int mm, N_int dd);
75 static N_int   day_of_week(N_int year, N_int mm, N_int dd);
76 static Z_long  dates_difference(N_int year1, N_int mm1, N_int dd1,
77                                 N_int year2, N_int mm2, N_int dd2);
78 static N_int   weeks_in_year(N_int year);
79
80 static boolean 
81 leap(N_int year)
82 {
83   return((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
84 }
85
86 static N_int 
87 day_of_week(N_int year, N_int mm, N_int dd)
88 {
89   Z_long  days;
90   
91   days = calc_days(year, mm, dd);
92   if (days > 0L)
93     {
94       days--;
95       days %= 7L;
96       days++;
97     }
98   return( (N_int) days );
99 }
100
101 static N_int weeks_in_year(N_int year)
102 {
103   return(52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
104 }
105
106 static boolean 
107 check_date(N_int year, N_int mm, N_int dd)
108 {
109   if (year < 1) return(false);
110   if ((mm < 1) or (mm > 12)) return(false);
111   if ((dd < 1) or (dd > month_length[leap(year)][mm])) return(false);
112   return(true);
113 }
114
115 static N_int 
116 week_number(N_int year, N_int mm, N_int dd)
117 {
118   N_int first;
119   
120   first = day_of_week(year,1,1) - 1;
121   return( (N_int) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
122           (first < 4) );
123 }
124
125 static Z_long 
126 year_to_days(N_int year)
127 {
128   return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
129 }
130
131
132 static Z_long 
133 calc_days(N_int year, N_int mm, N_int dd)
134 {
135   boolean lp;
136   
137   if (year < 1) return(0L);
138   if ((mm < 1) or (mm > 12)) return(0L);
139   if ((dd < 1) or (dd > month_length[(lp = leap(year))][mm])) return(0L);
140   return( year_to_days(--year) + days_in_months[lp][mm] + dd );
141 }
142
143 static boolean 
144 week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
145 {
146   if (check_date(*year,mm,dd))
147     {
148       *week = week_number(*year,mm,dd);
149       if (*week == 0) 
150         *week = weeks_in_year(--(*year));
151       else if (*week > weeks_in_year(*year))
152         {
153           *week = 1;
154           (*year)++;
155         }
156       return(true);
157     }
158   return(false);
159 }
160
161 static Z_long 
162 dates_difference(N_int year1, N_int mm1, N_int dd1,
163                  N_int year2, N_int mm2, N_int dd2)
164 {
165   return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
166 }
167
168 /*** END OF lib_date routines ********************************************/
169
170 /* Spacing around day/week headers and main area, inside those windows */
171 #define CALENDAR_MARGIN          0
172 /* Spacing around day/week headers and main area, outside those windows */
173 #define INNER_BORDER             4
174 /* Separation between day headers and main area */
175 #define CALENDAR_YSEP            4
176 /* Separation between week headers and main area */
177 #define CALENDAR_XSEP            4
178
179 #define DAY_XSEP                 0 /* not really good for small calendar */
180 #define DAY_YSEP                 0 /* not really good for small calendar */
181
182 /* Color usage */
183 #define HEADER_FG_COLOR(widget)          (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
184 #define HEADER_BG_COLOR(widget)          (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
185 #define SELECTED_BG_COLOR(widget)        (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
186 #define SELECTED_FG_COLOR(widget)        (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
187 #define NORMAL_DAY_COLOR(widget)         (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
188 #define PREV_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
189 #define NEXT_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
190 #define MARKED_COLOR(widget)             (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
191 #define BACKGROUND_COLOR(widget)         (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
192 #define HIGHLIGHT_BACK_COLOR(widget)     (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
193
194 enum {
195   ARROW_YEAR_LEFT,
196   ARROW_YEAR_RIGHT,
197   ARROW_MONTH_LEFT,
198   ARROW_MONTH_RIGHT
199 };
200
201 enum {
202   MONTH_PREV,
203   MONTH_CURRENT,
204   MONTH_NEXT
205 };
206
207 enum {
208   MONTH_CHANGED_SIGNAL,
209   DAY_SELECTED_SIGNAL,
210   DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
211   PREV_MONTH_SIGNAL,
212   NEXT_MONTH_SIGNAL,
213   PREV_YEAR_SIGNAL,
214   NEXT_YEAR_SIGNAL,
215   LAST_SIGNAL
216 };
217
218 enum
219 {
220   PROP_0,
221   PROP_YEAR,
222   PROP_MONTH,
223   PROP_DAY,
224   PROP_SHOW_HEADING,
225   PROP_SHOW_DAY_NAMES,
226   PROP_NO_MONTH_CHANGE,
227   PROP_SHOW_WEEK_NUMBERS,
228   PROP_LAST
229 };
230
231 static gint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
232
233 static GtkWidgetClass *parent_class = NULL;
234
235 typedef struct _GtkCalendarPrivateData GtkCalendarPrivateData;
236 struct _GtkCalendarPrivateData
237 {
238   GdkWindow *header_win;
239   GdkWindow *day_name_win;
240   GdkWindow *main_win;
241   GdkWindow *week_win;
242   GdkWindow *arrow_win[4];
243
244   guint header_h;
245   guint day_name_h;
246   guint main_h;
247
248   guint      arrow_state[4];
249   guint      arrow_width;
250   guint      max_month_width;
251   guint      max_year_width;
252   
253   guint day_width;
254   guint week_width;
255
256   guint min_day_width;
257   guint max_day_char_width;
258   guint max_day_char_ascent;
259   guint max_day_char_descent;
260   guint max_label_char_ascent;
261   guint max_label_char_descent;
262   guint max_week_char_width;
263   
264   guint freeze_count;
265
266   /* flags */
267   guint dirty_header : 1;
268   guint dirty_day_names : 1;
269   guint dirty_main : 1;
270   guint dirty_week : 1;
271
272   guint year_before : 1;
273
274   guint need_timer  : 1;
275
276   guint32 timer;
277   gint click_child;
278
279   gint week_start;
280 };
281
282 #define GTK_CALENDAR_PRIVATE_DATA(widget)  (((GtkCalendarPrivateData*)(GTK_CALENDAR (widget)->private_data)))
283
284 typedef void (*GtkCalendarSignalDate) (GtkObject *object, guint arg1, guint arg2, guint arg3, gpointer data);
285
286 static void gtk_calendar_class_init     (GtkCalendarClass *class);
287 static void gtk_calendar_init           (GtkCalendar *calendar);
288 static void gtk_calendar_finalize       (GObject *calendar);
289 static void gtk_calendar_destroy        (GtkObject *calendar);
290 static void gtk_calendar_set_property   (GObject      *object,
291                                          guint         prop_id,
292                                          const GValue *value,
293                                          GParamSpec   *pspec);
294 static void gtk_calendar_get_property   (GObject      *object,
295                                          guint         prop_id,
296                                          GValue       *value,
297                                          GParamSpec   *pspec);
298 static void gtk_calendar_realize        (GtkWidget *widget);
299 static void gtk_calendar_unrealize      (GtkWidget *widget);
300 static void gtk_calendar_size_request   (GtkWidget *widget,
301                                          GtkRequisition *requisition);
302 static void gtk_calendar_size_allocate  (GtkWidget *widget,
303                                          GtkAllocation *allocation);
304 static gint gtk_calendar_expose         (GtkWidget *widget,
305                                          GdkEventExpose *event);
306 static gint gtk_calendar_button_press   (GtkWidget *widget,
307                                          GdkEventButton *event);
308 static gint gtk_calendar_button_release (GtkWidget *widget,
309                                          GdkEventButton *event);
310 static void gtk_calendar_main_button    (GtkWidget *widget,
311                                          GdkEventButton *event);
312 static gint gtk_calendar_motion_notify  (GtkWidget *widget,
313                                          GdkEventMotion *event);
314 static gint gtk_calendar_enter_notify   (GtkWidget *widget,
315                                          GdkEventCrossing *event);
316 static gint gtk_calendar_leave_notify   (GtkWidget *widget,
317                                          GdkEventCrossing *event);
318 static gint gtk_calendar_key_press      (GtkWidget         *widget,
319                                          GdkEventKey       *event);
320 static gint gtk_calendar_scroll         (GtkWidget         *widget,
321                                          GdkEventScroll    *event);
322 static void gtk_calendar_grab_notify    (GtkWidget          *widget,
323                                          gboolean            was_grabbed);
324 static void gtk_calendar_state_changed  (GtkWidget *widget,
325                                          GtkStateType previous_state);
326 static void gtk_calendar_style_set      (GtkWidget *widget,
327                                          GtkStyle  *previous_style);
328 static void gtk_calendar_paint_header       (GtkWidget *widget);
329 static void gtk_calendar_paint_day_names    (GtkWidget *widget);
330 static void gtk_calendar_paint_week_numbers (GtkWidget *widget);
331 static void gtk_calendar_paint_main         (GtkWidget *widget);
332
333 static void gtk_calendar_select_and_focus_day (GtkCalendar *calendar,
334                                                guint        day);
335
336 static void gtk_calendar_paint_arrow    (GtkWidget    *widget,
337                                          guint         arrow);
338 static void gtk_calendar_paint_day_num  (GtkWidget    *widget,
339                                          gint          day);
340 static void gtk_calendar_paint_day      (GtkWidget    *widget,
341                                          gint          row,
342                                          gint          col);
343 static void gtk_calendar_compute_days   (GtkCalendar  *calendar);
344 static gint left_x_for_column           (GtkCalendar  *calendar,
345                                          gint          column);
346 static gint top_y_for_row               (GtkCalendar  *calendar,
347                                          gint          row);
348
349 static char    *default_abbreviated_dayname[7];
350 static char    *default_monthname[12];
351
352 GType
353 gtk_calendar_get_type (void)
354 {
355   static GType calendar_type = 0;
356   
357   if (!calendar_type)
358     {
359       static const GTypeInfo calendar_info =
360       {
361         sizeof (GtkCalendarClass),
362         NULL,           /* base_init */
363         NULL,           /* base_finalize */
364         (GClassInitFunc) gtk_calendar_class_init,
365         NULL,           /* class_finalize */
366         NULL,           /* class_data */
367         sizeof (GtkCalendar),
368         0,              /* n_preallocs */
369         (GInstanceInitFunc) gtk_calendar_init,
370       };
371
372       calendar_type = g_type_register_static (GTK_TYPE_WIDGET, "GtkCalendar",
373                                               &calendar_info, 0);
374     }
375   
376   return calendar_type;
377 }
378
379 static void
380 gtk_calendar_class_init (GtkCalendarClass *class)
381 {
382   GObjectClass   *gobject_class;
383   GtkObjectClass   *object_class;
384   GtkWidgetClass *widget_class;
385
386   gobject_class = (GObjectClass*)  class;
387   object_class = (GtkObjectClass*)  class;
388   widget_class = (GtkWidgetClass*) class;
389   
390   parent_class = g_type_class_peek_parent (class);
391   
392   gobject_class->set_property = gtk_calendar_set_property;
393   gobject_class->get_property = gtk_calendar_get_property;
394   gobject_class->finalize = gtk_calendar_finalize;
395
396   object_class->destroy = gtk_calendar_destroy;
397
398   widget_class->realize = gtk_calendar_realize;
399   widget_class->unrealize = gtk_calendar_unrealize;
400   widget_class->expose_event = gtk_calendar_expose;
401   widget_class->size_request = gtk_calendar_size_request;
402   widget_class->size_allocate = gtk_calendar_size_allocate;
403   widget_class->button_press_event = gtk_calendar_button_press;
404   widget_class->button_release_event = gtk_calendar_button_release;
405   widget_class->motion_notify_event = gtk_calendar_motion_notify;
406   widget_class->enter_notify_event = gtk_calendar_enter_notify;
407   widget_class->leave_notify_event = gtk_calendar_leave_notify;
408   widget_class->key_press_event = gtk_calendar_key_press;
409   widget_class->scroll_event = gtk_calendar_scroll;
410   widget_class->style_set = gtk_calendar_style_set;
411   widget_class->state_changed = gtk_calendar_state_changed;
412   widget_class->grab_notify = gtk_calendar_grab_notify;
413   
414   class->month_changed = NULL;
415   class->day_selected = NULL;
416   class->day_selected_double_click = NULL;
417   class->prev_month = NULL;
418   class->next_month = NULL;
419   class->prev_year = NULL;
420   class->next_year = NULL;
421
422   g_object_class_install_property (gobject_class,
423                                    PROP_YEAR,
424                                    g_param_spec_int ("year",
425                                                      _("Year"),
426                                                      _("The selected year"),
427                                                      0, G_MAXINT, 0,
428                                                      G_PARAM_READWRITE));
429   g_object_class_install_property (gobject_class,
430                                    PROP_MONTH,
431                                    g_param_spec_int ("month",
432                                                      _("Month"),
433                                                      _("The selected month (as a number between 0 and 11)"),
434                                                      0, 11, 0,
435                                                      G_PARAM_READWRITE));
436   g_object_class_install_property (gobject_class,
437                                    PROP_DAY,
438                                    g_param_spec_int ("day",
439                                                      _("Day"),
440                                                      _("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
441                                                      0, 31, 0,
442                                                      G_PARAM_READWRITE));
443
444 /**
445  * GtkCalendar:show-heading:
446  *
447  * Determines whether a heading is displayed.
448  *
449  * Since: 2.4
450  */
451   g_object_class_install_property (gobject_class,
452                                    PROP_SHOW_HEADING,
453                                    g_param_spec_boolean ("show_heading",
454                                                          _("Show Heading"),
455                                                          _("If TRUE, a heading is displayed"),
456                                                          TRUE,
457                                                          G_PARAM_READWRITE));
458
459 /**
460  * GtkCalendar:show-day-names:
461  *
462  * Determines whether day names are displayed.
463  *
464  * Since: 2.4
465  */
466   g_object_class_install_property (gobject_class,
467                                    PROP_SHOW_DAY_NAMES,
468                                    g_param_spec_boolean ("show_day_names",
469                                                          _("Show Day Names"),
470                                                          _("If TRUE, day names are displayed"),
471                                                          TRUE,
472                                                          G_PARAM_READWRITE));
473 /**
474  * GtkCalendar:no-month-change:
475  *
476  * Determines whether the selected month can be changed.
477  *
478  * Since: 2.4
479  */
480   g_object_class_install_property (gobject_class,
481                                    PROP_NO_MONTH_CHANGE,
482                                    g_param_spec_boolean ("no_month_change",
483                                                          _("No Month Change"),
484                                                          _("If TRUE, the selected month can not be changed"),
485                                                          FALSE,
486                                                          G_PARAM_READWRITE));
487
488 /**
489  * GtkCalendar:show-week-numbers:
490  *
491  * Determines whether week numbers are displayed.
492  *
493  * Since: 2.4
494  */
495   g_object_class_install_property (gobject_class,
496                                    PROP_SHOW_WEEK_NUMBERS,
497                                    g_param_spec_boolean ("show_week_numbers",
498                                                          _("Show Week Numbers"),
499                                                          _("If TRUE, week numbers are displayed"),
500                                                          FALSE,
501                                                          G_PARAM_READWRITE));
502
503   gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
504     g_signal_new ("month_changed",
505                   G_OBJECT_CLASS_TYPE (gobject_class),
506                   G_SIGNAL_RUN_FIRST,
507                   G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
508                   NULL, NULL,
509                   _gtk_marshal_VOID__VOID,
510                   G_TYPE_NONE, 0);
511   gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
512     g_signal_new ("day_selected",
513                   G_OBJECT_CLASS_TYPE (gobject_class),
514                   G_SIGNAL_RUN_FIRST,
515                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
516                   NULL, NULL,
517                   _gtk_marshal_VOID__VOID,
518                   G_TYPE_NONE, 0);
519   gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
520     g_signal_new ("day_selected_double_click",
521                   G_OBJECT_CLASS_TYPE (gobject_class),
522                   G_SIGNAL_RUN_FIRST,
523                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
524                   NULL, NULL,
525                   _gtk_marshal_VOID__VOID,
526                   G_TYPE_NONE, 0);
527   gtk_calendar_signals[PREV_MONTH_SIGNAL] =
528     g_signal_new ("prev_month",
529                   G_OBJECT_CLASS_TYPE (gobject_class),
530                   G_SIGNAL_RUN_FIRST,
531                   G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
532                   NULL, NULL,
533                   _gtk_marshal_VOID__VOID,
534                   G_TYPE_NONE, 0);
535   gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
536     g_signal_new ("next_month",
537                   G_OBJECT_CLASS_TYPE (gobject_class),
538                   G_SIGNAL_RUN_FIRST,
539                   G_STRUCT_OFFSET (GtkCalendarClass, next_month),
540                   NULL, NULL,
541                   _gtk_marshal_VOID__VOID,
542                   G_TYPE_NONE, 0);
543   gtk_calendar_signals[PREV_YEAR_SIGNAL] =
544     g_signal_new ("prev_year",
545                   G_OBJECT_CLASS_TYPE (gobject_class),
546                   G_SIGNAL_RUN_FIRST,
547                   G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
548                   NULL, NULL,
549                   _gtk_marshal_VOID__VOID,
550                   G_TYPE_NONE, 0);
551   gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
552     g_signal_new ("next_year",
553                   G_OBJECT_CLASS_TYPE (gobject_class),
554                   G_SIGNAL_RUN_FIRST,
555                   G_STRUCT_OFFSET (GtkCalendarClass, next_year),
556                   NULL, NULL,
557                   _gtk_marshal_VOID__VOID,
558                   G_TYPE_NONE, 0);
559 }
560
561 static void
562 gtk_calendar_init (GtkCalendar *calendar)
563 {
564   time_t secs;
565   struct tm *tm;
566   gint i;
567   char buffer[255];
568   time_t tmp_time;
569   GtkWidget *widget;
570   GtkCalendarPrivateData *private_data;
571   gchar *year_before;
572   gchar *week_start;
573   
574   widget = GTK_WIDGET (calendar);
575   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
576   
577   calendar->private_data = g_malloc (sizeof (GtkCalendarPrivateData));
578   private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);
579
580   if (!default_abbreviated_dayname[0])
581     for (i=0; i<7; i++)
582       {
583         tmp_time= (i+3)*86400;
584         strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
585         default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
586       }
587   
588   if (!default_monthname[0])
589     for (i=0; i<12; i++)
590       {
591         tmp_time=i*2764800;
592         strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
593         default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
594       }
595   
596   /* Set defaults */
597   secs = time (NULL);
598   tm = localtime (&secs);
599   calendar->month = tm->tm_mon;
600   calendar->year  = 1900 + tm->tm_year;
601   
602   for (i=0;i<31;i++)
603     calendar->marked_date[i] = FALSE;
604   calendar->num_marked_dates = 0;
605   calendar->selected_day = tm->tm_mday;
606   
607   calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
608                               GTK_CALENDAR_SHOW_DAY_NAMES );
609   
610   calendar->highlight_row = -1;
611   calendar->highlight_col = -1;
612   
613   calendar->focus_row = -1;
614   calendar->focus_col = -1;
615   calendar->xor_gc = NULL;
616
617   private_data->max_year_width = 0;
618   private_data->max_month_width = 0;
619   private_data->max_day_char_width = 0;
620   private_data->max_week_char_width = 0;
621
622   private_data->max_day_char_ascent = 0;
623   private_data->max_day_char_descent = 0;
624   private_data->max_label_char_ascent = 0;
625   private_data->max_label_char_descent = 0;
626
627   private_data->arrow_width = 10;
628
629   private_data->freeze_count = 0;
630   
631   private_data->dirty_header = 0;
632   private_data->dirty_day_names = 0;
633   private_data->dirty_week = 0;
634   private_data->dirty_main = 0;
635   
636   private_data->need_timer = 0;
637   private_data->timer = 0;
638   private_data->click_child = -1;
639
640   private_data->year_before = 0;
641
642   /* Translate to calendar:YM if you want years to be displayed
643    * before months; otherwise translate to calendar:MY.
644    * Do *not* translate it to anything else, if it
645    * it isn't calendar:YM or calendar:MY it will not work.
646    *
647    * Note that this flipping is in top the text direction flipping,
648    * so if you have a default text direction of RTL and YM, then
649    * the year will appear on the right.
650    */
651   year_before = _("calendar:MY");
652   if (strcmp (year_before, "calendar:YM") == 0)
653     private_data->year_before = 1;
654   else if (strcmp (year_before, "calendar:MY") != 0)
655     g_warning ("Whoever translated calendar:MY did so wrongly.\n");
656
657   /* Translate to calendar:week_start:1 if you want Monday to be the
658    * first day of the week; otherwise translate to calendar:week_start:0. 
659    * Do *not* translate it to anything else, if it isn't calendar:week_start:1 
660    * or calendar:week_start:0 it will not work.
661    */
662    week_start = _("calendar:week_start:0");
663    if (strcmp (week_start, "calendar:week_start:1") == 0)
664      private_data->week_start = 1;
665    else if (strcmp (week_start, "calendar:week_start:0") == 0) 
666      private_data->week_start = 0;
667    else
668      g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
669 }
670
671 GtkWidget*
672 gtk_calendar_new (void)
673 {
674   return g_object_new (GTK_TYPE_CALENDAR, NULL);
675 }
676
677 /* column_from_x: returns the column 0-6 that the
678  * x pixel of the xwindow is in */
679 static gint
680 column_from_x (GtkCalendar *calendar,
681                gint         event_x)
682 {
683   gint c, column;
684   gint x_left, x_right;
685   
686   column = -1;
687   
688   for (c = 0; c < 7; c++)
689     {
690       x_left = left_x_for_column (calendar, c);
691       x_right = x_left + GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
692       
693       if (event_x >= x_left && event_x < x_right)
694         {
695           column = c;
696           break;
697         }
698     }
699   
700   return column;
701 }
702
703 static gint
704 row_height (GtkCalendar *calendar)
705 {
706   return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h - CALENDAR_MARGIN
707           - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
708              ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
709 }
710
711
712 /* row_from_y: returns the row 0-5 that the
713  * y pixel of the xwindow is in */
714 static gint
715 row_from_y (GtkCalendar *calendar,
716             gint         event_y)
717 {
718   gint r, row;
719   gint height;
720   gint y_top, y_bottom;
721   
722   height = row_height (calendar);
723   row = -1;
724   
725   for (r = 0; r < 6; r++)
726     {
727       y_top = top_y_for_row (calendar, r);
728       y_bottom = y_top + height;
729       
730       if (event_y >= y_top && event_y < y_bottom)
731         {
732           row = r;
733           break;
734         }
735     }
736   
737   return row;
738 }/* left_x_for_column: returns the x coordinate
739  * for the left of the column */
740 static gint
741 left_x_for_column (GtkCalendar *calendar,
742                    gint         column)
743 {
744   gint width;
745   gint x_left;
746   
747   if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
748     column = 6 - column;
749
750   width = GTK_CALENDAR_PRIVATE_DATA (calendar)->day_width;
751   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
752     x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
753   else
754     x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
755   
756   return x_left;
757 }
758
759 /* top_y_for_row: returns the y coordinate
760  * for the top of the row */
761 static gint
762 top_y_for_row (GtkCalendar *calendar,
763                gint         row)
764 {
765   
766   return (GTK_CALENDAR_PRIVATE_DATA (calendar)->main_h 
767           - (CALENDAR_MARGIN + (6 - row)
768              * row_height (calendar)));
769 }
770
771 /* This function should be done by the toolkit, but we don't like the
772  * GTK arrows because they don't look good on this widget */
773 static void
774 draw_arrow_right (GdkWindow *window,
775                   GdkGC     *gc,
776                   gint       x,
777                   gint       y,
778                   gint       size)
779 {
780   gint i;
781   
782   for (i = 0; i <= size / 2; i++)
783     {
784       gdk_draw_line (window, gc,
785                      x + i,
786                      y + i,
787                      x + i,
788                      y + size - i);
789     }
790 }
791
792 /* This function should be done by the toolkit, but we don't like the
793  * GTK arrows because they don't look good on this widget */
794 static void
795 draw_arrow_left (GdkWindow *window,
796                  GdkGC     *gc,
797                  gint       x,
798                  gint       y,
799                  gint       size)
800 {
801   gint i;
802   
803   for (i = 0; i <= size / 2; i++)
804     {
805       gdk_draw_line (window, gc,
806                      x + size/2 - i,
807                      y + i,
808                      x + size/2 - i,
809                      y + size - i);
810     }
811 }
812
813 static void
814 gtk_calendar_set_month_prev (GtkCalendar *calendar)
815 {
816   gint month_len;
817   
818   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
819     return;
820   
821   if (calendar->month == 0)
822     {
823       calendar->month = 11;
824       calendar->year--;
825     } 
826   else 
827     calendar->month--;
828   
829   month_len = month_length[leap (calendar->year)][calendar->month + 1];
830   
831   gtk_calendar_freeze (calendar);
832   gtk_calendar_compute_days (calendar);
833   
834   g_signal_emit (calendar,
835                  gtk_calendar_signals[PREV_MONTH_SIGNAL],
836                  0);
837   g_signal_emit (calendar,
838                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
839                  0);
840   
841   if (month_len < calendar->selected_day)
842     {
843       calendar->selected_day = 0;
844       gtk_calendar_select_day (calendar, month_len);
845     }
846   else
847     {
848       if (calendar->selected_day < 0)
849         calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
850       gtk_calendar_select_day (calendar, calendar->selected_day);
851     }
852
853   gtk_widget_queue_draw (GTK_WIDGET (calendar));
854   gtk_calendar_thaw (calendar);
855 }
856
857
858 static void
859 gtk_calendar_set_month_next (GtkCalendar *calendar)
860 {
861   gint month_len;
862   
863   g_return_if_fail (GTK_IS_WIDGET (calendar));
864   
865   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
866     return;
867   
868   
869   if (calendar->month == 11)
870     {
871       calendar->month = 0;
872       calendar->year++;
873     } 
874   else 
875     calendar->month++;
876   
877   gtk_calendar_freeze (calendar);
878   gtk_calendar_compute_days (calendar);
879   g_signal_emit (calendar,
880                  gtk_calendar_signals[NEXT_MONTH_SIGNAL],
881                  0);
882   g_signal_emit (calendar,
883                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
884                  0);
885   
886   month_len = month_length[leap (calendar->year)][calendar->month + 1];
887   
888   if (month_len < calendar->selected_day)
889     {
890       calendar->selected_day = 0;
891       gtk_calendar_select_day (calendar, month_len);
892     }
893   else
894     gtk_calendar_select_day (calendar, calendar->selected_day);
895
896   gtk_widget_queue_draw (GTK_WIDGET (calendar));
897   gtk_calendar_thaw (calendar);
898 }
899
900 static void
901 gtk_calendar_set_year_prev (GtkCalendar *calendar)
902 {
903   gint month_len;
904   
905   g_return_if_fail (GTK_IS_WIDGET (calendar));
906   
907   calendar->year--;
908   gtk_calendar_freeze (calendar);
909   gtk_calendar_compute_days (calendar);
910   g_signal_emit (calendar,
911                  gtk_calendar_signals[PREV_YEAR_SIGNAL],
912                  0);
913   g_signal_emit (calendar,
914                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
915                  0);
916   
917   month_len = month_length[leap (calendar->year)][calendar->month + 1];
918   
919   if (month_len < calendar->selected_day)
920     {
921       calendar->selected_day = 0;
922       gtk_calendar_select_day (calendar, month_len);
923     }
924   else
925     gtk_calendar_select_day (calendar, calendar->selected_day);
926   
927   gtk_widget_queue_draw (GTK_WIDGET (calendar));
928   gtk_calendar_thaw (calendar);
929 }
930
931 static void
932 gtk_calendar_set_year_next (GtkCalendar *calendar)
933 {
934   gint month_len;
935   GtkWidget *widget;
936   
937   g_return_if_fail (GTK_IS_WIDGET (calendar));
938   
939   widget = GTK_WIDGET (calendar);
940   
941   gtk_calendar_freeze (calendar);
942   
943   calendar->year++;
944   gtk_calendar_compute_days (calendar);
945   g_signal_emit (calendar,
946                  gtk_calendar_signals[NEXT_YEAR_SIGNAL],
947                  0);
948   g_signal_emit (calendar,
949                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
950                  0);
951   
952   month_len = month_length[leap (calendar->year)][calendar->month + 1];
953   
954   if (month_len < calendar->selected_day)
955     {
956       calendar->selected_day = 0;
957       gtk_calendar_select_day (calendar, month_len);
958     }
959   else
960     gtk_calendar_select_day (calendar, calendar->selected_day);
961   
962   gtk_widget_queue_draw (GTK_WIDGET (calendar));
963   gtk_calendar_thaw (calendar);
964 }
965
966 static void
967 gtk_calendar_main_button (GtkWidget      *widget,
968                           GdkEventButton *event)
969 {
970   GtkCalendar *calendar;
971   gint x, y;
972   gint row, col;
973   gint day_month;
974   gint day;
975   
976   calendar = GTK_CALENDAR (widget);
977   
978   x = (gint) (event->x);
979   y = (gint) (event->y);
980   
981   row = row_from_y (calendar, y);
982   col = column_from_x (calendar, x);
983
984   /* If row or column isn't found, just return. */
985   if (row == -1 || col == -1)
986     return;
987   
988   day_month = calendar->day_month[row][col];
989
990   if (event->type == GDK_BUTTON_PRESS)
991     {
992       day = calendar->day[row][col];
993       
994       if (day_month == MONTH_PREV)
995         gtk_calendar_set_month_prev (calendar);
996       else if (day_month == MONTH_NEXT)
997         gtk_calendar_set_month_next (calendar);
998       
999       if (!GTK_WIDGET_HAS_FOCUS (widget))
1000         gtk_widget_grab_focus (widget);
1001           
1002       gtk_calendar_select_and_focus_day (calendar, day);
1003
1004     }
1005   else if (event->type == GDK_2BUTTON_PRESS)
1006     {
1007       if (day_month == MONTH_CURRENT)
1008         g_signal_emit (calendar,
1009                        gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
1010                        0);
1011     }
1012 }
1013
1014 static void
1015 gtk_calendar_realize_arrows (GtkWidget *widget)
1016 {
1017   GtkCalendar *calendar;
1018   GtkCalendarPrivateData *private_data;
1019   GdkWindowAttr attributes;
1020   gint attributes_mask;
1021   gint i;
1022   gboolean year_left;
1023   
1024   g_return_if_fail (GTK_IS_CALENDAR (widget));
1025   
1026   calendar = GTK_CALENDAR (widget);
1027   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1028
1029   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1030     year_left = private_data->year_before;
1031   else
1032     year_left = !private_data->year_before;
1033     
1034   /* Arrow windows ------------------------------------- */
1035   if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1036       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1037     {
1038       attributes.wclass = GDK_INPUT_OUTPUT;
1039       attributes.window_type = GDK_WINDOW_CHILD;
1040       attributes.visual = gtk_widget_get_visual (widget);
1041       attributes.colormap = gtk_widget_get_colormap (widget);
1042       attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1043                                | GDK_BUTTON_PRESS_MASK  | GDK_BUTTON_RELEASE_MASK
1044                                | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1045       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1046       attributes.y = 3;
1047       attributes.width = private_data->arrow_width;
1048       attributes.height = private_data->header_h - 7;
1049       for (i = 0; i < 4; i++)
1050         {
1051           switch (i)
1052             {
1053             case ARROW_MONTH_LEFT:
1054               if (year_left) 
1055                 attributes.x = (widget->allocation.width - 2 * widget->style->xthickness
1056                                 - (3 + 2*private_data->arrow_width 
1057                                    + private_data->max_month_width));
1058               else
1059               attributes.x = 3;
1060               break;
1061             case ARROW_MONTH_RIGHT:
1062               if (year_left) 
1063                 attributes.x = (widget->allocation.width - 2 * widget->style->xthickness 
1064                                 - 3 - private_data->arrow_width);
1065               else
1066               attributes.x = (private_data->arrow_width 
1067                               + private_data->max_month_width);
1068               break;
1069             case ARROW_YEAR_LEFT:
1070               if (year_left) 
1071                 attributes.x = 3;
1072               else
1073               attributes.x = (widget->allocation.width - 2 * widget->style->xthickness
1074                               - (3 + 2*private_data->arrow_width 
1075                                  + private_data->max_year_width));
1076               break;
1077             case ARROW_YEAR_RIGHT:
1078               if (year_left) 
1079                 attributes.x = (private_data->arrow_width 
1080                                 + private_data->max_year_width);
1081               else
1082               attributes.x = (widget->allocation.width - 2 * widget->style->xthickness 
1083                               - 3 - private_data->arrow_width);
1084               break;
1085             }
1086           private_data->arrow_win[i] = gdk_window_new (private_data->header_win,
1087                                                        &attributes, 
1088                                                        attributes_mask);
1089           private_data->arrow_state[i] = GTK_STATE_NORMAL;
1090           gdk_window_set_background (private_data->arrow_win[i],
1091                                      HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1092           gdk_window_show (private_data->arrow_win[i]);
1093           gdk_window_set_user_data (private_data->arrow_win[i], widget);
1094         }
1095     }
1096   else
1097     {
1098       for (i = 0; i < 4; i++)
1099         private_data->arrow_win[i] = NULL;
1100     }
1101 }
1102
1103 static void
1104 gtk_calendar_realize_header (GtkWidget *widget)
1105 {
1106   GtkCalendar *calendar;
1107   GtkCalendarPrivateData *private_data;
1108   GdkWindowAttr attributes;
1109   gint attributes_mask;
1110   
1111   g_return_if_fail (GTK_IS_CALENDAR (widget));
1112   
1113   calendar = GTK_CALENDAR (widget);
1114   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1115
1116   /* Header window ------------------------------------- */
1117   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1118     {
1119       attributes.wclass = GDK_INPUT_OUTPUT;
1120       attributes.window_type = GDK_WINDOW_CHILD;
1121       attributes.visual = gtk_widget_get_visual (widget);
1122       attributes.colormap = gtk_widget_get_colormap (widget);
1123       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1124       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1125       attributes.x = widget->style->xthickness;
1126       attributes.y = widget->style->ythickness;
1127       attributes.width = widget->allocation.width - 2 * attributes.x;
1128       attributes.height = private_data->header_h - 2 * attributes.y;
1129       private_data->header_win = gdk_window_new (widget->window,
1130                                              &attributes, attributes_mask);
1131       
1132       gdk_window_set_background (private_data->header_win,
1133                                  HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1134       gdk_window_show (private_data->header_win);
1135       gdk_window_set_user_data (private_data->header_win, widget);
1136       
1137     }
1138   else
1139     {
1140       private_data->header_win = NULL;
1141     }
1142   gtk_calendar_realize_arrows (widget);
1143 }
1144
1145 static void
1146 gtk_calendar_realize_day_names (GtkWidget *widget)
1147 {
1148   GtkCalendar *calendar;
1149   GtkCalendarPrivateData *private_data;
1150   GdkWindowAttr attributes;
1151   gint attributes_mask;
1152   
1153   g_return_if_fail (GTK_IS_CALENDAR (widget));
1154   
1155   calendar = GTK_CALENDAR (widget);
1156   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1157
1158   /* Day names  window --------------------------------- */
1159   if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1160     {
1161       attributes.wclass = GDK_INPUT_OUTPUT;
1162       attributes.window_type = GDK_WINDOW_CHILD;
1163       attributes.visual = gtk_widget_get_visual (widget);
1164       attributes.colormap = gtk_widget_get_colormap (widget);
1165       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1166       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1167       attributes.x = (widget->style->xthickness + INNER_BORDER);
1168       attributes.y = private_data->header_h + (widget->style->ythickness 
1169                                            + INNER_BORDER);
1170       attributes.width = (widget->allocation.width 
1171                           - (widget->style->xthickness + INNER_BORDER) 
1172                           * 2);
1173       attributes.height = private_data->day_name_h;
1174       private_data->day_name_win = gdk_window_new (widget->window,
1175                                                    &attributes, 
1176                                                    attributes_mask);
1177       gdk_window_set_background (private_data->day_name_win, 
1178                                  BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1179       gdk_window_show (private_data->day_name_win);
1180       gdk_window_set_user_data (private_data->day_name_win, widget);
1181     }
1182   else
1183     {
1184       private_data->day_name_win = NULL;
1185     }
1186 }
1187
1188 static void
1189 gtk_calendar_realize_week_numbers (GtkWidget *widget)
1190 {
1191   GtkCalendar *calendar;
1192   GtkCalendarPrivateData *private_data;
1193   GdkWindowAttr attributes;
1194   gint attributes_mask;
1195   
1196   g_return_if_fail (GTK_IS_CALENDAR (widget));
1197   
1198   calendar = GTK_CALENDAR (widget);
1199   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1200
1201   /* Week number window -------------------------------- */
1202   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1203     {
1204       attributes.wclass = GDK_INPUT_OUTPUT;
1205       attributes.window_type = GDK_WINDOW_CHILD;
1206       attributes.visual = gtk_widget_get_visual (widget);
1207       attributes.colormap = gtk_widget_get_colormap (widget);
1208       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1209       
1210       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1211       attributes.x = widget->style->xthickness + INNER_BORDER;
1212       attributes.y = (private_data->header_h + private_data->day_name_h 
1213                       + (widget->style->ythickness + INNER_BORDER));
1214       attributes.width = private_data->week_width;
1215       attributes.height = private_data->main_h;
1216       private_data->week_win = gdk_window_new (widget->window,
1217                                                &attributes, attributes_mask);
1218       gdk_window_set_background (private_data->week_win,  
1219                                  BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1220       gdk_window_show (private_data->week_win);
1221       gdk_window_set_user_data (private_data->week_win, widget);
1222     } 
1223   else
1224     {
1225       private_data->week_win = NULL;
1226     }
1227 }
1228
1229 static void
1230 gtk_calendar_realize (GtkWidget *widget)
1231 {
1232   GtkCalendar *calendar;
1233   GtkCalendarPrivateData *private_data;
1234   GdkWindowAttr attributes;
1235   gint attributes_mask;
1236   GdkGCValues values;
1237
1238   calendar = GTK_CALENDAR (widget);
1239   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1240   
1241   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1242   gtk_calendar_compute_days (calendar);
1243   
1244   attributes.x = widget->allocation.x;
1245   attributes.y = widget->allocation.y;
1246   attributes.width = widget->allocation.width;
1247   attributes.height = widget->allocation.height;
1248   attributes.wclass = GDK_INPUT_OUTPUT;
1249   attributes.window_type = GDK_WINDOW_CHILD;
1250   attributes.event_mask =  (gtk_widget_get_events (widget) 
1251                             | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1252   attributes.visual = gtk_widget_get_visual (widget);
1253   attributes.colormap = gtk_widget_get_colormap (widget);
1254   
1255   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1256   widget->window = gdk_window_new (widget->parent->window,
1257                                    &attributes, attributes_mask);
1258   
1259   widget->style = gtk_style_attach (widget->style, widget->window);
1260   
1261   /* Header window ------------------------------------- */
1262   gtk_calendar_realize_header (widget);
1263   /* Day names  window --------------------------------- */
1264   gtk_calendar_realize_day_names (widget);
1265   /* Week number window -------------------------------- */
1266   gtk_calendar_realize_week_numbers (widget);
1267   /* Main Window --------------------------------------  */
1268   attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1269                             | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1270                             | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1271   
1272   attributes.x = private_data->week_width + (widget->style->ythickness + INNER_BORDER);
1273   attributes.y = (private_data->header_h + private_data->day_name_h 
1274                   + (widget->style->ythickness + INNER_BORDER));
1275   attributes.width = (widget->allocation.width - attributes.x 
1276                       - (widget->style->xthickness + INNER_BORDER));
1277   attributes.height = private_data->main_h;
1278   private_data->main_win = gdk_window_new (widget->window,
1279                                            &attributes, attributes_mask);
1280   gdk_window_set_background (private_data->main_win, 
1281                              BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1282   gdk_window_show (private_data->main_win);
1283   gdk_window_set_user_data (private_data->main_win, widget);
1284   gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1285   gdk_window_show (widget->window);
1286   gdk_window_set_user_data (widget->window, widget);
1287   
1288   /* Set widgets gc */
1289   calendar->gc = gdk_gc_new (widget->window);
1290
1291   values.foreground = widget->style->white;
1292   values.function = GDK_XOR;
1293   calendar->xor_gc = gdk_gc_new_with_values (widget->window,
1294                                              &values,
1295                                              GDK_GC_FOREGROUND |
1296                                              GDK_GC_FUNCTION);
1297 }
1298
1299 static void
1300 gtk_calendar_unrealize (GtkWidget *widget)
1301 {
1302   GtkCalendar *calendar;
1303   GtkCalendarPrivateData *private_data;
1304   gint i;
1305   
1306   calendar = GTK_CALENDAR (widget);
1307   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1308   
1309   if (private_data->header_win)
1310     {
1311       for (i = 0; i < 4; i++)
1312         {
1313           if (private_data->arrow_win[i])
1314             {
1315               gdk_window_set_user_data (private_data->arrow_win[i], NULL);
1316               gdk_window_destroy (private_data->arrow_win[i]);
1317               private_data->arrow_win[i] = NULL;
1318             }
1319         }
1320       gdk_window_set_user_data (private_data->header_win, NULL);
1321       gdk_window_destroy (private_data->header_win);
1322       private_data->header_win = NULL;
1323     }
1324   
1325   if (private_data->week_win)
1326     {
1327       gdk_window_set_user_data (private_data->week_win, NULL);
1328       gdk_window_destroy (private_data->week_win);
1329       private_data->week_win = NULL;      
1330     }
1331   
1332   if (private_data->main_win)
1333     {
1334       gdk_window_set_user_data (private_data->main_win, NULL);
1335       gdk_window_destroy (private_data->main_win);
1336       private_data->main_win = NULL;      
1337     }
1338   if (private_data->day_name_win)
1339     {
1340       gdk_window_set_user_data (private_data->day_name_win, NULL);
1341       gdk_window_destroy (private_data->day_name_win);
1342       private_data->day_name_win = NULL;      
1343     }
1344   if (calendar->xor_gc)
1345     g_object_unref (calendar->xor_gc);
1346   if (calendar->gc)
1347     g_object_unref (calendar->gc);
1348   
1349   if (GTK_WIDGET_CLASS (parent_class)->unrealize)
1350     (* GTK_WIDGET_CLASS (parent_class)->unrealize) (widget);
1351 }
1352
1353 static void
1354 gtk_calendar_size_request (GtkWidget      *widget,
1355                            GtkRequisition *requisition)
1356 {
1357   GtkCalendar *calendar;
1358   GtkCalendarPrivateData *private_data;
1359   PangoLayout *layout;
1360   PangoRectangle logical_rect;
1361
1362   gint height;
1363   gint i;
1364   gchar buffer[255];
1365   gint calendar_margin = CALENDAR_MARGIN;
1366   gint header_width, main_width;
1367   gint max_header_height = 0;
1368   gint focus_width;
1369   gint focus_padding;
1370   
1371   calendar = GTK_CALENDAR (widget);
1372   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1373   gtk_widget_style_get (GTK_WIDGET (widget),
1374                         "focus-line-width", &focus_width,
1375                         "focus-padding", &focus_padding,
1376                         NULL);
1377
1378   layout = gtk_widget_create_pango_layout (widget, NULL);
1379   
1380   /*
1381    * Calculate the requisition  width for the widget.
1382    */
1383   
1384   /* Header width */
1385   
1386   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1387     {
1388       private_data->max_month_width = 0;
1389       for (i = 0; i < 12; i++)
1390         {
1391           pango_layout_set_text (layout, default_monthname[i], -1);
1392           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1393           private_data->max_month_width = MAX (private_data->max_month_width,
1394                                                logical_rect.width + 8);
1395           max_header_height = MAX (max_header_height, logical_rect.height); 
1396         }
1397       private_data->max_year_width = 0;
1398       for (i=0; i<10; i++)
1399         {
1400           g_snprintf (buffer, sizeof (buffer), "%d%d%d%d", i,i,i,i);
1401           pango_layout_set_text (layout, buffer, -1);     
1402           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1403           private_data->max_year_width = MAX (private_data->max_year_width,
1404                                               logical_rect.width + 8);
1405           max_header_height = MAX (max_header_height, logical_rect.height); 
1406         }
1407     } 
1408   else 
1409     {
1410       private_data->max_month_width = 0;
1411       private_data->max_year_width = 0;
1412     }
1413   
1414   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1415     header_width = (private_data->max_month_width 
1416                     + private_data->max_year_width
1417                     + 3 * 3);
1418   else
1419     header_width = (private_data->max_month_width 
1420                     + private_data->max_year_width
1421                     + 4 * private_data->arrow_width + 3 * 3);
1422
1423   /* Mainwindow labels width */
1424   
1425   private_data->max_day_char_width = 0;
1426   private_data->min_day_width = 0;
1427   private_data->max_label_char_ascent = 0;
1428   private_data->max_label_char_descent = 0;
1429
1430   for (i = 0; i < 9; i++)
1431     {
1432       g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
1433       pango_layout_set_text (layout, buffer, -1);         
1434       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1435       private_data->min_day_width = MAX (private_data->min_day_width,
1436                                          logical_rect.width);
1437
1438       private_data->max_day_char_ascent = MAX (private_data->max_label_char_ascent,
1439                                                PANGO_ASCENT (logical_rect));
1440       private_data->max_day_char_descent = MAX (private_data->max_label_char_descent, 
1441                                                 PANGO_DESCENT (logical_rect));
1442     }
1443   /* We add one to max_day_char_width to be able to make the marked day "bold" */
1444   private_data->max_day_char_width = private_data->min_day_width / 2 + 1;
1445   
1446   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1447     for (i = 0; i < 7; i++)
1448       {
1449         pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1450         pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
1451
1452         private_data->min_day_width = MAX (private_data->min_day_width, logical_rect.width);
1453         private_data->max_label_char_ascent = MAX (private_data->max_label_char_ascent,
1454                                                    PANGO_ASCENT (logical_rect));
1455         private_data->max_label_char_descent = MAX (private_data->max_label_char_descent, 
1456                                                     PANGO_DESCENT (logical_rect));
1457       }
1458   
1459   private_data->max_week_char_width = 0;
1460   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1461     for (i = 0; i < 9; i++)
1462       {
1463         g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
1464         pango_layout_set_text (layout, buffer, -1);       
1465         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1466         private_data->max_week_char_width = MAX (private_data->max_week_char_width,
1467                                                  logical_rect.width / 2);
1468       }
1469   
1470   main_width = (7 * (private_data->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1471                 + (private_data->max_week_char_width
1472                    ? private_data->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1473                    : 0));
1474   
1475   
1476   requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1477   
1478   /*
1479    * Calculate the requisition height for the widget.
1480    */
1481   
1482   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1483     {
1484       private_data->header_h = (max_header_height + CALENDAR_YSEP * 2);
1485     }
1486   else
1487     {
1488       private_data->header_h = 0;
1489     }
1490   
1491   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1492     {
1493       private_data->day_name_h = (private_data->max_label_char_ascent
1494                                   + private_data->max_label_char_descent
1495                                   + 2 * (focus_padding + focus_width) + calendar_margin);
1496       calendar_margin = CALENDAR_YSEP;
1497     } 
1498   else
1499     {
1500       private_data->day_name_h = 0;
1501     }
1502
1503   private_data->main_h = (CALENDAR_MARGIN + calendar_margin
1504                           + 6 * (private_data->max_day_char_ascent
1505                                  + private_data->max_day_char_descent 
1506                                  + 2 * (focus_padding + focus_width))
1507                           + DAY_YSEP * 5);
1508   
1509   height = (private_data->header_h + private_data->day_name_h 
1510             + private_data->main_h);
1511   
1512   requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1513
1514   g_object_unref (layout);
1515 }
1516
1517 static void
1518 gtk_calendar_size_allocate (GtkWidget     *widget,
1519                             GtkAllocation *allocation)
1520 {
1521   GtkCalendar *calendar;
1522   GtkCalendarPrivateData *private_data;
1523   gint xthickness = widget->style->xthickness;
1524   gint ythickness = widget->style->xthickness;
1525   gboolean year_left;
1526   
1527   widget->allocation = *allocation;
1528   
1529   calendar = GTK_CALENDAR (widget);
1530   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1531
1532   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1533     year_left = private_data->year_before;
1534   else
1535     year_left = !private_data->year_before;
1536   
1537   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1538     {
1539       private_data->day_width = (private_data->min_day_width
1540                              * ((allocation->width - (xthickness + INNER_BORDER) * 2
1541                                  - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1542                              / (7 * private_data->min_day_width + private_data->max_week_char_width * 2));
1543       private_data->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
1544                                - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1545                               - private_data->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1546     } 
1547   else 
1548     {
1549       private_data->day_width = (allocation->width
1550                              - (xthickness + INNER_BORDER) * 2
1551                              - (CALENDAR_MARGIN * 2)
1552                              - (DAY_XSEP * 6))/7;
1553       private_data->week_width = 0;
1554     }
1555   
1556   if (GTK_WIDGET_REALIZED (widget))
1557     {
1558       gdk_window_move_resize (widget->window,
1559                               allocation->x, allocation->y,
1560                               allocation->width, allocation->height);
1561       if (private_data->header_win)
1562         gdk_window_move_resize (private_data->header_win,
1563                                 xthickness, ythickness,
1564                                 allocation->width - 2 * xthickness, private_data->header_h);
1565       if (private_data->arrow_win[ARROW_MONTH_LEFT])
1566         {
1567           if (year_left)
1568             gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_LEFT],
1569                                     (allocation->width - 2 * xthickness
1570                                      - (3 + 2*private_data->arrow_width 
1571                                         + private_data->max_month_width)),
1572                                     3,
1573                                     private_data->arrow_width,
1574                                     private_data->header_h - 7);
1575           else
1576             gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_LEFT],
1577                                     3, 3,
1578                                     private_data->arrow_width,
1579                                     private_data->header_h - 7);
1580         }
1581       if (private_data->arrow_win[ARROW_MONTH_RIGHT])
1582         {
1583           if (year_left)
1584             gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_RIGHT],
1585                                     (allocation->width - 2 * xthickness 
1586                                      - 3 - private_data->arrow_width), 
1587                                     3,
1588                                     private_data->arrow_width,
1589                                     private_data->header_h - 7);
1590           else
1591             gdk_window_move_resize (private_data->arrow_win[ARROW_MONTH_RIGHT],
1592                                     (private_data->arrow_width 
1593                                      + private_data->max_month_width), 
1594                                     3,
1595                                     private_data->arrow_width,
1596                                     private_data->header_h - 7);
1597         }
1598       if (private_data->arrow_win[ARROW_YEAR_LEFT])
1599         {
1600           if (year_left)
1601             gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_LEFT],
1602                                     3, 3,
1603                                     private_data->arrow_width,
1604                                     private_data->header_h - 7);
1605           else
1606             gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_LEFT],
1607                                     (allocation->width - 2 * xthickness
1608                                      - (3 + 2*private_data->arrow_width 
1609                                         + private_data->max_year_width)),
1610                                     3,
1611                                     private_data->arrow_width,
1612                                     private_data->header_h - 7);
1613         }
1614       if (private_data->arrow_win[ARROW_YEAR_RIGHT])
1615         {
1616           if (year_left)
1617             gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_RIGHT],
1618                                     (private_data->arrow_width 
1619                                      + private_data->max_year_width), 
1620                                     3,
1621                                     private_data->arrow_width,
1622                                     private_data->header_h - 7);
1623           else
1624             gdk_window_move_resize (private_data->arrow_win[ARROW_YEAR_RIGHT],
1625                                     (allocation->width - 2 * xthickness 
1626                                      - 3 - private_data->arrow_width), 
1627                                     3,
1628                                     private_data->arrow_width,
1629                                     private_data->header_h - 7);
1630         }
1631       if (private_data->day_name_win)
1632         gdk_window_move_resize (private_data->day_name_win,
1633                                 xthickness + INNER_BORDER,
1634                                 private_data->header_h + (widget->style->ythickness + INNER_BORDER),
1635                                 allocation->width - (xthickness + INNER_BORDER) * 2,
1636                                 private_data->day_name_h);
1637       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1638         {
1639           if (private_data->week_win)
1640             gdk_window_move_resize (private_data->week_win,
1641                                     (xthickness + INNER_BORDER),
1642                                     private_data->header_h + private_data->day_name_h
1643                                     + (widget->style->ythickness + INNER_BORDER),
1644                                     private_data->week_width,
1645                                     private_data->main_h);
1646           gdk_window_move_resize (private_data->main_win,
1647                                   private_data->week_width + (xthickness + INNER_BORDER),
1648                                   private_data->header_h + private_data->day_name_h
1649                                   + (widget->style->ythickness + INNER_BORDER),
1650                                   allocation->width 
1651                                   - private_data->week_width 
1652                                   - (xthickness + INNER_BORDER) * 2,
1653                                   private_data->main_h);
1654         }
1655       else 
1656         {
1657           gdk_window_move_resize (private_data->main_win,
1658                                   (xthickness + INNER_BORDER),
1659                                   private_data->header_h + private_data->day_name_h
1660                                   + (widget->style->ythickness + INNER_BORDER),
1661                                   allocation->width 
1662                                   - private_data->week_width 
1663                                   - (xthickness + INNER_BORDER) * 2,
1664                                   private_data->main_h);
1665           if (private_data->week_win)
1666             gdk_window_move_resize (private_data->week_win,
1667                                     allocation->width 
1668                                     - private_data->week_width 
1669                                     - (xthickness + INNER_BORDER),
1670                                     private_data->header_h + private_data->day_name_h
1671                                     + (widget->style->ythickness + INNER_BORDER),
1672                                     private_data->week_width,
1673                                     private_data->main_h);
1674         }
1675     }
1676 }
1677
1678 static gboolean
1679 gtk_calendar_expose (GtkWidget      *widget,
1680                      GdkEventExpose *event)
1681 {
1682   GtkCalendar *calendar;
1683   GtkCalendarPrivateData *private_data;
1684
1685   calendar = GTK_CALENDAR (widget);
1686   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1687   
1688   if (GTK_WIDGET_DRAWABLE (widget))
1689     {
1690       if (event->window == private_data->main_win)
1691         gtk_calendar_paint_main (widget);
1692       
1693       if (event->window == private_data->header_win)
1694         gtk_calendar_paint_header (widget);
1695       
1696       if (event->window == private_data->day_name_win)
1697         gtk_calendar_paint_day_names (widget);
1698       
1699       if (event->window == private_data->week_win)
1700         gtk_calendar_paint_week_numbers (widget);
1701       if (event->window == widget->window)
1702         {
1703           gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
1704                             GTK_SHADOW_IN, NULL, widget, "calendar",
1705                             0, 0, widget->allocation.width, widget->allocation.height);
1706         }
1707     }
1708   
1709   return FALSE;
1710 }
1711
1712 static void
1713 gtk_calendar_paint_header (GtkWidget *widget)
1714 {
1715   GtkCalendar *calendar;
1716   GdkGC *gc;
1717   char buffer[255];
1718   int x, y;
1719   gint header_width, cal_height;
1720   gint max_month_width;
1721   gint max_year_width;
1722   GtkCalendarPrivateData *private_data;
1723   PangoLayout *layout;
1724   PangoRectangle logical_rect;
1725   gboolean year_left;
1726
1727   calendar = GTK_CALENDAR (widget);
1728   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1729
1730   if (private_data->freeze_count)
1731     {
1732       private_data->dirty_header = 1;
1733       return;
1734     }
1735
1736   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1737     year_left = private_data->year_before;
1738   else
1739     year_left = !private_data->year_before;
1740
1741   private_data->dirty_header = 0;
1742   gc = calendar->gc;
1743   
1744   /* Clear window */
1745   gdk_window_clear (private_data->header_win);
1746   
1747   header_width = widget->allocation.width - 2 * widget->style->xthickness;
1748   cal_height = widget->allocation.height;
1749   
1750   max_month_width = private_data->max_month_width;
1751   max_year_width = private_data->max_year_width;
1752   
1753   gdk_gc_set_foreground (gc, BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1754   gtk_paint_shadow (widget->style, private_data->header_win,
1755                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
1756                     NULL, widget, "calendar",
1757                     0, 0, header_width, private_data->header_h);
1758   
1759   
1760   g_snprintf (buffer, sizeof (buffer), "%d", calendar->year);
1761   layout = gtk_widget_create_pango_layout (widget, buffer);
1762   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1763   
1764   /* Draw title */
1765   y = (private_data->header_h - logical_rect.height) / 2;
1766   
1767   /* Draw year and its arrows */
1768   
1769   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1770     if (year_left)
1771       x = 3 + (max_year_width - logical_rect.width)/2;
1772     else
1773       x = header_width - (3 + max_year_width
1774                           - (max_year_width - logical_rect.width)/2);
1775   else
1776     if (year_left)
1777       x = 3 + private_data->arrow_width + (max_year_width - logical_rect.width)/2;
1778     else
1779       x = header_width - (3 + private_data->arrow_width + max_year_width
1780                           - (max_year_width - logical_rect.width)/2);
1781   
1782   
1783   gdk_gc_set_foreground (gc, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
1784   gdk_draw_layout (private_data->header_win, gc, x, y, layout);
1785   
1786   /* Draw month */
1787   g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
1788   pango_layout_set_text (layout, buffer, -1);
1789   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1790
1791   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1792     if (year_left)
1793       x = header_width - (3 + max_month_width
1794                           - (max_month_width - logical_rect.width)/2);      
1795     else
1796     x = 3 + (max_month_width - logical_rect.width) / 2;
1797   else
1798     if (year_left)
1799       x = header_width - (3 + private_data->arrow_width + max_month_width
1800                           - (max_month_width - logical_rect.width)/2);
1801     else
1802     x = 3 + private_data->arrow_width + (max_month_width - logical_rect.width)/2;
1803
1804   gdk_draw_layout (private_data->header_win, gc, x, y, layout);
1805   
1806   gdk_gc_set_foreground (gc, BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1807   
1808   gtk_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
1809   gtk_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
1810   gtk_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
1811   gtk_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
1812
1813   g_object_unref (layout);
1814 }
1815
1816 static void
1817 gtk_calendar_paint_day_names (GtkWidget *widget)
1818 {
1819   GtkCalendar *calendar;
1820   GdkGC *gc;
1821   char buffer[255];
1822   int day,i;
1823   int day_width, cal_width;
1824   gint cal_height;
1825   int day_wid_sep;
1826   PangoLayout *layout;
1827   PangoRectangle logical_rect;
1828   GtkCalendarPrivateData *private_data;
1829   gint focus_padding;
1830   gint focus_width;
1831   
1832   g_return_if_fail (GTK_IS_CALENDAR (widget));
1833   calendar = GTK_CALENDAR (widget);
1834   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1835   gc = calendar->gc;
1836   
1837   gtk_widget_style_get (GTK_WIDGET (widget),
1838                         "focus-line-width", &focus_width,
1839                         "focus-padding", &focus_padding,
1840                         NULL);
1841   /*
1842    * Handle freeze/thaw functionality
1843    */
1844   
1845   if (private_data->freeze_count)
1846     {
1847       private_data->dirty_day_names = 1;
1848       return;
1849     }
1850   private_data->dirty_day_names = 0;
1851   
1852   /*
1853    * Clear the window
1854    */
1855   
1856   gdk_window_clear (private_data->day_name_win);
1857   
1858   day_width = private_data->day_width;
1859   cal_width = widget->allocation.width;
1860   cal_height = widget->allocation.height;
1861   day_wid_sep = day_width + DAY_XSEP;
1862   
1863   /*
1864    * Draw rectangles as inverted background for the labels.
1865    */
1866   
1867   gdk_gc_set_foreground (gc, SELECTED_BG_COLOR (widget));
1868   gdk_draw_rectangle (private_data->day_name_win, gc, TRUE,
1869                       CALENDAR_MARGIN, CALENDAR_MARGIN,
1870                       cal_width-CALENDAR_MARGIN * 2,
1871                       private_data->day_name_h - CALENDAR_MARGIN);
1872   
1873   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1874     gdk_draw_rectangle (private_data->day_name_win, gc, TRUE,
1875                         CALENDAR_MARGIN,
1876                         private_data->day_name_h - CALENDAR_YSEP,
1877                         private_data->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
1878                         CALENDAR_YSEP);
1879   
1880   /*
1881    * Write the labels
1882    */
1883
1884   layout = gtk_widget_create_pango_layout (widget, NULL);
1885   
1886   gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (widget));
1887   for (i = 0; i < 7; i++)
1888     {
1889       if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1890         day = 6 - i;
1891       else
1892         day = i;
1893       if (private_data->week_start == 1) 
1894         day= (day+1)%7;
1895       g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
1896
1897       pango_layout_set_text (layout, buffer, -1);
1898       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1899
1900       gdk_draw_layout (private_data->day_name_win, gc, 
1901                        (CALENDAR_MARGIN +
1902                         + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
1903                            (private_data->week_width + (private_data->week_width ? CALENDAR_XSEP : 0))
1904                            : 0)
1905                         + day_wid_sep * i
1906                         + (day_width - logical_rect.width)/2),
1907                        CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y,
1908                        layout);
1909     }
1910   
1911   g_object_unref (layout);
1912 }
1913
1914 static void
1915 gtk_calendar_paint_week_numbers (GtkWidget *widget)
1916 {
1917   GtkCalendar *calendar;
1918   GdkGC *gc;
1919   gint row, week = 0, year;
1920   gint x_loc;
1921   char buffer[3];
1922   gint y_loc, day_height;
1923   GtkCalendarPrivateData *private_data;
1924   PangoLayout *layout;
1925   PangoRectangle logical_rect;
1926   gint focus_padding;
1927   gint focus_width;
1928   
1929   g_return_if_fail (GTK_IS_CALENDAR (widget));
1930   g_return_if_fail (widget->window != NULL);
1931   calendar = GTK_CALENDAR (widget);
1932   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
1933   gc = calendar->gc;
1934   
1935   /*
1936    * Handle freeze/thaw functionality
1937    */
1938   
1939   if (private_data->freeze_count)
1940     {
1941       private_data->dirty_week = 1;
1942       return;
1943     }
1944   private_data->dirty_week = 0;
1945   
1946   gtk_widget_style_get (GTK_WIDGET (widget),
1947                         "focus-line-width", &focus_width,
1948                         "focus-padding", &focus_padding,
1949                         NULL);
1950   
1951   /*
1952    * Clear the window
1953    */
1954   
1955   gdk_window_clear (private_data->week_win);
1956   
1957   /*
1958    * Draw a rectangle as inverted background for the labels.
1959    */
1960   
1961   gdk_gc_set_foreground (gc, SELECTED_BG_COLOR (widget));
1962   if (private_data->day_name_win)
1963     gdk_draw_rectangle (private_data->week_win, gc, TRUE,
1964                         CALENDAR_MARGIN,
1965                         0,
1966                         private_data->week_width - CALENDAR_MARGIN,
1967                         private_data->main_h - CALENDAR_MARGIN);
1968   else
1969     gdk_draw_rectangle (private_data->week_win, gc, TRUE,
1970                         CALENDAR_MARGIN,
1971                         CALENDAR_MARGIN,
1972                         private_data->week_width - CALENDAR_MARGIN,
1973                         private_data->main_h - 2 * CALENDAR_MARGIN);
1974   
1975   /*
1976    * Write the labels
1977    */
1978   
1979   layout = gtk_widget_create_pango_layout (widget, NULL);
1980   
1981   gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (widget));
1982   day_height = row_height (calendar);
1983   for (row = 0; row < 6; row++)
1984     {
1985       year = calendar->year;
1986       if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
1987         year++;
1988
1989       g_return_if_fail (week_of_year (&week, &year,             
1990                                       ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
1991                                        + calendar->month) % 12 + 1, calendar->day[row][6]));
1992
1993       g_snprintf (buffer, sizeof (buffer), "%d", week);
1994       pango_layout_set_text (layout, buffer, -1);
1995       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1996
1997       y_loc = top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
1998
1999       x_loc = (private_data->week_width
2000                - logical_rect.width
2001                - CALENDAR_XSEP - focus_padding - focus_width);
2002
2003       gdk_draw_layout (private_data->week_win, gc, x_loc, y_loc, layout);
2004     }
2005   
2006   g_object_unref (layout);
2007 }
2008
2009 static void
2010 gtk_calendar_paint_day_num (GtkWidget *widget,
2011                             gint       day)
2012 {
2013   GtkCalendar *calendar;
2014   gint r, c, row, col;
2015   
2016   g_return_if_fail (GTK_IS_CALENDAR (widget));
2017   
2018   calendar = GTK_CALENDAR (widget);
2019   
2020   row = -1;
2021   col = -1;
2022   for (r = 0; r < 6; r++)
2023     for (c = 0; c < 7; c++)
2024       if (calendar->day_month[r][c] == MONTH_CURRENT &&
2025           calendar->day[r][c] == day)
2026         {
2027           row = r;
2028           col = c;
2029         }
2030   
2031   g_return_if_fail (row != -1);
2032   g_return_if_fail (col != -1);
2033   
2034   gtk_calendar_paint_day (widget, row, col);
2035 }
2036
2037 static void
2038 gtk_calendar_paint_day (GtkWidget *widget,
2039                         gint       row,
2040                         gint       col)
2041 {
2042   GtkCalendar *calendar;
2043   GdkGC *gc;
2044   gchar buffer[255];
2045   gint day;
2046   gint day_height;
2047   gint x_left;
2048   gint x_loc;
2049   gint y_top;
2050   gint y_loc;
2051   gint day_xspace;
2052   gint focus_width;
2053
2054   GtkCalendarPrivateData *private_data;
2055   PangoLayout *layout;
2056   PangoRectangle logical_rect;
2057   
2058   g_return_if_fail (GTK_IS_CALENDAR (widget));
2059   g_return_if_fail (row < 6);
2060   g_return_if_fail (col < 7);
2061   calendar = GTK_CALENDAR (widget);
2062   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2063
2064   /*
2065    * Handle freeze/thaw functionality
2066    */
2067   
2068   if (private_data->freeze_count)
2069     {
2070       private_data->dirty_main = 1;
2071       return;
2072     }
2073   
2074   gtk_widget_style_get (widget, "focus-line-width", &focus_width, NULL);
2075
2076   day_height = row_height (calendar);
2077   
2078   day_xspace = private_data->day_width - private_data->max_day_char_width*2;
2079   
2080   day = calendar->day[row][col];
2081   
2082   x_left = left_x_for_column (calendar, col);
2083   x_loc = x_left + private_data->day_width / 2 + private_data->max_day_char_width;
2084   
2085   y_top = top_y_for_row (calendar, row);
2086   
2087   gdk_window_clear_area (private_data->main_win, x_left, y_top,
2088                          private_data->day_width, day_height);
2089   
2090   gc = calendar->gc;
2091   
2092   if (calendar->day_month[row][col] == MONTH_PREV)
2093     {
2094       gdk_gc_set_foreground (gc, PREV_MONTH_COLOR (GTK_WIDGET (calendar)));
2095     } 
2096   else if (calendar->day_month[row][col] == MONTH_NEXT)
2097     {
2098       gdk_gc_set_foreground (gc, NEXT_MONTH_COLOR (GTK_WIDGET (calendar)));
2099     } 
2100   else 
2101     {
2102       /*
2103       if (calendar->highlight_row == row && calendar->highlight_col == col)
2104         {
2105           gdk_gc_set_foreground (gc, HIGHLIGHT_BACK_COLOR (GTK_WIDGET (calendar)));
2106           gdk_draw_rectangle (private_data->main_win, gc, TRUE, x_left, y_top,
2107                               private_data->day_width, day_height);
2108         }
2109       */
2110       if (calendar->selected_day == day)
2111         {
2112           gdk_gc_set_foreground (gc, SELECTED_BG_COLOR (GTK_WIDGET (calendar)));
2113           gdk_draw_rectangle (private_data->main_win, gc, TRUE, x_left, y_top,
2114                               private_data->day_width, day_height);
2115         }
2116       
2117       if (calendar->marked_date[day-1])
2118         gdk_gc_set_foreground (gc, MARKED_COLOR  (GTK_WIDGET (calendar)));
2119       else
2120         gdk_gc_set_foreground (gc, NORMAL_DAY_COLOR (GTK_WIDGET (calendar)));
2121   
2122       if (calendar->selected_day == day)
2123         gdk_gc_set_foreground (gc, SELECTED_FG_COLOR (GTK_WIDGET (calendar)));
2124       else
2125         gdk_gc_set_foreground (gc, & (GTK_WIDGET (calendar)->style->fg[GTK_WIDGET_STATE (calendar)]));
2126     }
2127     
2128
2129   g_snprintf (buffer, sizeof (buffer), "%d", day);
2130   layout = gtk_widget_create_pango_layout (widget, buffer);
2131   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2132   
2133   x_loc -= logical_rect.width;
2134
2135   y_loc = y_top + (day_height - logical_rect.height) / 2;
2136   gdk_draw_layout (private_data->main_win, gc,
2137                    x_loc, y_loc, layout);
2138   if (calendar->marked_date[day-1]
2139       && calendar->day_month[row][col] == MONTH_CURRENT)
2140     gdk_draw_layout (private_data->main_win, gc,
2141                      x_loc-1, y_loc, layout);
2142
2143   if (GTK_WIDGET_HAS_FOCUS (calendar) 
2144       && calendar->focus_row == row && calendar->focus_col == col)
2145     {
2146       GtkStateType state;
2147
2148       if (calendar->selected_day == day)
2149         state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2150       else
2151         state = GTK_STATE_NORMAL;
2152       
2153       gtk_paint_focus (widget->style, 
2154                        private_data->main_win,
2155                        (calendar->selected_day == day) 
2156                           ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, 
2157                        NULL, widget, "calendar-day",
2158                        x_left, y_top, 
2159                        private_data->day_width, 
2160                        day_height);
2161     }
2162
2163   g_object_unref (layout);
2164 }
2165
2166
2167 static void
2168 gtk_calendar_paint_main (GtkWidget *widget)
2169 {
2170   GtkCalendar *calendar;
2171   GtkCalendarPrivateData *private_data;
2172   gint row, col;
2173   
2174   g_return_if_fail (GTK_IS_CALENDAR (widget));
2175   g_return_if_fail (widget->window != NULL);
2176   
2177   calendar = GTK_CALENDAR (widget);
2178   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2179
2180   if (private_data->freeze_count)
2181     {
2182       private_data->dirty_main = 1;
2183       return;
2184     }
2185   private_data->dirty_main = 0;
2186   gdk_window_clear (private_data->main_win);
2187   
2188   /* gtk_calendar_compute_days (calendar); */ /* REMOVE later */
2189   
2190   for (col = 0; col < 7; col++)
2191     for (row = 0; row < 6; row++)
2192       gtk_calendar_paint_day (widget, row, col);
2193 }
2194
2195 static void
2196 gtk_calendar_compute_days (GtkCalendar *calendar)
2197 {
2198   GtkCalendarPrivateData *private_data;
2199   gint month;
2200   gint year;
2201   gint ndays_in_month;
2202   gint ndays_in_prev_month;
2203   gint first_day;
2204   gint row;
2205   gint col;
2206   gint day;
2207   
2208   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2209
2210   private_data = GTK_CALENDAR_PRIVATE_DATA (GTK_WIDGET (calendar));
2211   
2212   year = calendar->year;
2213   month = calendar->month + 1;
2214   
2215   ndays_in_month = month_length[leap (year)][month];
2216   
2217   first_day = day_of_week (year, month, 1);
2218   
2219   if (private_data->week_start == 1)
2220     first_day--;
2221   else
2222     first_day %= 7;
2223   
2224   
2225   /* Compute days of previous month */
2226   if (month > 1)
2227     ndays_in_prev_month = month_length[leap (year)][month-1];
2228   else
2229     ndays_in_prev_month = month_length[leap (year)][12];
2230   day = ndays_in_prev_month - first_day + 1;
2231   
2232   row = 0;
2233   if (first_day > 0)
2234     {
2235       for (col = 0; col < first_day; col++)
2236         {
2237           calendar->day[row][col] = day;
2238           calendar->day_month[row][col] = MONTH_PREV;
2239           day++;
2240         }
2241     }
2242   
2243   /* Compute days of current month */
2244   col = first_day;
2245   for (day = 1; day <= ndays_in_month; day++)
2246     {
2247       calendar->day[row][col] = day;
2248       calendar->day_month[row][col] = MONTH_CURRENT;
2249       
2250       col++;
2251       if (col == 7)
2252         {
2253           row++;
2254           col = 0;
2255         }
2256     }
2257   
2258   /* Compute days of next month */
2259   day = 1;
2260   for (; row <= 5; row++)
2261     {
2262       for (; col <= 6; col++)
2263         {
2264           calendar->day[row][col] = day;
2265           calendar->day_month[row][col] = MONTH_NEXT;
2266           day++;
2267         }
2268       col = 0;
2269     }
2270 }
2271
2272 void
2273 gtk_calendar_display_options (GtkCalendar              *calendar,
2274                               GtkCalendarDisplayOptions flags)
2275 {
2276   gtk_calendar_set_display_options (calendar, flags);
2277 }
2278
2279 /**
2280  * gtk_calendar_get_display_options:
2281  * @calendar: a #GtkCalendar
2282  * 
2283  * Returns the current display options of @calendar. 
2284  * 
2285  * Return value: the display options.
2286  *
2287  * Since: 2.4
2288  **/
2289 GtkCalendarDisplayOptions 
2290 gtk_calendar_get_display_options (GtkCalendar         *calendar)
2291 {
2292   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
2293
2294   return calendar->display_flags;
2295 }
2296
2297 /**
2298  * gtk_calendar_set_display_options:
2299  * @calendar: a #GtkCalendar
2300  * @flags: the display options to set
2301  * 
2302  * Sets display options (whether to display the heading and the month  
2303  * headings).
2304  *
2305  * Since: 2.4
2306  **/
2307 void
2308 gtk_calendar_set_display_options (GtkCalendar          *calendar,
2309                                   GtkCalendarDisplayOptions flags)
2310 {
2311   GtkCalendarPrivateData *private_data;
2312   gint resize = 0;
2313   GtkWidget *widget;
2314   gint i;
2315   GtkCalendarDisplayOptions old_flags;
2316   
2317   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2318   
2319   widget = GTK_WIDGET (calendar);
2320   private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);
2321   old_flags = calendar->display_flags;
2322   
2323   if (GTK_WIDGET_REALIZED (widget))
2324     {
2325       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
2326         {
2327           resize ++;
2328           if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2329               && (private_data->header_win))
2330             {
2331               calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
2332               gtk_calendar_realize_arrows (widget);
2333             }
2334           else
2335             {
2336               for (i = 0; i < 4; i++)
2337                 {
2338                   if (private_data->arrow_win[i])
2339                     {
2340                       gdk_window_set_user_data (private_data->arrow_win[i], 
2341                                                 NULL);
2342                       gdk_window_destroy (private_data->arrow_win[i]);
2343                       private_data->arrow_win[i] = NULL;
2344                     }
2345                 }
2346             }
2347         }
2348       
2349       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
2350         {
2351           resize++;
2352           
2353           if (flags & GTK_CALENDAR_SHOW_HEADING)
2354             {
2355               calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
2356               gtk_calendar_realize_header (widget);
2357             }
2358           else
2359             {
2360               for (i = 0; i < 4; i++)
2361                 {
2362                   if (private_data->arrow_win[i])
2363                     {
2364                       gdk_window_set_user_data (private_data->arrow_win[i], 
2365                                                 NULL);
2366                       gdk_window_destroy (private_data->arrow_win[i]);
2367                       private_data->arrow_win[i] = NULL;
2368                     }
2369                 }
2370               gdk_window_set_user_data (private_data->header_win, NULL);
2371               gdk_window_destroy (private_data->header_win);
2372               private_data->header_win = NULL;
2373             }
2374         }
2375       
2376       
2377       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
2378         {
2379           resize++;
2380           
2381           if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
2382             {
2383               calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
2384               gtk_calendar_realize_day_names (widget);
2385             }
2386           else
2387             {
2388               gdk_window_set_user_data (private_data->day_name_win, NULL);
2389               gdk_window_destroy (private_data->day_name_win);
2390               private_data->day_name_win = NULL;
2391             }
2392         }
2393       
2394       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2395         {
2396           resize++;
2397           
2398           if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2399             {
2400               calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
2401               gtk_calendar_realize_week_numbers (widget);
2402             }
2403           else
2404             {
2405               gdk_window_set_user_data (private_data->week_win, NULL);
2406               gdk_window_destroy (private_data->week_win);
2407               private_data->week_win = NULL;
2408             }
2409         }
2410
2411       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
2412         g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
2413       
2414       calendar->display_flags = flags;
2415       if (resize)
2416         gtk_widget_queue_resize (GTK_WIDGET (calendar));
2417       
2418     } 
2419   else
2420     calendar->display_flags = flags;
2421   
2422   g_object_freeze_notify (G_OBJECT (calendar));
2423   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
2424     g_object_notify (G_OBJECT (calendar), "show_heading");
2425   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
2426     g_object_notify (G_OBJECT (calendar), "show_day_names");
2427   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
2428     g_object_notify (G_OBJECT (calendar), "no_month_change");
2429   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2430     g_object_notify (G_OBJECT (calendar), "show_week_numbers");
2431   g_object_thaw_notify (G_OBJECT (calendar));
2432 }
2433
2434 gboolean
2435 gtk_calendar_select_month (GtkCalendar *calendar,
2436                            guint        month,
2437                            guint        year)
2438 {
2439   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
2440   g_return_val_if_fail (month <= 11, FALSE);
2441   
2442   calendar->month = month;
2443   calendar->year  = year;
2444   
2445   gtk_calendar_compute_days (calendar);
2446   
2447   gtk_widget_queue_draw (GTK_WIDGET (calendar));
2448
2449   g_object_freeze_notify (G_OBJECT (calendar));
2450   g_object_notify (G_OBJECT (calendar), "month");
2451   g_object_notify (G_OBJECT (calendar), "year");
2452   g_object_thaw_notify (G_OBJECT (calendar));
2453
2454   g_signal_emit (calendar,
2455                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
2456                  0);
2457   return TRUE;
2458 }
2459
2460 void
2461 gtk_calendar_select_day (GtkCalendar *calendar,
2462                          guint        day)
2463 {
2464   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2465   g_return_if_fail (day <= 31);
2466   
2467   /* gtk_calendar_compute_days (calendar); */
2468   
2469   /* Deselect the old day */
2470   if (calendar->selected_day > 0)
2471     {
2472       gint selected_day;
2473       
2474       selected_day = calendar->selected_day;
2475       calendar->selected_day = 0;
2476       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2477         gtk_calendar_paint_day_num (GTK_WIDGET (calendar), selected_day);
2478     }
2479   
2480   calendar->selected_day = day;
2481   
2482   /* Select the new day */
2483   if (day != 0)
2484     {
2485       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2486         gtk_calendar_paint_day_num (GTK_WIDGET (calendar), day);
2487     }
2488   
2489   g_object_notify (G_OBJECT (calendar), "day");
2490
2491   g_signal_emit (calendar,
2492                  gtk_calendar_signals[DAY_SELECTED_SIGNAL],
2493                  0);
2494 }
2495
2496 static void
2497 gtk_calendar_select_and_focus_day (GtkCalendar *calendar,
2498                                    guint        day)
2499 {
2500   gint old_focus_row = calendar->focus_row;
2501   gint old_focus_col = calendar->focus_col;
2502   gint row;
2503   gint col;
2504   
2505   for (row = 0; row < 6; row ++)
2506     for (col = 0; col < 7; col++)
2507       {
2508         if (calendar->day_month[row][col] == MONTH_CURRENT 
2509             && calendar->day[row][col] == day)
2510           {
2511             calendar->focus_row = row;
2512             calendar->focus_col = col;
2513           }
2514       }
2515
2516   if (old_focus_row != -1 && old_focus_col != -1)
2517     gtk_calendar_paint_day (GTK_WIDGET (calendar), old_focus_row, old_focus_col);
2518   
2519   gtk_calendar_select_day (calendar, day);
2520 }
2521
2522 void
2523 gtk_calendar_clear_marks (GtkCalendar *calendar)
2524 {
2525   guint day;
2526   
2527   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2528   
2529   for (day = 0; day < 31; day++)
2530     {
2531       calendar->marked_date[day] = FALSE;
2532     }
2533  
2534   calendar->num_marked_dates = 0;
2535  
2536   if (GTK_WIDGET_DRAWABLE (calendar))
2537     {
2538       gtk_calendar_paint_main (GTK_WIDGET (calendar));
2539     }
2540 }
2541
2542 gboolean
2543 gtk_calendar_mark_day (GtkCalendar *calendar,
2544                        guint        day)
2545 {
2546   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
2547   
2548   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
2549     {
2550       calendar->marked_date[day - 1] = TRUE;
2551       calendar->num_marked_dates++;
2552     }
2553   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2554     {
2555       gtk_calendar_paint_main (GTK_WIDGET (calendar));
2556     }
2557   
2558   return TRUE;
2559 }
2560
2561 gboolean
2562 gtk_calendar_unmark_day (GtkCalendar *calendar,
2563                          guint        day)
2564 {
2565   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
2566   
2567   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
2568     {
2569       calendar->marked_date[day - 1] = FALSE;
2570       calendar->num_marked_dates--;
2571     }
2572   
2573   if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
2574     {
2575       gtk_calendar_paint_main (GTK_WIDGET (calendar));
2576     }
2577   
2578   return TRUE;
2579 }
2580
2581 void
2582 gtk_calendar_get_date (GtkCalendar *calendar,
2583                        guint       *year,
2584                        guint       *month,
2585                        guint       *day)
2586 {
2587   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2588   
2589   if (year)
2590     *year = calendar->year;
2591   
2592   if (month)
2593     *month = calendar->month;
2594   
2595   if (day)
2596     *day = calendar->selected_day;
2597 }
2598
2599 #define CALENDAR_INITIAL_TIMER_DELAY    200
2600 #define CALENDAR_TIMER_DELAY            20
2601
2602 static void
2603 arrow_action (GtkCalendar *calendar,
2604               guint        arrow)
2605 {
2606   switch (arrow)
2607     {
2608     case ARROW_YEAR_LEFT:
2609       gtk_calendar_set_year_prev (calendar);
2610       break;
2611     case ARROW_YEAR_RIGHT:
2612       gtk_calendar_set_year_next (calendar);
2613       break;
2614     case ARROW_MONTH_LEFT:
2615       gtk_calendar_set_month_prev (calendar);
2616       break;
2617     case ARROW_MONTH_RIGHT:
2618       gtk_calendar_set_month_next (calendar);
2619       break;
2620     default:;
2621       // do nothing
2622     }
2623 }
2624
2625 static gboolean
2626 calendar_timer (gpointer data)
2627 {
2628   GtkCalendar *calendar = data;
2629   GtkCalendarPrivateData *private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);
2630   gboolean retval = FALSE;
2631   
2632   GDK_THREADS_ENTER ();
2633
2634   if (private_data->timer)
2635     {
2636       arrow_action (calendar, private_data->click_child);
2637
2638       if (private_data->need_timer)
2639         {
2640           private_data->need_timer = FALSE;
2641           private_data->timer = g_timeout_add (CALENDAR_TIMER_DELAY, 
2642                                                (GtkFunction) calendar_timer, 
2643                                                (gpointer) calendar);
2644         }
2645       else 
2646         retval = TRUE;
2647     }
2648
2649   GDK_THREADS_LEAVE ();
2650
2651   return retval;
2652 }
2653
2654 static void
2655 start_spinning (GtkWidget *widget,
2656                 gint       click_child)
2657 {
2658   GtkCalendarPrivateData *private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2659
2660   private_data->click_child = click_child;
2661   
2662   if (!private_data->timer)
2663     {
2664       private_data->need_timer = TRUE;
2665       private_data->timer = g_timeout_add (CALENDAR_INITIAL_TIMER_DELAY, 
2666                                            calendar_timer,
2667                                            (gpointer) widget);
2668     }
2669 }
2670
2671 static void
2672 stop_spinning (GtkWidget *widget)
2673 {
2674   GtkCalendarPrivateData *private_data;
2675
2676   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2677
2678   if (private_data->timer)
2679     {
2680       g_source_remove (private_data->timer);
2681       private_data->timer = 0;
2682       private_data->need_timer = FALSE;
2683     }
2684 }
2685
2686 static void
2687 gtk_calendar_destroy (GtkObject *object)
2688 {
2689   stop_spinning (GTK_WIDGET (object));
2690   
2691   GTK_OBJECT_CLASS (parent_class)->destroy (object);
2692 }
2693
2694 static void
2695 gtk_calendar_grab_notify (GtkWidget *widget,
2696                           gboolean   was_grabbed)
2697 {
2698   if (!was_grabbed)
2699     stop_spinning (widget);
2700 }
2701
2702 static gboolean
2703 gtk_calendar_button_press (GtkWidget      *widget,
2704                            GdkEventButton *event)
2705 {
2706   GtkCalendar *calendar;
2707   GtkCalendarPrivateData *private_data;
2708   gint arrow = -1;
2709   
2710   calendar = GTK_CALENDAR (widget);
2711   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2712   
2713   if (event->window == private_data->main_win)
2714     gtk_calendar_main_button (widget, event);
2715
2716   if (!GTK_WIDGET_HAS_FOCUS (widget))
2717     gtk_widget_grab_focus (widget);
2718
2719   for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2720     {
2721       if (event->window == private_data->arrow_win[arrow])
2722         {
2723           
2724           /* only call the action on single click, not double */
2725           if (event->type == GDK_BUTTON_PRESS)
2726             {
2727               arrow_action (calendar, arrow);
2728               
2729               if (event->button == 1)
2730                 start_spinning (widget, arrow);
2731             }
2732
2733           return TRUE;
2734         }
2735     }
2736
2737   return FALSE;
2738 }
2739
2740 static gboolean
2741 gtk_calendar_button_release (GtkWidget    *widget,
2742                              GdkEventButton *event)
2743 {
2744   if (event->button == 1) 
2745     stop_spinning (widget);
2746
2747   return TRUE;
2748 }
2749
2750 static gboolean
2751 gtk_calendar_motion_notify (GtkWidget      *widget,
2752                             GdkEventMotion *event)
2753 {
2754   GtkCalendar *calendar;
2755   GtkCalendarPrivateData *private_data;
2756   gint event_x, event_y;
2757   gint row, col;
2758   gint old_row, old_col;
2759   
2760   calendar = GTK_CALENDAR (widget);
2761   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2762   event_x = (gint) (event->x);
2763   event_y = (gint) (event->y);
2764   
2765   if (event->window == private_data->main_win)
2766     {
2767       
2768       row = row_from_y (calendar, event_y);
2769       col = column_from_x (calendar, event_x);
2770       
2771       if (row != calendar->highlight_row || calendar->highlight_col != col)
2772         {
2773           old_row = calendar->highlight_row;
2774           old_col = calendar->highlight_col;
2775           if (old_row > -1 && old_col > -1)
2776             {
2777               calendar->highlight_row = -1;
2778               calendar->highlight_col = -1;
2779               gtk_calendar_paint_day (widget, old_row, old_col);
2780             }
2781           
2782           calendar->highlight_row = row;
2783           calendar->highlight_col = col;
2784           
2785           if (row > -1 && col > -1)
2786             gtk_calendar_paint_day (widget, row, col);
2787         }
2788     }
2789   return TRUE;
2790 }
2791
2792 static gboolean
2793 gtk_calendar_enter_notify (GtkWidget        *widget,
2794                            GdkEventCrossing *event)
2795 {
2796   GtkCalendar *calendar;
2797   GtkCalendarPrivateData *private_data;
2798   
2799   calendar = GTK_CALENDAR (widget);
2800   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2801   
2802   if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
2803     {
2804       private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2805       gtk_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
2806     }
2807   
2808   if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
2809     {
2810       private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2811       gtk_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
2812     }
2813   
2814   if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
2815     {
2816       private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2817       gtk_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
2818     }
2819   
2820   if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
2821     {
2822       private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2823       gtk_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
2824     }
2825   
2826   return TRUE;
2827 }
2828
2829 static gboolean
2830 gtk_calendar_leave_notify (GtkWidget        *widget,
2831                            GdkEventCrossing *event)
2832 {
2833   GtkCalendar *calendar;
2834   GtkCalendarPrivateData *private_data;
2835   gint row;
2836   gint col;
2837   
2838   calendar = GTK_CALENDAR (widget);
2839   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2840   
2841   if (event->window == private_data->main_win)
2842     {
2843       row = calendar->highlight_row;
2844       col = calendar->highlight_col;
2845       calendar->highlight_row = -1;
2846       calendar->highlight_col = -1;
2847       if (row > -1 && col > -1)
2848         gtk_calendar_paint_day (widget, row, col);
2849     }
2850   
2851   if (event->window == private_data->arrow_win[ARROW_MONTH_LEFT])
2852     {
2853       private_data->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
2854       gtk_calendar_paint_arrow (widget, ARROW_MONTH_LEFT);
2855     }
2856   
2857   if (event->window == private_data->arrow_win[ARROW_MONTH_RIGHT])
2858     {
2859       private_data->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
2860       gtk_calendar_paint_arrow (widget, ARROW_MONTH_RIGHT);
2861     }
2862   
2863   if (event->window == private_data->arrow_win[ARROW_YEAR_LEFT])
2864     {
2865       private_data->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
2866       gtk_calendar_paint_arrow (widget, ARROW_YEAR_LEFT);
2867     }
2868   
2869   if (event->window == private_data->arrow_win[ARROW_YEAR_RIGHT])
2870     {
2871       private_data->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
2872       gtk_calendar_paint_arrow (widget, ARROW_YEAR_RIGHT);
2873     }
2874   
2875   return TRUE;
2876 }
2877
2878 static void
2879 gtk_calendar_paint_arrow (GtkWidget *widget,
2880                           guint      arrow)
2881 {
2882   GtkCalendarPrivateData *private_data;
2883   GdkWindow *window;
2884   GdkGC *gc;
2885   GtkCalendar *calendar;
2886   gint state;
2887   gint width, height;
2888   
2889   calendar = GTK_CALENDAR (widget);
2890   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2891
2892   if (private_data->freeze_count)
2893     {
2894       private_data->dirty_header = 1;
2895       return;
2896     }
2897   window = private_data->arrow_win[arrow];
2898   if (window)
2899     {
2900       state = private_data->arrow_state[arrow];
2901       gc = calendar->gc;
2902       
2903       gdk_window_clear (window);
2904       gdk_window_set_background (window, &(widget)->style->bg[state]);
2905       gdk_drawable_get_size (window, &width, &height);
2906       gdk_window_clear_area (window,
2907                              0,0,
2908                              width,height);
2909       
2910       gdk_gc_set_foreground (gc, & (widget)->style->fg[state]);
2911       
2912       if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2913         draw_arrow_left (window, gc, width/2 - 3, height/2 - 4, 8);
2914       else 
2915         draw_arrow_right (window, gc, width/2 - 2, height/2 - 4, 8);
2916       return;
2917     }
2918 }
2919
2920 void
2921 gtk_calendar_freeze (GtkCalendar *calendar)
2922 {
2923   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2924   
2925   GTK_CALENDAR_PRIVATE_DATA (calendar)->freeze_count++;
2926 }
2927
2928 void
2929 gtk_calendar_thaw (GtkCalendar *calendar)
2930 {
2931   GtkCalendarPrivateData *private_data;
2932
2933   g_return_if_fail (GTK_IS_CALENDAR (calendar));
2934   
2935   private_data = GTK_CALENDAR_PRIVATE_DATA (calendar);
2936   
2937   if (private_data->freeze_count)
2938     if (!(--private_data->freeze_count))
2939       {
2940         if (private_data->dirty_header)
2941           if (GTK_WIDGET_DRAWABLE (calendar))
2942             gtk_calendar_paint_header (GTK_WIDGET (calendar));
2943         
2944         if (private_data->dirty_day_names)
2945           if (GTK_WIDGET_DRAWABLE (calendar))
2946             gtk_calendar_paint_day_names (GTK_WIDGET (calendar));
2947         
2948         if (private_data->dirty_week)
2949           if (GTK_WIDGET_DRAWABLE (calendar))
2950             gtk_calendar_paint_week_numbers (GTK_WIDGET (calendar));
2951         
2952         if (private_data->dirty_main)
2953           if (GTK_WIDGET_DRAWABLE (calendar))
2954             gtk_calendar_paint_main (GTK_WIDGET (calendar));
2955       }
2956 }
2957
2958 static void
2959 gtk_calendar_set_background (GtkWidget *widget)
2960 {
2961   GtkCalendar *calendar;
2962   GtkCalendarPrivateData *private_data;
2963   gint i;
2964   
2965   calendar = GTK_CALENDAR (widget);
2966   private_data = GTK_CALENDAR_PRIVATE_DATA (widget);
2967
2968   if (GTK_WIDGET_REALIZED (widget))
2969     {
2970       for (i = 0; i < 4; i++)
2971         {
2972           if (private_data->arrow_win[i])
2973             gdk_window_set_background (private_data->arrow_win[i], 
2974                                        HEADER_BG_COLOR (widget));
2975         }
2976       if (private_data->header_win)
2977         gdk_window_set_background (private_data->header_win, 
2978                                    HEADER_BG_COLOR (widget));
2979       if (private_data->day_name_win)
2980         gdk_window_set_background (private_data->day_name_win, 
2981                                    BACKGROUND_COLOR (widget));
2982       if (private_data->week_win)
2983         gdk_window_set_background (private_data->week_win,
2984                                    BACKGROUND_COLOR (widget));
2985       if (private_data->main_win)
2986         gdk_window_set_background (private_data->main_win,
2987                                    BACKGROUND_COLOR (widget));
2988       if (widget->window)
2989         gdk_window_set_background (widget->window,
2990                                    BACKGROUND_COLOR (widget)); 
2991     }
2992 }
2993
2994 static void
2995 gtk_calendar_style_set (GtkWidget *widget,
2996                         GtkStyle  *previous_style)
2997 {
2998   if (previous_style && GTK_WIDGET_REALIZED (widget))
2999     gtk_calendar_set_background(widget);
3000 }
3001
3002 static void
3003 gtk_calendar_state_changed (GtkWidget      *widget,
3004                             GtkStateType    previous_state)
3005 {
3006   if (!GTK_WIDGET_IS_SENSITIVE (widget))
3007     stop_spinning (widget);    
3008
3009   gtk_calendar_set_background (widget);
3010 }
3011
3012 static void
3013 gtk_calendar_finalize (GObject *object)
3014 {
3015   GtkCalendarPrivateData *private_data;
3016   private_data = GTK_CALENDAR_PRIVATE_DATA (object);
3017   
3018   g_free (private_data);
3019   
3020   if (G_OBJECT_CLASS (parent_class)->finalize)
3021     (* G_OBJECT_CLASS (parent_class)->finalize) (object);
3022 }
3023
3024 static gboolean
3025 gtk_calendar_scroll (GtkWidget      *widget,
3026                      GdkEventScroll *event)
3027 {
3028   GtkCalendar *calendar = GTK_CALENDAR (widget);
3029
3030   if (event->direction == GDK_SCROLL_UP) 
3031     {
3032       if (!GTK_WIDGET_HAS_FOCUS (widget))
3033         gtk_widget_grab_focus (widget);
3034       gtk_calendar_set_month_prev (calendar);
3035     }
3036   else if (event->direction == GDK_SCROLL_DOWN) 
3037     {
3038       if (!GTK_WIDGET_HAS_FOCUS (widget))
3039         gtk_widget_grab_focus (widget);
3040       gtk_calendar_set_month_next (calendar);
3041     }
3042   else
3043     return FALSE;
3044
3045   return TRUE;
3046 }
3047
3048 static void 
3049 move_focus (GtkCalendar *calendar, 
3050             gint direction)
3051 {
3052   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3053
3054   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3055       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
3056     {
3057       if (calendar->focus_col > 0)
3058           calendar->focus_col--;
3059       else if (calendar->focus_row > 0)
3060         {
3061           calendar->focus_col = 6;
3062           calendar->focus_row--;
3063         }
3064     }
3065   else 
3066     {
3067       if (calendar->focus_col < 6)
3068         calendar->focus_col++;
3069       else if (calendar->focus_row < 5)
3070         {
3071           calendar->focus_col = 0;
3072           calendar->focus_row++;
3073         }
3074     }
3075 }
3076
3077 static gboolean
3078 gtk_calendar_key_press (GtkWidget   *widget,
3079                         GdkEventKey *event)
3080 {
3081   GtkCalendar *calendar;
3082   gint return_val;
3083   gint old_focus_row;
3084   gint old_focus_col;
3085   gint row, col, day;
3086   
3087   calendar = GTK_CALENDAR (widget);
3088   return_val = FALSE;
3089   
3090   old_focus_row = calendar->focus_row;
3091   old_focus_col = calendar->focus_col;
3092
3093   switch (event->keyval)
3094     {
3095     case GDK_KP_Left:
3096     case GDK_Left:
3097       return_val = TRUE;
3098       if (event->state & GDK_CONTROL_MASK)
3099         gtk_calendar_set_month_prev (calendar);
3100       else
3101         {
3102           move_focus (calendar, -1);
3103           gtk_calendar_paint_day (widget, old_focus_row, old_focus_col);
3104           gtk_calendar_paint_day (widget, calendar->focus_row,
3105                                   calendar->focus_col);
3106         }
3107       break;
3108     case GDK_KP_Right:
3109     case GDK_Right:
3110       return_val = TRUE;
3111       if (event->state & GDK_CONTROL_MASK)
3112         gtk_calendar_set_month_next (calendar);
3113       else
3114         {
3115           move_focus (calendar, 1);
3116           gtk_calendar_paint_day (widget, old_focus_row, old_focus_col);
3117           gtk_calendar_paint_day (widget, calendar->focus_row,
3118                                   calendar->focus_col);
3119         }
3120       break;
3121     case GDK_KP_Up:
3122     case GDK_Up:
3123       return_val = TRUE;
3124       if (event->state & GDK_CONTROL_MASK)
3125         gtk_calendar_set_year_prev (calendar);
3126       else
3127         {
3128           if (calendar->focus_row > 0)
3129             calendar->focus_row--;
3130           gtk_calendar_paint_day (widget, old_focus_row, old_focus_col);
3131           gtk_calendar_paint_day (widget, calendar->focus_row,
3132                                   calendar->focus_col);
3133         }
3134       break;
3135     case GDK_KP_Down:
3136     case GDK_Down:
3137       return_val = TRUE;
3138       if (event->state & GDK_CONTROL_MASK)
3139         gtk_calendar_set_year_next (calendar);
3140       else
3141         {
3142           if (calendar->focus_row < 5)
3143             calendar->focus_row++;
3144           gtk_calendar_paint_day (widget, old_focus_row, old_focus_col);
3145           gtk_calendar_paint_day (widget, calendar->focus_row,
3146                                   calendar->focus_col);
3147         }
3148       break;
3149     case GDK_KP_Space:
3150     case GDK_space:
3151       row = calendar->focus_row;
3152       col = calendar->focus_col;
3153       day = calendar->day[row][col];
3154       
3155       if (row > -1 && col > -1)
3156         {
3157           return_val = TRUE;
3158           gtk_calendar_freeze (calendar);         
3159
3160           if (calendar->day_month[row][col] == MONTH_PREV)
3161             gtk_calendar_set_month_prev (calendar);
3162           else if (calendar->day_month[row][col] == MONTH_NEXT)
3163             gtk_calendar_set_month_next (calendar);
3164
3165           gtk_calendar_select_and_focus_day (calendar, day);
3166           
3167           gtk_calendar_thaw (calendar);   
3168         }
3169     }   
3170   
3171   return return_val;
3172 }
3173
3174 static void
3175 gtk_calendar_set_display_option (GtkCalendar              *calendar,
3176                                  GtkCalendarDisplayOptions flag,
3177                                  gboolean                  setting)
3178 {
3179   GtkCalendarDisplayOptions flags;
3180   if (setting) 
3181     flags = calendar->display_flags | flag;
3182   else
3183     flags = calendar->display_flags & ~flag; 
3184   gtk_calendar_display_options (calendar, flags);
3185 }
3186
3187 static gboolean
3188 gtk_calendar_get_display_option (GtkCalendar              *calendar,
3189                                  GtkCalendarDisplayOptions flag)
3190 {
3191   return (calendar->display_flags & flag) != 0;
3192 }
3193
3194
3195 static void 
3196 gtk_calendar_set_property (GObject      *object,
3197                            guint         prop_id,
3198                            const GValue *value,
3199                            GParamSpec   *pspec)
3200 {
3201   GtkCalendar *calendar;
3202
3203   calendar = GTK_CALENDAR (object);
3204
3205   switch (prop_id) 
3206     {
3207     case PROP_YEAR:
3208       gtk_calendar_select_month (calendar,
3209                                  calendar->month,
3210                                  g_value_get_int (value));
3211       break;
3212     case PROP_MONTH:
3213       gtk_calendar_select_month (calendar,
3214                                  g_value_get_int (value),
3215                                  calendar->year);
3216       break;
3217     case PROP_DAY:
3218       gtk_calendar_select_day (calendar,
3219                                g_value_get_int (value));
3220       break;
3221     case PROP_SHOW_HEADING:
3222       gtk_calendar_set_display_option (calendar,
3223                                        GTK_CALENDAR_SHOW_HEADING,
3224                                        g_value_get_boolean (value));
3225       break;
3226     case PROP_SHOW_DAY_NAMES:
3227       gtk_calendar_set_display_option (calendar,
3228                                        GTK_CALENDAR_SHOW_DAY_NAMES,
3229                                        g_value_get_boolean (value));
3230       break;
3231     case PROP_NO_MONTH_CHANGE:
3232       gtk_calendar_set_display_option (calendar,
3233                                        GTK_CALENDAR_NO_MONTH_CHANGE,
3234                                        g_value_get_boolean (value));
3235       break;
3236     case PROP_SHOW_WEEK_NUMBERS:
3237       gtk_calendar_set_display_option (calendar,
3238                                        GTK_CALENDAR_SHOW_WEEK_NUMBERS,
3239                                        g_value_get_boolean (value));
3240       break;
3241     default:
3242       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3243       break;
3244     }
3245 }
3246
3247 static void 
3248 gtk_calendar_get_property (GObject      *object,
3249                            guint         prop_id,
3250                            GValue       *value,
3251                            GParamSpec   *pspec)
3252 {
3253   GtkCalendar *calendar;
3254
3255   calendar = GTK_CALENDAR (object);
3256
3257   switch (prop_id) 
3258     {
3259     case PROP_YEAR:
3260       g_value_set_int (value, calendar->year);
3261       break;
3262     case PROP_MONTH:
3263       g_value_set_int (value, calendar->month);
3264       break;
3265     case PROP_DAY:
3266       g_value_set_int (value, calendar->selected_day);
3267       break;
3268     case PROP_SHOW_HEADING:
3269       g_value_set_boolean (value, gtk_calendar_get_display_option (calendar,
3270                                                                    GTK_CALENDAR_SHOW_HEADING));
3271       break;
3272     case PROP_SHOW_DAY_NAMES:
3273       g_value_set_boolean (value, gtk_calendar_get_display_option (calendar,
3274                                                                    GTK_CALENDAR_SHOW_DAY_NAMES));
3275       break;
3276     case PROP_NO_MONTH_CHANGE:
3277       g_value_set_boolean (value, gtk_calendar_get_display_option (calendar,
3278                                                                    GTK_CALENDAR_NO_MONTH_CHANGE));
3279       break;
3280     case PROP_SHOW_WEEK_NUMBERS:
3281       g_value_set_boolean (value, gtk_calendar_get_display_option (calendar,
3282                                                                    GTK_CALENDAR_SHOW_WEEK_NUMBERS));
3283       break;
3284     default:
3285       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
3286       break;
3287     }
3288
3289 }