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