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