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