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