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