]> Pileus Git - ~andy/gtk/blob - gtk/gtkcalendar.c
Try more decent appearance of calendar details separator. (#339540)
[~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 Groenlund
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 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
38 #include <langinfo.h>
39 #endif
40 #include <string.h>
41 #include <stdlib.h>
42 #include <time.h>
43
44 #include <glib.h>
45
46 #ifdef G_OS_WIN32
47 #include <windows.h>
48 #endif
49
50 #undef GTK_DISABLE_DEPRECATED
51 #include "gtkcalendar.h"
52
53 #include "gtkdnd.h"
54 #include "gtkintl.h"
55 #include "gtkmain.h"
56 #include "gtkmarshalers.h"
57 #include "gtktooltip.h"
58 #include "gtkprivate.h"
59 #include "gdk/gdkkeysyms.h"
60 #include "gtkalias.h"
61
62 /***************************************************************************/
63 /* The following date routines are taken from the lib_date package. 
64  * They have been minimally edited to avoid conflict with types defined
65  * in win32 headers.
66  */
67
68 static const guint month_length[2][13] =
69 {
70   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
71   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
72 };
73
74 static const guint days_in_months[2][14] =
75 {
76   { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
77   { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
78 };
79
80 static glong  calc_days(guint year, guint mm, guint dd);
81 static guint  day_of_week(guint year, guint mm, guint dd);
82 static glong  dates_difference(guint year1, guint mm1, guint dd1,
83                                guint year2, guint mm2, guint dd2);
84 static guint  weeks_in_year(guint year);
85
86 static gboolean 
87 leap (guint year)
88 {
89   return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
90 }
91
92 static guint 
93 day_of_week (guint year, guint mm, guint dd)
94 {
95   glong  days;
96   
97   days = calc_days(year, mm, dd);
98   if (days > 0L)
99     {
100       days--;
101       days %= 7L;
102       days++;
103     }
104   return( (guint) days );
105 }
106
107 static guint weeks_in_year(guint year)
108 {
109   return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
110 }
111
112 static gboolean 
113 check_date(guint year, guint mm, guint dd)
114 {
115   if (year < 1) return FALSE;
116   if ((mm < 1) || (mm > 12)) return FALSE;
117   if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
118   return TRUE;
119 }
120
121 static guint 
122 week_number(guint year, guint mm, guint dd)
123 {
124   guint first;
125   
126   first = day_of_week(year,1,1) - 1;
127   return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
128           (first < 4) );
129 }
130
131 static glong 
132 year_to_days(guint year)
133 {
134   return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
135 }
136
137
138 static glong 
139 calc_days(guint year, guint mm, guint dd)
140 {
141   gboolean lp;
142   
143   if (year < 1) return(0L);
144   if ((mm < 1) || (mm > 12)) return(0L);
145   if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
146   return( year_to_days(--year) + days_in_months[lp][mm] + dd );
147 }
148
149 static gboolean 
150 week_of_year(guint *week, guint *year, guint mm, guint dd)
151 {
152   if (check_date(*year,mm,dd))
153     {
154       *week = week_number(*year,mm,dd);
155       if (*week == 0) 
156         *week = weeks_in_year(--(*year));
157       else if (*week > weeks_in_year(*year))
158         {
159           *week = 1;
160           (*year)++;
161         }
162       return TRUE;
163     }
164   return FALSE;
165 }
166
167 static glong 
168 dates_difference(guint year1, guint mm1, guint dd1,
169                  guint year2, guint mm2, guint dd2)
170 {
171   return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
172 }
173
174 /*** END OF lib_date routines ********************************************/
175
176 /* Spacing around day/week headers and main area, inside those windows */
177 #define CALENDAR_MARGIN          0
178 /* Spacing around day/week headers and main area, outside those windows */
179 #define INNER_BORDER             4
180 /* Separation between day headers and main area */
181 #define CALENDAR_YSEP            4
182 /* Separation between week headers and main area */
183 #define CALENDAR_XSEP            4
184
185 #define DAY_XSEP                 0 /* not really good for small calendar */
186 #define DAY_YSEP                 0 /* not really good for small calendar */
187
188 #define SCROLL_DELAY_FACTOR      5
189
190 /* Color usage */
191 #define HEADER_FG_COLOR(widget)          (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
192 #define HEADER_BG_COLOR(widget)          (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
193 #define SELECTED_BG_COLOR(widget)        (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
194 #define SELECTED_FG_COLOR(widget)        (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
195 #define NORMAL_DAY_COLOR(widget)         (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
196 #define PREV_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
197 #define NEXT_MONTH_COLOR(widget)         (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
198 #define MARKED_COLOR(widget)             (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
199 #define BACKGROUND_COLOR(widget)         (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
200 #define HIGHLIGHT_BACK_COLOR(widget)     (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
201
202 enum {
203   ARROW_YEAR_LEFT,
204   ARROW_YEAR_RIGHT,
205   ARROW_MONTH_LEFT,
206   ARROW_MONTH_RIGHT
207 };
208
209 enum {
210   MONTH_PREV,
211   MONTH_CURRENT,
212   MONTH_NEXT
213 };
214
215 enum {
216   MONTH_CHANGED_SIGNAL,
217   DAY_SELECTED_SIGNAL,
218   DAY_SELECTED_DOUBLE_CLICK_SIGNAL,
219   PREV_MONTH_SIGNAL,
220   NEXT_MONTH_SIGNAL,
221   PREV_YEAR_SIGNAL,
222   NEXT_YEAR_SIGNAL,
223   LAST_SIGNAL
224 };
225
226 enum
227 {
228   PROP_0,
229   PROP_YEAR,
230   PROP_MONTH,
231   PROP_DAY,
232   PROP_SHOW_HEADING,
233   PROP_SHOW_DAY_NAMES,
234   PROP_NO_MONTH_CHANGE,
235   PROP_SHOW_WEEK_NUMBERS,
236   PROP_DETAIL_WIDTH_CHARS,
237   PROP_DETAIL_HEIGHT_ROWS,
238   PROP_LAST
239 };
240
241 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
242
243 struct _GtkCalendarPrivate
244 {
245   GdkWindow *header_win;
246   GdkWindow *day_name_win;
247   GdkWindow *main_win;
248   GdkWindow *week_win;
249   GdkWindow *arrow_win[4];
250
251   guint header_h;
252   guint day_name_h;
253   guint main_h;
254
255   guint      arrow_state[4];
256   guint      arrow_width;
257   guint      max_month_width;
258   guint      max_year_width;
259   guint      max_detail_height;
260   
261   guint day_width;
262   guint week_width;
263
264   guint min_day_width;
265   guint max_day_char_width;
266   guint max_day_char_ascent;
267   guint max_day_char_descent;
268   guint max_label_char_ascent;
269   guint max_label_char_descent;
270   guint max_week_char_width;
271   
272   /* flags */
273   guint year_before : 1;
274
275   guint need_timer  : 1;
276
277   guint in_drag : 1;
278   guint drag_highlight : 1;
279
280   guint32 timer;
281   gint click_child;
282
283   gint week_start;
284
285   gint drag_start_x;
286   gint drag_start_y;
287
288   /* Optional callback, used to display extra information for each day. */
289   GtkCalendarDetailFunc detail_func;
290   gpointer              detail_func_user_data;
291   GDestroyNotify        detail_func_destroy;
292
293   /* Size requistion for details provided by the hook. */
294   gint detail_height_rows;
295   gint detail_width_chars;
296   gint detail_overflow[6];
297 };
298
299 #define GTK_CALENDAR_GET_PRIVATE(widget)  (GTK_CALENDAR (widget)->priv)
300
301 static void gtk_calendar_finalize     (GObject      *calendar);
302 static void gtk_calendar_destroy      (GtkObject    *calendar);
303 static void gtk_calendar_set_property (GObject      *object,
304                                        guint         prop_id,
305                                        const GValue *value,
306                                        GParamSpec   *pspec);
307 static void gtk_calendar_get_property (GObject      *object,
308                                        guint         prop_id,
309                                        GValue       *value,
310                                        GParamSpec   *pspec);
311
312 static void     gtk_calendar_realize        (GtkWidget        *widget);
313 static void     gtk_calendar_unrealize      (GtkWidget        *widget);
314 static void     gtk_calendar_size_request   (GtkWidget        *widget,
315                                              GtkRequisition   *requisition);
316 static void     gtk_calendar_size_allocate  (GtkWidget        *widget,
317                                              GtkAllocation    *allocation);
318 static gboolean gtk_calendar_expose         (GtkWidget        *widget,
319                                              GdkEventExpose   *event);
320 static gboolean gtk_calendar_button_press   (GtkWidget        *widget,
321                                              GdkEventButton   *event);
322 static gboolean gtk_calendar_button_release (GtkWidget        *widget,
323                                              GdkEventButton   *event);
324 static gboolean gtk_calendar_motion_notify  (GtkWidget        *widget,
325                                              GdkEventMotion   *event);
326 static gboolean gtk_calendar_enter_notify   (GtkWidget        *widget,
327                                              GdkEventCrossing *event);
328 static gboolean gtk_calendar_leave_notify   (GtkWidget        *widget,
329                                              GdkEventCrossing *event);
330 static gboolean gtk_calendar_scroll         (GtkWidget        *widget,
331                                              GdkEventScroll   *event);
332 static gboolean gtk_calendar_key_press      (GtkWidget        *widget,
333                                              GdkEventKey      *event);
334 static gboolean gtk_calendar_focus_out      (GtkWidget        *widget,
335                                              GdkEventFocus    *event);
336 static void     gtk_calendar_grab_notify    (GtkWidget        *widget,
337                                              gboolean          was_grabbed);
338 static void     gtk_calendar_state_changed  (GtkWidget        *widget,
339                                              GtkStateType      previous_state);
340 static void     gtk_calendar_style_set      (GtkWidget        *widget,
341                                              GtkStyle         *previous_style);
342 static gboolean gtk_calendar_query_tooltip  (GtkWidget        *widget,
343                                              gint              x,
344                                              gint              y,
345                                              gboolean          keyboard_mode,
346                                              GtkTooltip       *tooltip);
347
348 static void     gtk_calendar_drag_data_get      (GtkWidget        *widget,
349                                                  GdkDragContext   *context,
350                                                  GtkSelectionData *selection_data,
351                                                  guint             info,
352                                                  guint             time);
353 static void     gtk_calendar_drag_data_received (GtkWidget        *widget,
354                                                  GdkDragContext   *context,
355                                                  gint              x,
356                                                  gint              y,
357                                                  GtkSelectionData *selection_data,
358                                                  guint             info,
359                                                  guint             time);
360 static gboolean gtk_calendar_drag_motion        (GtkWidget        *widget,
361                                                  GdkDragContext   *context,
362                                                  gint              x,
363                                                  gint              y,
364                                                  guint             time);
365 static void     gtk_calendar_drag_leave         (GtkWidget        *widget,
366                                                  GdkDragContext   *context,
367                                                  guint             time);
368 static gboolean gtk_calendar_drag_drop          (GtkWidget        *widget,
369                                                  GdkDragContext   *context,
370                                                  gint              x,
371                                                  gint              y,
372                                                  guint             time);
373
374 static void calendar_start_spinning (GtkCalendar *calendar,
375                                      gint         click_child);
376 static void calendar_stop_spinning  (GtkCalendar *calendar);
377
378 static void calendar_invalidate_day     (GtkCalendar *widget,
379                                          gint       row,
380                                          gint       col);
381 static void calendar_invalidate_day_num (GtkCalendar *widget,
382                                          gint       day);
383 static void calendar_invalidate_arrow   (GtkCalendar *widget,
384                                          guint      arrow);
385
386 static void calendar_compute_days      (GtkCalendar *calendar);
387      
388 static char    *default_abbreviated_dayname[7];
389 static char    *default_monthname[12];
390
391 G_DEFINE_TYPE (GtkCalendar, gtk_calendar, GTK_TYPE_WIDGET)
392
393 static void
394 gtk_calendar_class_init (GtkCalendarClass *class)
395 {
396   GObjectClass   *gobject_class;
397   GtkObjectClass   *object_class;
398   GtkWidgetClass *widget_class;
399
400   gobject_class = (GObjectClass*)  class;
401   object_class = (GtkObjectClass*)  class;
402   widget_class = (GtkWidgetClass*) class;
403   
404   gobject_class->set_property = gtk_calendar_set_property;
405   gobject_class->get_property = gtk_calendar_get_property;
406   gobject_class->finalize = gtk_calendar_finalize;
407
408   object_class->destroy = gtk_calendar_destroy;
409
410   widget_class->realize = gtk_calendar_realize;
411   widget_class->unrealize = gtk_calendar_unrealize;
412   widget_class->expose_event = gtk_calendar_expose;
413   widget_class->size_request = gtk_calendar_size_request;
414   widget_class->size_allocate = gtk_calendar_size_allocate;
415   widget_class->button_press_event = gtk_calendar_button_press;
416   widget_class->button_release_event = gtk_calendar_button_release;
417   widget_class->motion_notify_event = gtk_calendar_motion_notify;
418   widget_class->enter_notify_event = gtk_calendar_enter_notify;
419   widget_class->leave_notify_event = gtk_calendar_leave_notify;
420   widget_class->key_press_event = gtk_calendar_key_press;
421   widget_class->scroll_event = gtk_calendar_scroll;
422   widget_class->style_set = gtk_calendar_style_set;
423   widget_class->state_changed = gtk_calendar_state_changed;
424   widget_class->grab_notify = gtk_calendar_grab_notify;
425   widget_class->focus_out_event = gtk_calendar_focus_out;
426   widget_class->query_tooltip = gtk_calendar_query_tooltip;
427
428   widget_class->drag_data_get = gtk_calendar_drag_data_get;
429   widget_class->drag_motion = gtk_calendar_drag_motion;
430   widget_class->drag_leave = gtk_calendar_drag_leave;
431   widget_class->drag_drop = gtk_calendar_drag_drop;
432   widget_class->drag_data_received = gtk_calendar_drag_data_received;
433   
434   g_object_class_install_property (gobject_class,
435                                    PROP_YEAR,
436                                    g_param_spec_int ("year",
437                                                      P_("Year"),
438                                                      P_("The selected year"),
439                                                      0, G_MAXINT, 0,
440                                                      GTK_PARAM_READWRITE));
441   g_object_class_install_property (gobject_class,
442                                    PROP_MONTH,
443                                    g_param_spec_int ("month",
444                                                      P_("Month"),
445                                                      P_("The selected month (as a number between 0 and 11)"),
446                                                      0, 11, 0,
447                                                      GTK_PARAM_READWRITE));
448   g_object_class_install_property (gobject_class,
449                                    PROP_DAY,
450                                    g_param_spec_int ("day",
451                                                      P_("Day"),
452                                                      P_("The selected day (as a number between 1 and 31, or 0 to unselect the currently selected day)"),
453                                                      0, 31, 0,
454                                                      GTK_PARAM_READWRITE));
455
456 /**
457  * GtkCalendar:show-heading:
458  *
459  * Determines whether a heading is displayed.
460  *
461  * Since: 2.4
462  */
463   g_object_class_install_property (gobject_class,
464                                    PROP_SHOW_HEADING,
465                                    g_param_spec_boolean ("show-heading",
466                                                          P_("Show Heading"),
467                                                          P_("If TRUE, a heading is displayed"),
468                                                          TRUE,
469                                                          GTK_PARAM_READWRITE));
470
471 /**
472  * GtkCalendar:show-day-names:
473  *
474  * Determines whether day names are displayed.
475  *
476  * Since: 2.4
477  */
478   g_object_class_install_property (gobject_class,
479                                    PROP_SHOW_DAY_NAMES,
480                                    g_param_spec_boolean ("show-day-names",
481                                                          P_("Show Day Names"),
482                                                          P_("If TRUE, day names are displayed"),
483                                                          TRUE,
484                                                          GTK_PARAM_READWRITE));
485 /**
486  * GtkCalendar:no-month-change:
487  *
488  * Determines whether the selected month can be changed.
489  *
490  * Since: 2.4
491  */
492   g_object_class_install_property (gobject_class,
493                                    PROP_NO_MONTH_CHANGE,
494                                    g_param_spec_boolean ("no-month-change",
495                                                          P_("No Month Change"),
496                                                          P_("If TRUE, the selected month cannot be changed"),
497                                                          FALSE,
498                                                          GTK_PARAM_READWRITE));
499
500 /**
501  * GtkCalendar:show-week-numbers:
502  *
503  * Determines whether week numbers are displayed.
504  *
505  * Since: 2.4
506  */
507   g_object_class_install_property (gobject_class,
508                                    PROP_SHOW_WEEK_NUMBERS,
509                                    g_param_spec_boolean ("show-week-numbers",
510                                                          P_("Show Week Numbers"),
511                                                          P_("If TRUE, week numbers are displayed"),
512                                                          FALSE,
513                                                          GTK_PARAM_READWRITE));
514
515 /**
516  * GtkCalendar:detail-width-chars:
517  *
518  * Width of a detail cell, in characters.
519  * A value of 0 allows any width. See gtk_calendar_set_detail_func().
520  *
521  * Since: 2.16
522  */
523   g_object_class_install_property (gobject_class,
524                                    PROP_DETAIL_WIDTH_CHARS,
525                                    g_param_spec_int ("detail-width-chars",
526                                                      P_("Details Width"),
527                                                      P_("Details width in characters"),
528                                                      0, 127, 0,
529                                                      GTK_PARAM_READWRITE));
530
531 /**
532  * GtkCalendar:detail-height-rows:
533  *
534  * Height of a detail cell, in rows.
535  * A value of 0 allows any width. See gtk_calendar_set_detail_func().
536  *
537  * Since: 2.16
538  */
539   g_object_class_install_property (gobject_class,
540                                    PROP_DETAIL_HEIGHT_ROWS,
541                                    g_param_spec_int ("detail-height-rows",
542                                                      P_("Details Height"),
543                                                      P_("Details height in rows"),
544                                                      0, 127, 0,
545                                                      GTK_PARAM_READWRITE));
546
547   gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
548     g_signal_new (I_("month_changed"),
549                   G_OBJECT_CLASS_TYPE (gobject_class),
550                   G_SIGNAL_RUN_FIRST,
551                   G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
552                   NULL, NULL,
553                   _gtk_marshal_VOID__VOID,
554                   G_TYPE_NONE, 0);
555   gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
556     g_signal_new (I_("day_selected"),
557                   G_OBJECT_CLASS_TYPE (gobject_class),
558                   G_SIGNAL_RUN_FIRST,
559                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
560                   NULL, NULL,
561                   _gtk_marshal_VOID__VOID,
562                   G_TYPE_NONE, 0);
563   gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
564     g_signal_new (I_("day_selected_double_click"),
565                   G_OBJECT_CLASS_TYPE (gobject_class),
566                   G_SIGNAL_RUN_FIRST,
567                   G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
568                   NULL, NULL,
569                   _gtk_marshal_VOID__VOID,
570                   G_TYPE_NONE, 0);
571   gtk_calendar_signals[PREV_MONTH_SIGNAL] =
572     g_signal_new (I_("prev_month"),
573                   G_OBJECT_CLASS_TYPE (gobject_class),
574                   G_SIGNAL_RUN_FIRST,
575                   G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
576                   NULL, NULL,
577                   _gtk_marshal_VOID__VOID,
578                   G_TYPE_NONE, 0);
579   gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
580     g_signal_new (I_("next_month"),
581                   G_OBJECT_CLASS_TYPE (gobject_class),
582                   G_SIGNAL_RUN_FIRST,
583                   G_STRUCT_OFFSET (GtkCalendarClass, next_month),
584                   NULL, NULL,
585                   _gtk_marshal_VOID__VOID,
586                   G_TYPE_NONE, 0);
587   gtk_calendar_signals[PREV_YEAR_SIGNAL] =
588     g_signal_new (I_("prev_year"),
589                   G_OBJECT_CLASS_TYPE (gobject_class),
590                   G_SIGNAL_RUN_FIRST,
591                   G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
592                   NULL, NULL,
593                   _gtk_marshal_VOID__VOID,
594                   G_TYPE_NONE, 0);
595   gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
596     g_signal_new (I_("next_year"),
597                   G_OBJECT_CLASS_TYPE (gobject_class),
598                   G_SIGNAL_RUN_FIRST,
599                   G_STRUCT_OFFSET (GtkCalendarClass, next_year),
600                   NULL, NULL,
601                   _gtk_marshal_VOID__VOID,
602                   G_TYPE_NONE, 0);
603   
604   g_type_class_add_private (gobject_class, sizeof (GtkCalendarPrivate));
605 }
606
607 static void
608 gtk_calendar_init (GtkCalendar *calendar)
609 {
610   GtkWidget *widget = GTK_WIDGET (calendar);
611   time_t secs;
612   struct tm *tm;
613   gint i;
614 #ifdef G_OS_WIN32
615   wchar_t wbuffer[100];
616 #else
617   char buffer[255];
618   time_t tmp_time;
619 #endif
620   GtkCalendarPrivate *priv;
621   gchar *year_before;
622 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
623   union { unsigned int word; char *string; } langinfo;
624   gint week_1stday = 0;
625   gint first_weekday = 1;
626   guint week_origin;
627 #else
628   gchar *week_start;
629 #endif
630
631   priv = calendar->priv = G_TYPE_INSTANCE_GET_PRIVATE (calendar,
632                                                        GTK_TYPE_CALENDAR,
633                                                        GtkCalendarPrivate);
634
635   GTK_WIDGET_SET_FLAGS (widget, GTK_CAN_FOCUS);
636   
637   if (!default_abbreviated_dayname[0])
638     for (i=0; i<7; i++)
639       {
640 #ifndef G_OS_WIN32
641         tmp_time= (i+3)*86400;
642         strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
643         default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
644 #else
645         if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
646                              wbuffer, G_N_ELEMENTS (wbuffer)))
647           default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
648         else
649           default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
650 #endif
651       }
652   
653   if (!default_monthname[0])
654     for (i=0; i<12; i++)
655       {
656 #ifndef G_OS_WIN32
657         tmp_time=i*2764800;
658         strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
659         default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
660 #else
661         if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
662                              wbuffer, G_N_ELEMENTS (wbuffer)))
663           default_monthname[i] = g_strdup_printf ("(%d)", i);
664         else
665           default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
666 #endif
667       }
668   
669   /* Set defaults */
670   secs = time (NULL);
671   tm = localtime (&secs);
672   calendar->month = tm->tm_mon;
673   calendar->year  = 1900 + tm->tm_year;
674
675   for (i=0;i<31;i++)
676     calendar->marked_date[i] = FALSE;
677   calendar->num_marked_dates = 0;
678   calendar->selected_day = tm->tm_mday;
679   
680   calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
681                               GTK_CALENDAR_SHOW_DAY_NAMES );
682   
683   calendar->highlight_row = -1;
684   calendar->highlight_col = -1;
685   
686   calendar->focus_row = -1;
687   calendar->focus_col = -1;
688
689   priv->max_year_width = 0;
690   priv->max_month_width = 0;
691   priv->max_day_char_width = 0;
692   priv->max_week_char_width = 0;
693
694   priv->max_day_char_ascent = 0;
695   priv->max_day_char_descent = 0;
696   priv->max_label_char_ascent = 0;
697   priv->max_label_char_descent = 0;
698
699   priv->arrow_width = 10;
700
701   priv->need_timer = 0;
702   priv->timer = 0;
703   priv->click_child = -1;
704
705   priv->in_drag = 0;
706   priv->drag_highlight = 0;
707
708   gtk_drag_dest_set (widget, 0, NULL, 0, GDK_ACTION_COPY);
709   gtk_drag_dest_add_text_targets (widget);
710
711   priv->year_before = 0;
712
713   /* Translate to calendar:YM if you want years to be displayed
714    * before months; otherwise translate to calendar:MY.
715    * Do *not* translate it to anything else, if it
716    * it isn't calendar:YM or calendar:MY it will not work.
717    *
718    * Note that this flipping is in top of the text direction flipping,
719    * so if you have a default text direction of RTL and YM, then
720    * the year will appear on the right.
721    */
722   year_before = _("calendar:MY");
723   if (strcmp (year_before, "calendar:YM") == 0)
724     priv->year_before = 1;
725   else if (strcmp (year_before, "calendar:MY") != 0)
726     g_warning ("Whoever translated calendar:MY did so wrongly.\n");
727
728 #ifdef G_OS_WIN32
729   priv->week_start = 0;
730   week_start = NULL;
731
732   if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
733                       wbuffer, G_N_ELEMENTS (wbuffer)))
734     week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
735       
736   if (week_start != NULL)
737     {
738       priv->week_start = (week_start[0] - '0' + 1) % 7;
739       g_free(week_start);
740     }
741 #else
742 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
743   langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
744   first_weekday = langinfo.string[0];
745   langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
746   week_origin = langinfo.word;
747   if (week_origin == 19971130) /* Sunday */
748     week_1stday = 0;
749   else if (week_origin == 19971201) /* Monday */
750     week_1stday = 1;
751   else
752     g_warning ("Unknown value of _NL_TIME_WEEK_1STDAY.\n");
753
754   priv->week_start = (week_1stday + first_weekday - 1) % 7;
755 #else
756   /* Translate to calendar:week_start:0 if you want Sunday to be the
757    * first day of the week to calendar:week_start:1 if you want Monday
758    * to be the first day of the week, and so on.
759    */  
760   week_start = _("calendar:week_start:0");
761
762   if (strncmp (week_start, "calendar:week_start:", 20) == 0)
763     priv->week_start = *(week_start + 20) - '0';
764   else 
765     priv->week_start = -1;
766   
767   if (priv->week_start < 0 || priv->week_start > 6)
768     {
769       g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
770       priv->week_start = 0;
771     }
772 #endif
773 #endif
774
775   calendar_compute_days (calendar);
776 }
777
778 \f
779 /****************************************
780  *          Utility Functions           *
781  ****************************************/
782
783 static void
784 calendar_queue_refresh (GtkCalendar *calendar)
785 {
786   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
787
788   if (!priv->detail_func ||
789        priv->detail_width_chars ||
790        priv->detail_height_rows)
791     gtk_widget_queue_draw (GTK_WIDGET (calendar));
792   else
793     gtk_widget_queue_resize (GTK_WIDGET (calendar));
794 }
795
796 static void
797 calendar_set_month_next (GtkCalendar *calendar)
798 {
799   gint month_len;
800   
801   g_return_if_fail (GTK_IS_WIDGET (calendar));
802   
803   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
804     return;
805   
806   
807   if (calendar->month == 11)
808     {
809       calendar->month = 0;
810       calendar->year++;
811     } 
812   else 
813     calendar->month++;
814   
815   calendar_compute_days (calendar);
816   g_signal_emit (calendar,
817                  gtk_calendar_signals[NEXT_MONTH_SIGNAL],
818                  0);
819   g_signal_emit (calendar,
820                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
821                  0);
822   
823   month_len = month_length[leap (calendar->year)][calendar->month + 1];
824   
825   if (month_len < calendar->selected_day)
826     {
827       calendar->selected_day = 0;
828       gtk_calendar_select_day (calendar, month_len);
829     }
830   else
831     gtk_calendar_select_day (calendar, calendar->selected_day);
832
833   calendar_queue_refresh (calendar);
834 }
835
836 static void
837 calendar_set_year_prev (GtkCalendar *calendar)
838 {
839   gint month_len;
840   
841   g_return_if_fail (GTK_IS_WIDGET (calendar));
842   
843   calendar->year--;
844   calendar_compute_days (calendar);
845   g_signal_emit (calendar,
846                  gtk_calendar_signals[PREV_YEAR_SIGNAL],
847                  0);
848   g_signal_emit (calendar,
849                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
850                  0);
851   
852   month_len = month_length[leap (calendar->year)][calendar->month + 1];
853   
854   if (month_len < calendar->selected_day)
855     {
856       calendar->selected_day = 0;
857       gtk_calendar_select_day (calendar, month_len);
858     }
859   else
860     gtk_calendar_select_day (calendar, calendar->selected_day);
861   
862   calendar_queue_refresh (calendar);
863 }
864
865 static void
866 calendar_set_year_next (GtkCalendar *calendar)
867 {
868   gint month_len;
869   
870   g_return_if_fail (GTK_IS_WIDGET (calendar));
871   
872   calendar->year++;
873   calendar_compute_days (calendar);
874   g_signal_emit (calendar,
875                  gtk_calendar_signals[NEXT_YEAR_SIGNAL],
876                  0);
877   g_signal_emit (calendar,
878                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
879                  0);
880   
881   month_len = month_length[leap (calendar->year)][calendar->month + 1];
882   
883   if (month_len < calendar->selected_day)
884     {
885       calendar->selected_day = 0;
886       gtk_calendar_select_day (calendar, month_len);
887     }
888   else
889     gtk_calendar_select_day (calendar, calendar->selected_day);
890   
891   calendar_queue_refresh (calendar);
892 }
893
894 static void
895 calendar_compute_days (GtkCalendar *calendar)
896 {
897   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (GTK_WIDGET (calendar));
898   gint month;
899   gint year;
900   gint ndays_in_month;
901   gint ndays_in_prev_month;
902   gint first_day;
903   gint row;
904   gint col;
905   gint day;
906
907   g_return_if_fail (GTK_IS_CALENDAR (calendar));
908
909   year = calendar->year;
910   month = calendar->month + 1;
911   
912   ndays_in_month = month_length[leap (year)][month];
913   
914   first_day = day_of_week (year, month, 1);
915   first_day = (first_day + 7 - priv->week_start) % 7;
916   
917   /* Compute days of previous month */
918   if (month > 1)
919     ndays_in_prev_month = month_length[leap (year)][month-1];
920   else
921     ndays_in_prev_month = month_length[leap (year)][12];
922   day = ndays_in_prev_month - first_day + 1;
923   
924   row = 0;
925   if (first_day > 0)
926     {
927       for (col = 0; col < first_day; col++)
928         {
929           calendar->day[row][col] = day;
930           calendar->day_month[row][col] = MONTH_PREV;
931           day++;
932         }
933     }
934   
935   /* Compute days of current month */
936   col = first_day;
937   for (day = 1; day <= ndays_in_month; day++)
938     {
939       calendar->day[row][col] = day;
940       calendar->day_month[row][col] = MONTH_CURRENT;
941       
942       col++;
943       if (col == 7)
944         {
945           row++;
946           col = 0;
947         }
948     }
949   
950   /* Compute days of next month */
951   day = 1;
952   for (; row <= 5; row++)
953     {
954       for (; col <= 6; col++)
955         {
956           calendar->day[row][col] = day;
957           calendar->day_month[row][col] = MONTH_NEXT;
958           day++;
959         }
960       col = 0;
961     }
962 }
963
964 static void
965 calendar_select_and_focus_day (GtkCalendar *calendar,
966                                guint        day)
967 {
968   gint old_focus_row = calendar->focus_row;
969   gint old_focus_col = calendar->focus_col;
970   gint row;
971   gint col;
972   
973   for (row = 0; row < 6; row ++)
974     for (col = 0; col < 7; col++)
975       {
976         if (calendar->day_month[row][col] == MONTH_CURRENT 
977             && calendar->day[row][col] == day)
978           {
979             calendar->focus_row = row;
980             calendar->focus_col = col;
981           }
982       }
983
984   if (old_focus_row != -1 && old_focus_col != -1)
985     calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
986   
987   gtk_calendar_select_day (calendar, day);
988 }
989
990 \f
991 /****************************************
992  *     Layout computation utilities     *
993  ****************************************/
994
995 static gint
996 calendar_row_height (GtkCalendar *calendar)
997 {
998   return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h - CALENDAR_MARGIN
999           - ((calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1000              ? CALENDAR_YSEP : CALENDAR_MARGIN)) / 6;
1001 }
1002
1003
1004 /* calendar_left_x_for_column: returns the x coordinate
1005  * for the left of the column */
1006 static gint
1007 calendar_left_x_for_column (GtkCalendar *calendar,
1008                             gint         column)
1009 {
1010   gint width;
1011   gint x_left;
1012   
1013   if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
1014     column = 6 - column;
1015
1016   width = GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1017   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1018     x_left = CALENDAR_XSEP + (width + DAY_XSEP) * column;
1019   else
1020     x_left = CALENDAR_MARGIN + (width + DAY_XSEP) * column;
1021   
1022   return x_left;
1023 }
1024
1025 /* column_from_x: returns the column 0-6 that the
1026  * x pixel of the xwindow is in */
1027 static gint
1028 calendar_column_from_x (GtkCalendar *calendar,
1029                         gint         event_x)
1030 {
1031   gint c, column;
1032   gint x_left, x_right;
1033   
1034   column = -1;
1035   
1036   for (c = 0; c < 7; c++)
1037     {
1038       x_left = calendar_left_x_for_column (calendar, c);
1039       x_right = x_left + GTK_CALENDAR_GET_PRIVATE (calendar)->day_width;
1040       
1041       if (event_x >= x_left && event_x < x_right)
1042         {
1043           column = c;
1044           break;
1045         }
1046     }
1047   
1048   return column;
1049 }
1050
1051 /* calendar_top_y_for_row: returns the y coordinate
1052  * for the top of the row */
1053 static gint
1054 calendar_top_y_for_row (GtkCalendar *calendar,
1055                         gint         row)
1056 {
1057   
1058   return (GTK_CALENDAR_GET_PRIVATE (calendar)->main_h 
1059           - (CALENDAR_MARGIN + (6 - row)
1060              * calendar_row_height (calendar)));
1061 }
1062
1063 /* row_from_y: returns the row 0-5 that the
1064  * y pixel of the xwindow is in */
1065 static gint
1066 calendar_row_from_y (GtkCalendar *calendar,
1067                      gint         event_y)
1068 {
1069   gint r, row;
1070   gint height;
1071   gint y_top, y_bottom;
1072   
1073   height = calendar_row_height (calendar);
1074   row = -1;
1075   
1076   for (r = 0; r < 6; r++)
1077     {
1078       y_top = calendar_top_y_for_row (calendar, r);
1079       y_bottom = y_top + height;
1080       
1081       if (event_y >= y_top && event_y < y_bottom)
1082         {
1083           row = r;
1084           break;
1085         }
1086     }
1087   
1088   return row;
1089 }
1090
1091 static void
1092 calendar_arrow_rectangle (GtkCalendar  *calendar,
1093                           guint         arrow,
1094                           GdkRectangle *rect)
1095 {
1096   GtkWidget *widget = GTK_WIDGET (calendar);
1097   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1098   gboolean year_left;
1099
1100   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
1101     year_left = priv->year_before;
1102   else
1103     year_left = !priv->year_before;
1104     
1105   rect->y = 3;
1106   rect->width = priv->arrow_width;
1107   rect->height = priv->header_h - 7;
1108   
1109   switch (arrow)
1110     {
1111     case ARROW_MONTH_LEFT:
1112       if (year_left) 
1113         rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1114                    - (3 + 2*priv->arrow_width 
1115                       + priv->max_month_width));
1116       else
1117         rect->x = 3;
1118       break;
1119     case ARROW_MONTH_RIGHT:
1120       if (year_left) 
1121         rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
1122                    - 3 - priv->arrow_width);
1123       else
1124         rect->x = (priv->arrow_width 
1125                    + priv->max_month_width);
1126       break;
1127     case ARROW_YEAR_LEFT:
1128       if (year_left) 
1129         rect->x = 3;
1130       else
1131         rect->x = (widget->allocation.width - 2 * widget->style->xthickness
1132                    - (3 + 2*priv->arrow_width 
1133                       + priv->max_year_width));
1134       break;
1135     case ARROW_YEAR_RIGHT:
1136       if (year_left) 
1137         rect->x = (priv->arrow_width 
1138                    + priv->max_year_width);
1139       else
1140         rect->x = (widget->allocation.width - 2 * widget->style->xthickness 
1141                    - 3 - priv->arrow_width);
1142       break;
1143     }
1144 }
1145
1146 static void
1147 calendar_day_rectangle (GtkCalendar  *calendar,
1148                         gint          row,
1149                         gint          col,
1150                         GdkRectangle *rect)
1151 {
1152   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1153
1154   rect->x = calendar_left_x_for_column (calendar, col);
1155   rect->y = calendar_top_y_for_row (calendar, row);
1156   rect->height = calendar_row_height (calendar);
1157   rect->width = priv->day_width;
1158 }
1159
1160 static void
1161 calendar_set_month_prev (GtkCalendar *calendar)
1162 {
1163   gint month_len;
1164   
1165   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1166     return;
1167   
1168   if (calendar->month == 0)
1169     {
1170       calendar->month = 11;
1171       calendar->year--;
1172     } 
1173   else 
1174     calendar->month--;
1175   
1176   month_len = month_length[leap (calendar->year)][calendar->month + 1];
1177   
1178   calendar_compute_days (calendar);
1179   
1180   g_signal_emit (calendar,
1181                  gtk_calendar_signals[PREV_MONTH_SIGNAL],
1182                  0);
1183   g_signal_emit (calendar,
1184                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
1185                  0);
1186   
1187   if (month_len < calendar->selected_day)
1188     {
1189       calendar->selected_day = 0;
1190       gtk_calendar_select_day (calendar, month_len);
1191     }
1192   else
1193     {
1194       if (calendar->selected_day < 0)
1195         calendar->selected_day = calendar->selected_day + 1 + month_length[leap (calendar->year)][calendar->month + 1];
1196       gtk_calendar_select_day (calendar, calendar->selected_day);
1197     }
1198
1199   calendar_queue_refresh (calendar);
1200 }
1201
1202 \f
1203 /****************************************
1204  *           Basic object methods       *
1205  ****************************************/
1206
1207 static void
1208 gtk_calendar_finalize (GObject *object)
1209 {
1210   (* G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize) (object);
1211 }
1212
1213 static void
1214 gtk_calendar_destroy (GtkObject *object)
1215 {
1216   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1217
1218   calendar_stop_spinning (GTK_CALENDAR (object));
1219   
1220   /* Call the destroy function for the extra display callback: */
1221   if (priv->detail_func_destroy && priv->detail_func_user_data)
1222     {
1223       priv->detail_func_destroy (priv->detail_func_user_data);
1224       priv->detail_func_user_data = NULL;
1225       priv->detail_func_destroy = NULL;
1226     }
1227
1228   GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
1229 }
1230
1231
1232 static void
1233 calendar_set_display_option (GtkCalendar              *calendar,
1234                              GtkCalendarDisplayOptions flag,
1235                              gboolean                  setting)
1236 {
1237   GtkCalendarDisplayOptions flags;
1238   if (setting) 
1239     flags = calendar->display_flags | flag;
1240   else
1241     flags = calendar->display_flags & ~flag; 
1242   gtk_calendar_display_options (calendar, flags);
1243 }
1244
1245 static gboolean
1246 calendar_get_display_option (GtkCalendar              *calendar,
1247                              GtkCalendarDisplayOptions flag)
1248 {
1249   return (calendar->display_flags & flag) != 0;
1250 }
1251
1252 static void 
1253 gtk_calendar_set_property (GObject      *object,
1254                            guint         prop_id,
1255                            const GValue *value,
1256                            GParamSpec   *pspec)
1257 {
1258   GtkCalendar *calendar;
1259
1260   calendar = GTK_CALENDAR (object);
1261
1262   switch (prop_id) 
1263     {
1264     case PROP_YEAR:
1265       gtk_calendar_select_month (calendar,
1266                                  calendar->month,
1267                                  g_value_get_int (value));
1268       break;
1269     case PROP_MONTH:
1270       gtk_calendar_select_month (calendar,
1271                                  g_value_get_int (value),
1272                                  calendar->year);
1273       break;
1274     case PROP_DAY:
1275       gtk_calendar_select_day (calendar,
1276                                g_value_get_int (value));
1277       break;
1278     case PROP_SHOW_HEADING:
1279       calendar_set_display_option (calendar,
1280                                    GTK_CALENDAR_SHOW_HEADING,
1281                                    g_value_get_boolean (value));
1282       break;
1283     case PROP_SHOW_DAY_NAMES:
1284       calendar_set_display_option (calendar,
1285                                    GTK_CALENDAR_SHOW_DAY_NAMES,
1286                                    g_value_get_boolean (value));
1287       break;
1288     case PROP_NO_MONTH_CHANGE:
1289       calendar_set_display_option (calendar,
1290                                    GTK_CALENDAR_NO_MONTH_CHANGE,
1291                                    g_value_get_boolean (value));
1292       break;
1293     case PROP_SHOW_WEEK_NUMBERS:
1294       calendar_set_display_option (calendar,
1295                                    GTK_CALENDAR_SHOW_WEEK_NUMBERS,
1296                                    g_value_get_boolean (value));
1297       break;
1298     case PROP_DETAIL_WIDTH_CHARS:
1299       gtk_calendar_set_detail_width_chars (calendar,
1300                                            g_value_get_int (value));
1301       break;
1302     case PROP_DETAIL_HEIGHT_ROWS:
1303       gtk_calendar_set_detail_height_rows (calendar,
1304                                            g_value_get_int (value));
1305       break;
1306     default:
1307       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1308       break;
1309     }
1310 }
1311
1312 static void 
1313 gtk_calendar_get_property (GObject      *object,
1314                            guint         prop_id,
1315                            GValue       *value,
1316                            GParamSpec   *pspec)
1317 {
1318   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
1319   GtkCalendar *calendar = GTK_CALENDAR (object);
1320
1321   switch (prop_id) 
1322     {
1323     case PROP_YEAR:
1324       g_value_set_int (value, calendar->year);
1325       break;
1326     case PROP_MONTH:
1327       g_value_set_int (value, calendar->month);
1328       break;
1329     case PROP_DAY:
1330       g_value_set_int (value, calendar->selected_day);
1331       break;
1332     case PROP_SHOW_HEADING:
1333       g_value_set_boolean (value, calendar_get_display_option (calendar,
1334                                                                GTK_CALENDAR_SHOW_HEADING));
1335       break;
1336     case PROP_SHOW_DAY_NAMES:
1337       g_value_set_boolean (value, calendar_get_display_option (calendar,
1338                                                                GTK_CALENDAR_SHOW_DAY_NAMES));
1339       break;
1340     case PROP_NO_MONTH_CHANGE:
1341       g_value_set_boolean (value, calendar_get_display_option (calendar,
1342                                                                GTK_CALENDAR_NO_MONTH_CHANGE));
1343       break;
1344     case PROP_SHOW_WEEK_NUMBERS:
1345       g_value_set_boolean (value, calendar_get_display_option (calendar,
1346                                                                GTK_CALENDAR_SHOW_WEEK_NUMBERS));
1347       break;
1348     case PROP_DETAIL_WIDTH_CHARS:
1349       g_value_set_int (value, priv->detail_width_chars);
1350       break;
1351     case PROP_DETAIL_HEIGHT_ROWS:
1352       g_value_set_int (value, priv->detail_height_rows);
1353       break;
1354     default:
1355       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1356       break;
1357     }
1358 }
1359
1360 \f
1361 /****************************************
1362  *             Realization              *
1363  ****************************************/
1364
1365 static void
1366 calendar_realize_arrows (GtkCalendar *calendar)
1367 {
1368   GtkWidget *widget = GTK_WIDGET (calendar);
1369   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1370   GdkWindowAttr attributes;
1371   gint attributes_mask;
1372   gint i;
1373   
1374   /* Arrow windows ------------------------------------- */
1375   if (! (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1376       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
1377     {
1378       attributes.wclass = GDK_INPUT_OUTPUT;
1379       attributes.window_type = GDK_WINDOW_CHILD;
1380       attributes.visual = gtk_widget_get_visual (widget);
1381       attributes.colormap = gtk_widget_get_colormap (widget);
1382       attributes.event_mask = (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1383                                | GDK_BUTTON_PRESS_MASK  | GDK_BUTTON_RELEASE_MASK
1384                                | GDK_ENTER_NOTIFY_MASK | GDK_LEAVE_NOTIFY_MASK);
1385       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1386       for (i = 0; i < 4; i++)
1387         {
1388           GdkRectangle rect;
1389           calendar_arrow_rectangle (calendar, i, &rect);
1390           
1391           attributes.x = rect.x;
1392           attributes.y = rect.y;
1393           attributes.width = rect.width;
1394           attributes.height = rect.height;
1395           priv->arrow_win[i] = gdk_window_new (priv->header_win,
1396                                                &attributes, 
1397                                                attributes_mask);
1398           if (GTK_WIDGET_IS_SENSITIVE (widget))
1399             priv->arrow_state[i] = GTK_STATE_NORMAL;
1400           else 
1401             priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
1402           gdk_window_set_background (priv->arrow_win[i],
1403                                      HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1404           gdk_window_show (priv->arrow_win[i]);
1405           gdk_window_set_user_data (priv->arrow_win[i], widget);
1406         }
1407     }
1408   else
1409     {
1410       for (i = 0; i < 4; i++)
1411         priv->arrow_win[i] = NULL;
1412     }
1413 }
1414
1415 static void
1416 calendar_realize_header (GtkCalendar *calendar)
1417 {
1418   GtkWidget *widget = GTK_WIDGET (calendar);
1419   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1420   GdkWindowAttr attributes;
1421   gint attributes_mask;
1422   
1423   /* Header window ------------------------------------- */
1424   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1425     {
1426       attributes.wclass = GDK_INPUT_OUTPUT;
1427       attributes.window_type = GDK_WINDOW_CHILD;
1428       attributes.visual = gtk_widget_get_visual (widget);
1429       attributes.colormap = gtk_widget_get_colormap (widget);
1430       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1431       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1432       attributes.x = widget->style->xthickness;
1433       attributes.y = widget->style->ythickness;
1434       attributes.width = widget->allocation.width - 2 * attributes.x;
1435       attributes.height = priv->header_h - 2 * attributes.y;
1436       priv->header_win = gdk_window_new (widget->window,
1437                                          &attributes, attributes_mask);
1438       
1439       gdk_window_set_background (priv->header_win,
1440                                  HEADER_BG_COLOR (GTK_WIDGET (calendar)));
1441       gdk_window_show (priv->header_win);
1442       gdk_window_set_user_data (priv->header_win, widget);
1443       
1444     }
1445   else
1446     {
1447       priv->header_win = NULL;
1448     }
1449   calendar_realize_arrows (calendar);
1450 }
1451
1452 static void
1453 calendar_realize_day_names (GtkCalendar *calendar)
1454 {
1455   GtkWidget *widget = GTK_WIDGET (calendar);
1456   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1457   GdkWindowAttr attributes;
1458   gint attributes_mask;
1459   
1460   /* Day names  window --------------------------------- */
1461   if ( calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1462     {
1463       attributes.wclass = GDK_INPUT_OUTPUT;
1464       attributes.window_type = GDK_WINDOW_CHILD;
1465       attributes.visual = gtk_widget_get_visual (widget);
1466       attributes.colormap = gtk_widget_get_colormap (widget);
1467       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1468       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1469       attributes.x = (widget->style->xthickness + INNER_BORDER);
1470       attributes.y = priv->header_h + (widget->style->ythickness 
1471                                            + INNER_BORDER);
1472       attributes.width = (widget->allocation.width 
1473                           - (widget->style->xthickness + INNER_BORDER) 
1474                           * 2);
1475       attributes.height = priv->day_name_h;
1476       priv->day_name_win = gdk_window_new (widget->window,
1477                                            &attributes, 
1478                                            attributes_mask);
1479       gdk_window_set_background (priv->day_name_win, 
1480                                  BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1481       gdk_window_show (priv->day_name_win);
1482       gdk_window_set_user_data (priv->day_name_win, widget);
1483     }
1484   else
1485     {
1486       priv->day_name_win = NULL;
1487     }
1488 }
1489
1490 static void
1491 calendar_realize_week_numbers (GtkCalendar *calendar)
1492 {
1493   GtkWidget *widget = GTK_WIDGET (calendar);
1494   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1495   GdkWindowAttr attributes;
1496   gint attributes_mask;
1497   
1498   /* Week number window -------------------------------- */
1499   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1500     {
1501       attributes.wclass = GDK_INPUT_OUTPUT;
1502       attributes.window_type = GDK_WINDOW_CHILD;
1503       attributes.visual = gtk_widget_get_visual (widget);
1504       attributes.colormap = gtk_widget_get_colormap (widget);
1505       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
1506       
1507       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1508       attributes.x = widget->style->xthickness + INNER_BORDER;
1509       attributes.y = (priv->header_h + priv->day_name_h 
1510                       + (widget->style->ythickness + INNER_BORDER));
1511       attributes.width = priv->week_width;
1512       attributes.height = priv->main_h;
1513       priv->week_win = gdk_window_new (widget->window,
1514                                        &attributes, attributes_mask);
1515       gdk_window_set_background (priv->week_win,  
1516                                  BACKGROUND_COLOR (GTK_WIDGET (calendar)));
1517       gdk_window_show (priv->week_win);
1518       gdk_window_set_user_data (priv->week_win, widget);
1519     } 
1520   else
1521     {
1522       priv->week_win = NULL;
1523     }
1524 }
1525
1526 static void
1527 gtk_calendar_realize (GtkWidget *widget)
1528 {
1529   GtkCalendar *calendar = GTK_CALENDAR (widget);
1530   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1531   GdkWindowAttr attributes;
1532   gint attributes_mask;
1533
1534   GTK_WIDGET_SET_FLAGS (widget, GTK_REALIZED);
1535   
1536   attributes.x = widget->allocation.x;
1537   attributes.y = widget->allocation.y;
1538   attributes.width = widget->allocation.width;
1539   attributes.height = widget->allocation.height;
1540   attributes.wclass = GDK_INPUT_OUTPUT;
1541   attributes.window_type = GDK_WINDOW_CHILD;
1542   attributes.event_mask =  (gtk_widget_get_events (widget) 
1543                             | GDK_EXPOSURE_MASK |GDK_KEY_PRESS_MASK | GDK_SCROLL_MASK);
1544   attributes.visual = gtk_widget_get_visual (widget);
1545   attributes.colormap = gtk_widget_get_colormap (widget);
1546   
1547   attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
1548   widget->window = gdk_window_new (widget->parent->window,
1549                                    &attributes, attributes_mask);
1550   
1551   widget->style = gtk_style_attach (widget->style, widget->window);
1552   
1553   /* Header window ------------------------------------- */
1554   calendar_realize_header (calendar);
1555   /* Day names  window --------------------------------- */
1556   calendar_realize_day_names (calendar);
1557   /* Week number window -------------------------------- */
1558   calendar_realize_week_numbers (calendar);
1559   /* Main Window --------------------------------------  */
1560   attributes.event_mask =  (gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK
1561                             | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
1562                             | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
1563   
1564   attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
1565   attributes.y = (priv->header_h + priv->day_name_h 
1566                   + (widget->style->ythickness + INNER_BORDER));
1567   attributes.width = (widget->allocation.width - attributes.x 
1568                       - (widget->style->xthickness + INNER_BORDER));
1569   attributes.height = priv->main_h;
1570   priv->main_win = gdk_window_new (widget->window,
1571                                    &attributes, attributes_mask);
1572   gdk_window_set_background (priv->main_win, 
1573                              BACKGROUND_COLOR ( GTK_WIDGET ( calendar)));
1574   gdk_window_show (priv->main_win);
1575   gdk_window_set_user_data (priv->main_win, widget);
1576   gdk_window_set_background (widget->window, BACKGROUND_COLOR (widget));
1577   gdk_window_show (widget->window);
1578   gdk_window_set_user_data (widget->window, widget);
1579 }
1580
1581 static void
1582 gtk_calendar_unrealize (GtkWidget *widget)
1583 {
1584   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1585   gint i;
1586   
1587   if (priv->header_win)
1588     {
1589       for (i = 0; i < 4; i++)
1590         {
1591           if (priv->arrow_win[i])
1592             {
1593               gdk_window_set_user_data (priv->arrow_win[i], NULL);
1594               gdk_window_destroy (priv->arrow_win[i]);
1595               priv->arrow_win[i] = NULL;
1596             }
1597         }
1598       gdk_window_set_user_data (priv->header_win, NULL);
1599       gdk_window_destroy (priv->header_win);
1600       priv->header_win = NULL;
1601     }
1602   
1603   if (priv->week_win)
1604     {
1605       gdk_window_set_user_data (priv->week_win, NULL);
1606       gdk_window_destroy (priv->week_win);
1607       priv->week_win = NULL;      
1608     }
1609   
1610   if (priv->main_win)
1611     {
1612       gdk_window_set_user_data (priv->main_win, NULL);
1613       gdk_window_destroy (priv->main_win);
1614       priv->main_win = NULL;      
1615     }
1616   if (priv->day_name_win)
1617     {
1618       gdk_window_set_user_data (priv->day_name_win, NULL);
1619       gdk_window_destroy (priv->day_name_win);
1620       priv->day_name_win = NULL;      
1621     }
1622   
1623   if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize)
1624     (* GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize) (widget);
1625 }
1626
1627 static G_CONST_RETURN gchar*
1628 gtk_calendar_get_detail (GtkCalendar *calendar,
1629                          gint         row,
1630                          gint         column)
1631 {
1632   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1633   gint year, month;
1634
1635   year = calendar->year;
1636   month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1637
1638   if (month < 0)
1639     {
1640       month += 12;
1641       year -= 1;
1642     }
1643   else if (month > 11)
1644     {
1645       month -= 12;
1646       year += 1;
1647     }
1648
1649   return priv->detail_func (calendar,
1650                             year, month,
1651                             calendar->day[row][column],
1652                             priv->detail_func_user_data);
1653 }
1654
1655 static gboolean
1656 gtk_calendar_query_tooltip (GtkWidget  *widget,
1657                             gint        x,
1658                             gint        y,
1659                             gboolean    keyboard_mode,
1660                             GtkTooltip *tooltip)
1661 {
1662   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1663   GtkCalendar *calendar = GTK_CALENDAR (widget);
1664   const gchar *detail = NULL;
1665   GdkRectangle day_rect;
1666
1667   if (priv->main_win)
1668     {
1669       gint x0, y0, row, col;
1670
1671       gdk_window_get_position (priv->main_win, &x0, &y0);
1672       col = calendar_column_from_x (calendar, x - x0);
1673       row = calendar_row_from_y (calendar, y - y0);
1674
1675       if (priv->detail_overflow[row] & (1 << col))
1676         {
1677           detail = gtk_calendar_get_detail (calendar, row, col);
1678           calendar_day_rectangle (calendar, row, col, &day_rect);
1679
1680           day_rect.x += x0;
1681           day_rect.y += y0;
1682         }
1683     }
1684
1685   if (detail)
1686     {
1687       gtk_tooltip_set_tip_area (tooltip, &day_rect);
1688       gtk_tooltip_set_markup (tooltip, detail);
1689
1690       return TRUE;
1691     }
1692
1693   if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1694     return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1695
1696   return FALSE;
1697 }
1698
1699 \f
1700 /****************************************
1701  *       Size Request and Allocate      *
1702  ****************************************/
1703
1704 static void
1705 gtk_calendar_size_request (GtkWidget      *widget,
1706                            GtkRequisition *requisition)
1707 {
1708   GtkCalendar *calendar = GTK_CALENDAR (widget);
1709   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1710   PangoLayout *layout;
1711   PangoRectangle logical_rect;
1712
1713   gint height;
1714   gint i, r, c;
1715   gint calendar_margin = CALENDAR_MARGIN;
1716   gint header_width, main_width;
1717   gint max_header_height = 0;
1718   gint focus_width;
1719   gint focus_padding;
1720   
1721   gtk_widget_style_get (GTK_WIDGET (widget),
1722                         "focus-line-width", &focus_width,
1723                         "focus-padding", &focus_padding,
1724                         NULL);
1725
1726   layout = gtk_widget_create_pango_layout (widget, NULL);
1727   
1728   /*
1729    * Calculate the requisition  width for the widget.
1730    */
1731   
1732   /* Header width */
1733   
1734   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1735     {
1736       priv->max_month_width = 0;
1737       for (i = 0; i < 12; i++)
1738         {
1739           pango_layout_set_text (layout, default_monthname[i], -1);
1740           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1741           priv->max_month_width = MAX (priv->max_month_width,
1742                                                logical_rect.width + 8);
1743           max_header_height = MAX (max_header_height, logical_rect.height); 
1744         }
1745
1746       priv->max_year_width = 0;
1747       /* Translators:  This is a text measurement template.
1748        * Translate it to the widest year text. 
1749        * 
1750        * Don't include the prefix "year measurement template|" 
1751        * in the translation.
1752        *
1753        * If you don't understand this, leave it as "2000"
1754        */
1755       pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);   
1756       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1757       priv->max_year_width = MAX (priv->max_year_width,
1758                                   logical_rect.width + 8);
1759       max_header_height = MAX (max_header_height, logical_rect.height); 
1760     } 
1761   else 
1762     {
1763       priv->max_month_width = 0;
1764       priv->max_year_width = 0;
1765     }
1766   
1767   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1768     header_width = (priv->max_month_width 
1769                     + priv->max_year_width
1770                     + 3 * 3);
1771   else
1772     header_width = (priv->max_month_width 
1773                     + priv->max_year_width
1774                     + 4 * priv->arrow_width + 3 * 3);
1775
1776   /* Mainwindow labels width */
1777   
1778   priv->max_day_char_width = 0;
1779   priv->max_day_char_ascent = 0;
1780   priv->max_day_char_descent = 0;
1781   priv->min_day_width = 0;
1782
1783   for (i = 0; i < 9; i++)
1784     {
1785       gchar buffer[32];
1786       g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1787       pango_layout_set_text (layout, buffer, -1);         
1788       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1789       priv->min_day_width = MAX (priv->min_day_width,
1790                                          logical_rect.width);
1791
1792       priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1793                                                PANGO_ASCENT (logical_rect));
1794       priv->max_day_char_descent = MAX (priv->max_day_char_descent, 
1795                                                 PANGO_DESCENT (logical_rect));
1796     }
1797   
1798   priv->max_label_char_ascent = 0;
1799   priv->max_label_char_descent = 0;
1800   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1801     for (i = 0; i < 7; i++)
1802       {
1803         pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1804         pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1805
1806         priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1807         priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1808                                                    PANGO_ASCENT (logical_rect));
1809         priv->max_label_char_descent = MAX (priv->max_label_char_descent, 
1810                                                     PANGO_DESCENT (logical_rect));
1811       }
1812   
1813   priv->max_week_char_width = 0;
1814   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1815     for (i = 0; i < 9; i++)
1816       {
1817         gchar buffer[32];
1818         g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1819         pango_layout_set_text (layout, buffer, -1);       
1820         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1821         priv->max_week_char_width = MAX (priv->max_week_char_width,
1822                                            logical_rect.width / 2);
1823       }
1824   
1825   /* Calculate detail extents. Do this as late as possible since
1826    * pango_layout_set_markup is called which alters font settings. */
1827   priv->max_detail_height = 0;
1828
1829   if (priv->detail_func)
1830     {
1831       gchar *markup, *tail;
1832
1833       if (priv->detail_width_chars || priv->detail_height_rows)
1834         {
1835           gint rows = MAX (1, priv->detail_height_rows) - 1;
1836           gsize len = priv->detail_width_chars + rows + 16;
1837
1838           markup = tail = g_alloca (len);
1839
1840           memcpy (tail,     "<small>", 7);
1841           tail += 7;
1842
1843           memset (tail, 'm', priv->detail_width_chars);
1844           tail += priv->detail_width_chars;
1845
1846           memset (tail, '\n', rows);
1847           tail += rows;
1848
1849           memcpy (tail,     "</small>", 9);
1850           tail += 9;
1851
1852           g_assert (len == (tail - markup));
1853
1854           pango_layout_set_markup (layout, markup, -1);
1855           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1856
1857
1858           if (priv->detail_width_chars)
1859             priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1860           if (priv->detail_height_rows)
1861             priv->max_detail_height = MAX (priv->max_detail_height, logical_rect.height);
1862         }
1863
1864       if (!priv->detail_width_chars || !priv->detail_height_rows)
1865         for (r = 0; r < 6; r++)
1866           for (c = 0; c < 7; c++)
1867             {
1868               const gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1869
1870               if (detail)
1871                 {
1872                   markup = g_strconcat ("<small>", detail, "</small>", NULL);
1873                   pango_layout_set_markup (layout, markup, -1);
1874
1875                   if (priv->detail_width_chars)
1876                     {
1877                       pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1878                       pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1879                     }
1880
1881                   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1882
1883                   if (!priv->detail_width_chars)
1884                     priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1885                   if (!priv->detail_height_rows)
1886                     priv->max_detail_height = MAX (priv->max_detail_height, logical_rect.height);
1887
1888                   g_free (markup);
1889                 }
1890             }
1891     }
1892
1893   /* We add one to max_day_char_width to be able to make the marked day "bold" */
1894   priv->max_day_char_width = priv->min_day_width / 2 + 1;
1895
1896   main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1897                 + (priv->max_week_char_width
1898                    ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1899                    : 0));
1900   
1901   
1902   requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1903   
1904   /*
1905    * Calculate the requisition height for the widget.
1906    */
1907   
1908   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1909     {
1910       priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1911     }
1912   else
1913     {
1914       priv->header_h = 0;
1915     }
1916   
1917   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1918     {
1919       priv->day_name_h = (priv->max_label_char_ascent
1920                                   + priv->max_label_char_descent
1921                                   + 2 * (focus_padding + focus_width) + calendar_margin);
1922       calendar_margin = CALENDAR_YSEP;
1923     } 
1924   else
1925     {
1926       priv->day_name_h = 0;
1927     }
1928
1929   priv->main_h = (CALENDAR_MARGIN + calendar_margin
1930                           + 6 * (priv->max_day_char_ascent
1931                                  + priv->max_day_char_descent 
1932                                  + priv->max_detail_height
1933                                  + 2 * (focus_padding + focus_width))
1934                           + DAY_YSEP * 5);
1935   
1936   height = (priv->header_h + priv->day_name_h 
1937             + priv->main_h);
1938   
1939   requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1940
1941   g_object_unref (layout);
1942 }
1943
1944 static void
1945 gtk_calendar_size_allocate (GtkWidget     *widget,
1946                             GtkAllocation *allocation)
1947 {
1948   GtkCalendar *calendar = GTK_CALENDAR (widget);
1949   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1950   gint xthickness = widget->style->xthickness;
1951   gint ythickness = widget->style->xthickness;
1952   guint i;
1953   
1954   widget->allocation = *allocation;
1955     
1956   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1957     {
1958       priv->day_width = (priv->min_day_width
1959                          * ((allocation->width - (xthickness + INNER_BORDER) * 2
1960                              - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
1961                          / (7 * priv->min_day_width + priv->max_week_char_width * 2));
1962       priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
1963                            - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
1964                           - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
1965     } 
1966   else 
1967     {
1968       priv->day_width = (allocation->width
1969                          - (xthickness + INNER_BORDER) * 2
1970                          - (CALENDAR_MARGIN * 2)
1971                          - (DAY_XSEP * 6))/7;
1972       priv->week_width = 0;
1973     }
1974   
1975   if (GTK_WIDGET_REALIZED (widget))
1976     {
1977       gdk_window_move_resize (widget->window,
1978                               allocation->x, allocation->y,
1979                               allocation->width, allocation->height);
1980       if (priv->header_win)
1981         gdk_window_move_resize (priv->header_win,
1982                                 xthickness, ythickness,
1983                                 allocation->width - 2 * xthickness, priv->header_h);
1984
1985       for (i = 0 ; i < 4 ; i++)
1986         {
1987           if (priv->arrow_win[i])
1988             {
1989               GdkRectangle rect;
1990               calendar_arrow_rectangle (calendar, i, &rect);
1991           
1992               gdk_window_move_resize (priv->arrow_win[i],
1993                                       rect.x, rect.y, rect.width, rect.height);
1994             }
1995         }
1996       
1997       if (priv->day_name_win)
1998         gdk_window_move_resize (priv->day_name_win,
1999                                 xthickness + INNER_BORDER,
2000                                 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2001                                 allocation->width - (xthickness + INNER_BORDER) * 2,
2002                                 priv->day_name_h);
2003       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
2004         {
2005           if (priv->week_win)
2006             gdk_window_move_resize (priv->week_win,
2007                                     (xthickness + INNER_BORDER),
2008                                     priv->header_h + priv->day_name_h
2009                                     + (widget->style->ythickness + INNER_BORDER),
2010                                     priv->week_width,
2011                                     priv->main_h);
2012           gdk_window_move_resize (priv->main_win,
2013                                   priv->week_width + (xthickness + INNER_BORDER),
2014                                   priv->header_h + priv->day_name_h
2015                                   + (widget->style->ythickness + INNER_BORDER),
2016                                   allocation->width 
2017                                   - priv->week_width 
2018                                   - (xthickness + INNER_BORDER) * 2,
2019                                   priv->main_h);
2020         }
2021       else 
2022         {
2023           gdk_window_move_resize (priv->main_win,
2024                                   (xthickness + INNER_BORDER),
2025                                   priv->header_h + priv->day_name_h
2026                                   + (widget->style->ythickness + INNER_BORDER),
2027                                   allocation->width 
2028                                   - priv->week_width 
2029                                   - (xthickness + INNER_BORDER) * 2,
2030                                   priv->main_h);
2031           if (priv->week_win)
2032             gdk_window_move_resize (priv->week_win,
2033                                     allocation->width 
2034                                     - priv->week_width 
2035                                     - (xthickness + INNER_BORDER),
2036                                     priv->header_h + priv->day_name_h
2037                                     + (widget->style->ythickness + INNER_BORDER),
2038                                     priv->week_width,
2039                                     priv->main_h);
2040         }
2041     }
2042 }
2043
2044 \f
2045 /****************************************
2046  *              Repainting              *
2047  ****************************************/
2048
2049 static void
2050 calendar_paint_header (GtkCalendar *calendar)
2051 {
2052   GtkWidget *widget = GTK_WIDGET (calendar);
2053   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2054   cairo_t *cr;
2055   char buffer[255];
2056   int x, y;
2057   gint header_width;
2058   gint max_month_width;
2059   gint max_year_width;
2060   PangoLayout *layout;
2061   PangoRectangle logical_rect;
2062   gboolean year_left;
2063   time_t tmp_time;
2064   struct tm *tm;
2065   gchar *str;
2066
2067   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
2068     year_left = priv->year_before;
2069   else
2070     year_left = !priv->year_before;
2071
2072   cr = gdk_cairo_create (priv->header_win);
2073   
2074   header_width = widget->allocation.width - 2 * widget->style->xthickness;
2075   
2076   max_month_width = priv->max_month_width;
2077   max_year_width = priv->max_year_width;
2078   
2079   gtk_paint_shadow (widget->style, priv->header_win,
2080                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2081                     NULL, widget, "calendar",
2082                     0, 0, header_width, priv->header_h);
2083
2084   tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
2085   tm = gmtime (&tmp_time);
2086   tm->tm_year = calendar->year - 1900;
2087
2088   /* Translators: This dictates how the year is displayed in
2089    * gtkcalendar widget.  See strftime() manual for the format.
2090    * Use only ASCII in the translation.
2091    *
2092    * Also look for the msgid "year measurement template|2000".  
2093    * Translate that entry to a year with the widest output of this
2094    * msgid. 
2095    * 
2096    * Don't include the prefix "calendar year format|" in the 
2097    * translation. "%Y" is appropriate for most locales.
2098    */
2099   strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2100   str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2101   layout = gtk_widget_create_pango_layout (widget, str);
2102   g_free (str);
2103   
2104   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2105   
2106   /* Draw title */
2107   y = (priv->header_h - logical_rect.height) / 2;
2108   
2109   /* Draw year and its arrows */
2110   
2111   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2112     if (year_left)
2113       x = 3 + (max_year_width - logical_rect.width)/2;
2114     else
2115       x = header_width - (3 + max_year_width
2116                           - (max_year_width - logical_rect.width)/2);
2117   else
2118     if (year_left)
2119       x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2120     else
2121       x = header_width - (3 + priv->arrow_width + max_year_width
2122                           - (max_year_width - logical_rect.width)/2);
2123   
2124
2125   gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2126   cairo_move_to (cr, x, y);
2127   pango_cairo_show_layout (cr, layout);
2128   
2129   /* Draw month */
2130   g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2131   pango_layout_set_text (layout, buffer, -1);
2132   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2133
2134   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2135     if (year_left)
2136       x = header_width - (3 + max_month_width
2137                           - (max_month_width - logical_rect.width)/2);      
2138     else
2139     x = 3 + (max_month_width - logical_rect.width) / 2;
2140   else
2141     if (year_left)
2142       x = header_width - (3 + priv->arrow_width + max_month_width
2143                           - (max_month_width - logical_rect.width)/2);
2144     else
2145     x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2146
2147   cairo_move_to (cr, x, y);
2148   pango_cairo_show_layout (cr, layout);
2149
2150   g_object_unref (layout);
2151   cairo_destroy (cr);
2152 }
2153
2154 static void
2155 calendar_paint_day_names (GtkCalendar *calendar)
2156 {
2157   GtkWidget *widget = GTK_WIDGET (calendar);
2158   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2159   cairo_t *cr;
2160   char buffer[255];
2161   int day,i;
2162   int day_width, cal_width;
2163   int day_wid_sep;
2164   PangoLayout *layout;
2165   PangoRectangle logical_rect;
2166   gint focus_padding;
2167   gint focus_width;
2168   
2169   cr = gdk_cairo_create (priv->day_name_win);
2170   
2171   gtk_widget_style_get (GTK_WIDGET (widget),
2172                         "focus-line-width", &focus_width,
2173                         "focus-padding", &focus_padding,
2174                         NULL);
2175   
2176   day_width = priv->day_width;
2177   cal_width = widget->allocation.width;
2178   day_wid_sep = day_width + DAY_XSEP;
2179   
2180   /*
2181    * Draw rectangles as inverted background for the labels.
2182    */
2183
2184   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2185   cairo_rectangle (cr,
2186                    CALENDAR_MARGIN, CALENDAR_MARGIN,
2187                    cal_width-CALENDAR_MARGIN * 2,
2188                    priv->day_name_h - CALENDAR_MARGIN);
2189   cairo_fill (cr);
2190   
2191   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2192     {
2193       cairo_rectangle (cr, 
2194                        CALENDAR_MARGIN,
2195                        priv->day_name_h - CALENDAR_YSEP,
2196                        priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2197                        CALENDAR_YSEP);
2198       cairo_fill (cr);
2199     }
2200   
2201   /*
2202    * Write the labels
2203    */
2204
2205   layout = gtk_widget_create_pango_layout (widget, NULL);
2206
2207   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2208   for (i = 0; i < 7; i++)
2209     {
2210       if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2211         day = 6 - i;
2212       else
2213         day = i;
2214       day = (day + priv->week_start) % 7;
2215       g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2216
2217       pango_layout_set_text (layout, buffer, -1);
2218       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2219
2220       cairo_move_to (cr, 
2221                      (CALENDAR_MARGIN +
2222                       + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2223                          (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2224                          : 0)
2225                       + day_wid_sep * i
2226                       + (day_width - logical_rect.width)/2),
2227                      CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2228       pango_cairo_show_layout (cr, layout);
2229     }
2230   
2231   g_object_unref (layout);
2232   cairo_destroy (cr);
2233 }
2234
2235 static void
2236 calendar_paint_week_numbers (GtkCalendar *calendar)
2237 {
2238   GtkWidget *widget = GTK_WIDGET (calendar);
2239   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2240   cairo_t *cr;
2241
2242   guint week = 0, year;
2243   gint row, x_loc, y_loc;
2244   gint day_height;
2245   char buffer[32];
2246   PangoLayout *layout;
2247   PangoRectangle logical_rect;
2248   gint focus_padding;
2249   gint focus_width;
2250   
2251   cr = gdk_cairo_create (priv->week_win);
2252   
2253   gtk_widget_style_get (GTK_WIDGET (widget),
2254                         "focus-line-width", &focus_width,
2255                         "focus-padding", &focus_padding,
2256                         NULL);
2257   
2258   /*
2259    * Draw a rectangle as inverted background for the labels.
2260    */
2261
2262   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2263   if (priv->day_name_win)
2264     cairo_rectangle (cr, 
2265                      CALENDAR_MARGIN,
2266                      0,
2267                      priv->week_width - CALENDAR_MARGIN,
2268                      priv->main_h - CALENDAR_MARGIN);
2269   else
2270     cairo_rectangle (cr,
2271                      CALENDAR_MARGIN,
2272                      CALENDAR_MARGIN,
2273                      priv->week_width - CALENDAR_MARGIN,
2274                      priv->main_h - 2 * CALENDAR_MARGIN);
2275   cairo_fill (cr);
2276   
2277   /*
2278    * Write the labels
2279    */
2280   
2281   layout = gtk_widget_create_pango_layout (widget, NULL);
2282   
2283   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2284   day_height = calendar_row_height (calendar);
2285   for (row = 0; row < 6; row++)
2286     {
2287       gboolean result;
2288       
2289       year = calendar->year;
2290       if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2291         year++;
2292
2293       result = week_of_year (&week, &year,              
2294                              ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2295                               + calendar->month) % 12 + 1, calendar->day[row][6]);
2296       g_return_if_fail (result);
2297
2298       /* Translators: this defines whether the week numbers should use
2299        * localized digits or the ones used in English (0123...).
2300        *
2301        * Translate to "%Id" if you want to use localized digits, or
2302        * translate to "%d" otherwise.  Don't include the
2303        * "calendar:week:digits|" part in the translation.
2304        *
2305        * Note that translating this doesn't guarantee that you get localized
2306        * digits.  That needs support from your system and locale definition
2307        * too.
2308        */
2309       g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2310       pango_layout_set_text (layout, buffer, -1);
2311       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2312
2313       y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2314
2315       x_loc = (priv->week_width
2316                - logical_rect.width
2317                - CALENDAR_XSEP - focus_padding - focus_width);
2318
2319       cairo_move_to (cr, x_loc, y_loc);
2320       pango_cairo_show_layout (cr, layout);
2321     }
2322   
2323   g_object_unref (layout);
2324   cairo_destroy (cr);
2325 }
2326
2327 static void
2328 calendar_invalidate_day_num (GtkCalendar *calendar,
2329                              gint         day)
2330 {
2331   gint r, c, row, col;
2332   
2333   row = -1;
2334   col = -1;
2335   for (r = 0; r < 6; r++)
2336     for (c = 0; c < 7; c++)
2337       if (calendar->day_month[r][c] == MONTH_CURRENT &&
2338           calendar->day[r][c] == day)
2339         {
2340           row = r;
2341           col = c;
2342         }
2343   
2344   g_return_if_fail (row != -1);
2345   g_return_if_fail (col != -1);
2346   
2347   calendar_invalidate_day (calendar, row, col);
2348 }
2349
2350 static void
2351 calendar_invalidate_day (GtkCalendar *calendar,
2352                          gint         row,
2353                          gint         col)
2354 {
2355   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2356
2357   if (priv->main_win)
2358     {
2359       GdkRectangle day_rect;
2360       
2361       calendar_day_rectangle (calendar, row, col, &day_rect);
2362       gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2363     }
2364 }
2365
2366 static gboolean
2367 is_color_attribute (PangoAttribute *attribute,
2368                     gpointer        data)
2369 {
2370   return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2371           attribute->klass->type == PANGO_ATTR_BACKGROUND);
2372 }
2373
2374 static void
2375 calendar_paint_day (GtkCalendar *calendar,
2376                     gint             row,
2377                     gint             col)
2378 {
2379   GtkWidget *widget = GTK_WIDGET (calendar);
2380   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2381   cairo_t *cr;
2382   GdkColor *text_color;
2383   const gchar *detail;
2384   gchar buffer[32];
2385   gint day;
2386   gint x_loc, y_loc;
2387   GdkRectangle day_rect;
2388
2389   PangoLayout *layout;
2390   PangoRectangle logical_rect;
2391   gboolean overflow = FALSE;
2392   
2393   g_return_if_fail (row < 6);
2394   g_return_if_fail (col < 7);
2395
2396   cr = gdk_cairo_create (priv->main_win);
2397
2398   day = calendar->day[row][col];
2399
2400   calendar_day_rectangle (calendar, row, col, &day_rect);
2401   
2402   if (calendar->day_month[row][col] == MONTH_PREV)
2403     {
2404       text_color = PREV_MONTH_COLOR (widget);
2405     } 
2406   else if (calendar->day_month[row][col] == MONTH_NEXT)
2407     {
2408       text_color =  NEXT_MONTH_COLOR (widget);
2409     } 
2410   else 
2411     {
2412 #if 0      
2413       if (calendar->highlight_row == row && calendar->highlight_col == col)
2414         {
2415           cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2416           gdk_cairo_rectangle (cr, &day_rect);
2417           cairo_fill (cr);
2418         }
2419 #endif     
2420       if (calendar->selected_day == day)
2421         {
2422           gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2423           gdk_cairo_rectangle (cr, &day_rect);
2424           cairo_fill (cr);
2425         }
2426       if (calendar->selected_day == day)
2427         text_color = SELECTED_FG_COLOR (widget);
2428       else if (calendar->marked_date[day-1])
2429         text_color = MARKED_COLOR (widget);
2430       else
2431         text_color = NORMAL_DAY_COLOR (widget);
2432     }
2433
2434   /* Translators: this defines whether the day numbers should use
2435    * localized digits or the ones used in English (0123...).
2436    *
2437    * Translate to "%Id" if you want to use localized digits, or
2438    * translate to "%d" otherwise.  Don't include the "calendar:day:digits|"
2439    * part in the translation.
2440    *
2441    * Note that translating this doesn't guarantee that you get localized
2442    * digits.  That needs support from your system and locale definition
2443    * too.
2444    */
2445   g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2446
2447   /* Get extra information to show, if any: */
2448
2449   if (priv->detail_func)
2450     detail = gtk_calendar_get_detail (calendar, row, col);
2451   else
2452     detail = NULL;
2453
2454   layout = gtk_widget_create_pango_layout (widget, buffer);
2455   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2456   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2457   
2458   x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2459   y_loc = day_rect.y;
2460   
2461   gdk_cairo_set_source_color (cr, text_color);
2462   cairo_move_to (cr, x_loc, y_loc);
2463   pango_cairo_show_layout (cr, layout);
2464     
2465   if (calendar->marked_date[day-1]
2466       && calendar->day_month[row][col] == MONTH_CURRENT)
2467     {
2468       cairo_move_to (cr, x_loc - 1, y_loc);
2469       pango_cairo_show_layout (cr, layout);
2470     }
2471
2472   y_loc += priv->max_day_char_descent;
2473
2474   if (priv->detail_func)
2475     {
2476       cairo_save (cr);
2477
2478       if (calendar->selected_day == day)
2479         gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2480       else if (calendar->day_month[row][col] == MONTH_CURRENT)
2481         gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2482       else
2483         gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2484
2485       cairo_set_line_width (cr, 1);
2486       cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2487       cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2488       cairo_stroke (cr);
2489
2490       cairo_restore (cr);
2491
2492       y_loc += 2;
2493     }
2494
2495   if (detail)
2496     {
2497       gint i, n_lines;
2498
2499       gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2500       pango_layout_set_markup (layout, markup, -1);
2501       g_free (markup);
2502
2503       if (day == calendar->selected_day)
2504         {
2505           /* Stripping colors as they conflict with selection marking. */
2506
2507           PangoAttrList *attrs = pango_layout_get_attributes (layout);
2508           PangoAttrList *colors = NULL;
2509
2510           if (attrs)
2511             colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2512           if (colors)
2513             pango_attr_list_unref (colors);
2514         }
2515
2516       if (priv->detail_width_chars)
2517         {
2518           pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2519           pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
2520         }
2521
2522       n_lines = pango_layout_get_line_count (layout);
2523
2524       if (priv->detail_height_rows &&
2525           n_lines > priv->detail_height_rows)
2526         {
2527           n_lines = priv->detail_height_rows;
2528           overflow = TRUE;
2529         }
2530
2531       for (i = 0; i < n_lines; ++i)
2532         {
2533           PangoLayoutLine *line = pango_layout_get_line_readonly (layout, i);
2534           pango_layout_line_get_pixel_extents (line, NULL, &logical_rect);
2535
2536           x_loc  = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2537           y_loc += PANGO_ASCENT (logical_rect);
2538
2539           cairo_move_to (cr, x_loc, y_loc);
2540           pango_cairo_show_layout_line (cr, line);
2541
2542           y_loc += PANGO_DESCENT (logical_rect);
2543         }
2544     }
2545
2546   if (GTK_WIDGET_HAS_FOCUS (calendar) 
2547       && calendar->focus_row == row && calendar->focus_col == col)
2548     {
2549       GtkStateType state;
2550
2551       if (calendar->selected_day == day)
2552         state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2553       else
2554         state = GTK_STATE_NORMAL;
2555       
2556       gtk_paint_focus (widget->style, 
2557                        priv->main_win,
2558                        state,
2559                        NULL, widget, "calendar-day",
2560                        day_rect.x,     day_rect.y, 
2561                        day_rect.width, day_rect.height);
2562     }
2563
2564   if (overflow)
2565     priv->detail_overflow[row] |= (1 << col);
2566   else
2567     priv->detail_overflow[row] &= ~(1 << col);
2568
2569   g_object_unref (layout);
2570   cairo_destroy (cr);
2571 }
2572
2573 static void
2574 calendar_paint_main (GtkCalendar *calendar)
2575 {
2576   gint row, col;
2577   
2578   for (col = 0; col < 7; col++)
2579     for (row = 0; row < 6; row++)
2580       calendar_paint_day (calendar, row, col);
2581 }
2582
2583 static void
2584 calendar_invalidate_arrow (GtkCalendar *calendar,
2585                            guint        arrow)
2586 {
2587   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2588   GdkWindow *window;
2589   
2590   window = priv->arrow_win[arrow];
2591   if (window)
2592     gdk_window_invalidate_rect (window, NULL, FALSE);
2593 }
2594
2595 static void
2596 calendar_paint_arrow (GtkCalendar *calendar,
2597                       guint            arrow)
2598 {
2599   GtkWidget *widget = GTK_WIDGET (calendar);
2600   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2601   GdkWindow *window;
2602   
2603   window = priv->arrow_win[arrow];
2604   if (window)
2605     {
2606       cairo_t *cr = gdk_cairo_create (window);
2607       gint width, height;
2608       gint state;
2609         
2610       state = priv->arrow_state[arrow];
2611
2612       gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2613       cairo_paint (cr);
2614       cairo_destroy (cr);
2615       
2616       gdk_drawable_get_size (window, &width, &height);
2617       if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2618         gtk_paint_arrow (widget->style, window, state, 
2619                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2620                          GTK_ARROW_LEFT, TRUE, 
2621                          width/2 - 3, height/2 - 4, 8, 8);
2622       else 
2623         gtk_paint_arrow (widget->style, window, state, 
2624                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2625                          GTK_ARROW_RIGHT, TRUE, 
2626                          width/2 - 4, height/2 - 4, 8, 8);
2627     }
2628 }
2629
2630 static gboolean
2631 gtk_calendar_expose (GtkWidget      *widget,
2632                      GdkEventExpose *event)
2633 {
2634   GtkCalendar *calendar = GTK_CALENDAR (widget);
2635   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2636   int i;
2637
2638   if (GTK_WIDGET_DRAWABLE (widget))
2639     {
2640       if (event->window == priv->main_win)
2641         calendar_paint_main (calendar);
2642       
2643       if (event->window == priv->header_win)
2644         calendar_paint_header (calendar);
2645
2646       for (i = 0; i < 4; i++)
2647         if (event->window == priv->arrow_win[i])
2648           calendar_paint_arrow (calendar, i);
2649       
2650       if (event->window == priv->day_name_win)
2651         calendar_paint_day_names (calendar);
2652       
2653       if (event->window == priv->week_win)
2654         calendar_paint_week_numbers (calendar);
2655       if (event->window == widget->window)
2656         {
2657           gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2658                             GTK_SHADOW_IN, NULL, widget, "calendar",
2659                             0, 0, widget->allocation.width, widget->allocation.height);
2660         }
2661     }
2662   
2663   return FALSE;
2664 }
2665
2666 \f
2667 /****************************************
2668  *           Mouse handling             *
2669  ****************************************/
2670
2671 static void
2672 calendar_arrow_action (GtkCalendar *calendar,
2673                        guint        arrow)
2674 {
2675   switch (arrow)
2676     {
2677     case ARROW_YEAR_LEFT:
2678       calendar_set_year_prev (calendar);
2679       break;
2680     case ARROW_YEAR_RIGHT:
2681       calendar_set_year_next (calendar);
2682       break;
2683     case ARROW_MONTH_LEFT:
2684       calendar_set_month_prev (calendar);
2685       break;
2686     case ARROW_MONTH_RIGHT:
2687       calendar_set_month_next (calendar);
2688       break;
2689     default:;
2690       /* do nothing */
2691     }
2692 }
2693
2694 static gboolean
2695 calendar_timer (gpointer data)
2696 {
2697   GtkCalendar *calendar = data;
2698   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2699   gboolean retval = FALSE;
2700   
2701   if (priv->timer)
2702     {
2703       calendar_arrow_action (calendar, priv->click_child);
2704
2705       if (priv->need_timer)
2706         {
2707           GtkSettings *settings;
2708           guint        timeout;
2709
2710           settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2711           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2712
2713           priv->need_timer = FALSE;
2714           priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2715                                             timeout * SCROLL_DELAY_FACTOR,
2716                                             (GSourceFunc) calendar_timer,
2717                                             (gpointer) calendar, NULL);
2718         }
2719       else 
2720         retval = TRUE;
2721     }
2722
2723   return retval;
2724 }
2725
2726 static void
2727 calendar_start_spinning (GtkCalendar *calendar,
2728                          gint         click_child)
2729 {
2730   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2731
2732   priv->click_child = click_child;
2733   
2734   if (!priv->timer)
2735     {
2736       GtkSettings *settings;
2737       guint        timeout;
2738
2739       settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2740       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2741
2742       priv->need_timer = TRUE;
2743       priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2744                                         timeout,
2745                                         (GSourceFunc) calendar_timer,
2746                                         (gpointer) calendar, NULL);
2747     }
2748 }
2749
2750 static void
2751 calendar_stop_spinning (GtkCalendar *calendar)
2752 {
2753   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2754
2755   if (priv->timer)
2756     {
2757       g_source_remove (priv->timer);
2758       priv->timer = 0;
2759       priv->need_timer = FALSE;
2760     }
2761 }
2762
2763 static void
2764 calendar_main_button_press (GtkCalendar    *calendar,
2765                             GdkEventButton *event)
2766 {
2767   GtkWidget *widget = GTK_WIDGET (calendar);
2768   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2769   gint x, y;
2770   gint row, col;
2771   gint day_month;
2772   gint day;
2773   
2774   x = (gint) (event->x);
2775   y = (gint) (event->y);
2776   
2777   row = calendar_row_from_y (calendar, y);
2778   col = calendar_column_from_x (calendar, x);
2779
2780   /* If row or column isn't found, just return. */
2781   if (row == -1 || col == -1)
2782     return;
2783   
2784   day_month = calendar->day_month[row][col];
2785
2786   if (event->type == GDK_BUTTON_PRESS)
2787     {
2788       day = calendar->day[row][col];
2789       
2790       if (day_month == MONTH_PREV)
2791         calendar_set_month_prev (calendar);
2792       else if (day_month == MONTH_NEXT)
2793         calendar_set_month_next (calendar);
2794       
2795       if (!GTK_WIDGET_HAS_FOCUS (widget))
2796         gtk_widget_grab_focus (widget);
2797           
2798       if (event->button == 1) 
2799         {
2800           priv->in_drag = 1;
2801           priv->drag_start_x = x;
2802           priv->drag_start_y = y;
2803         }
2804
2805       calendar_select_and_focus_day (calendar, day);
2806     }
2807   else if (event->type == GDK_2BUTTON_PRESS)
2808     {
2809       priv->in_drag = 0;
2810       if (day_month == MONTH_CURRENT)
2811         g_signal_emit (calendar,
2812                        gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2813                        0);
2814     }
2815 }
2816
2817 static gboolean
2818 gtk_calendar_button_press (GtkWidget      *widget,
2819                            GdkEventButton *event)
2820 {
2821   GtkCalendar *calendar = GTK_CALENDAR (widget);
2822   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2823   gint arrow = -1;
2824   
2825   if (event->window == priv->main_win)
2826     calendar_main_button_press (calendar, event);
2827
2828   if (!GTK_WIDGET_HAS_FOCUS (widget))
2829     gtk_widget_grab_focus (widget);
2830
2831   for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2832     {
2833       if (event->window == priv->arrow_win[arrow])
2834         {
2835           
2836           /* only call the action on single click, not double */
2837           if (event->type == GDK_BUTTON_PRESS)
2838             {
2839               if (event->button == 1)
2840                 calendar_start_spinning (calendar, arrow);
2841
2842               calendar_arrow_action (calendar, arrow);        
2843             }
2844
2845           return TRUE;
2846         }
2847     }
2848
2849   return FALSE;
2850 }
2851
2852 static gboolean
2853 gtk_calendar_button_release (GtkWidget    *widget,
2854                              GdkEventButton *event)
2855 {
2856   GtkCalendar *calendar = GTK_CALENDAR (widget);
2857   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2858
2859   if (event->button == 1) 
2860     {
2861       calendar_stop_spinning (calendar);
2862
2863       if (priv->in_drag)
2864         priv->in_drag = 0;
2865     }
2866
2867   return TRUE;
2868 }
2869
2870 static gboolean
2871 gtk_calendar_motion_notify (GtkWidget      *widget,
2872                             GdkEventMotion *event)
2873 {
2874   GtkCalendar *calendar = GTK_CALENDAR (widget);
2875   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2876   gint event_x, event_y;
2877   gint row, col;
2878   gint old_row, old_col;
2879   
2880   event_x = (gint) (event->x);
2881   event_y = (gint) (event->y);
2882   
2883   if (event->window == priv->main_win)
2884     {
2885       
2886       if (priv->in_drag) 
2887         {
2888           if (gtk_drag_check_threshold (widget,
2889                                         priv->drag_start_x, priv->drag_start_y,
2890                                         event->x, event->y))
2891             {
2892               GdkDragContext *context;
2893               GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2894               gtk_target_list_add_text_targets (target_list, 0);
2895               context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2896                                         1, (GdkEvent *)event);
2897
2898           
2899               priv->in_drag = 0;
2900               
2901               gtk_target_list_unref (target_list);
2902               gtk_drag_set_icon_default (context);
2903             }
2904         }
2905       else 
2906         {
2907           row = calendar_row_from_y (calendar, event_y);
2908           col = calendar_column_from_x (calendar, event_x);
2909           
2910           if (row != calendar->highlight_row || calendar->highlight_col != col)
2911             {
2912               old_row = calendar->highlight_row;
2913               old_col = calendar->highlight_col;
2914               if (old_row > -1 && old_col > -1)
2915                 {
2916                   calendar->highlight_row = -1;
2917                   calendar->highlight_col = -1;
2918                   calendar_invalidate_day (calendar, old_row, old_col);
2919                 }
2920               
2921               calendar->highlight_row = row;
2922               calendar->highlight_col = col;
2923               
2924               if (row > -1 && col > -1)
2925                 calendar_invalidate_day (calendar, row, col);
2926             }
2927         }
2928     }
2929   return TRUE;
2930 }
2931
2932 static gboolean
2933 gtk_calendar_enter_notify (GtkWidget        *widget,
2934                            GdkEventCrossing *event)
2935 {
2936   GtkCalendar *calendar = GTK_CALENDAR (widget);
2937   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2938   
2939   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2940     {
2941       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2942       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2943     }
2944   
2945   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2946     {
2947       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2948       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2949     }
2950   
2951   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2952     {
2953       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2954       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2955     }
2956   
2957   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2958     {
2959       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2960       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2961     }
2962   
2963   return TRUE;
2964 }
2965
2966 static gboolean
2967 gtk_calendar_leave_notify (GtkWidget        *widget,
2968                            GdkEventCrossing *event)
2969 {
2970   GtkCalendar *calendar = GTK_CALENDAR (widget);
2971   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2972   gint row;
2973   gint col;
2974   
2975   if (event->window == priv->main_win)
2976     {
2977       row = calendar->highlight_row;
2978       col = calendar->highlight_col;
2979       calendar->highlight_row = -1;
2980       calendar->highlight_col = -1;
2981       if (row > -1 && col > -1)
2982         calendar_invalidate_day (calendar, row, col);
2983     }
2984   
2985   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2986     {
2987       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
2988       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2989     }
2990   
2991   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2992     {
2993       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
2994       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2995     }
2996   
2997   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2998     {
2999       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3000       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3001     }
3002   
3003   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3004     {
3005       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3006       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3007     }
3008   
3009   return TRUE;
3010 }
3011
3012 static gboolean
3013 gtk_calendar_scroll (GtkWidget      *widget,
3014                      GdkEventScroll *event)
3015 {
3016   GtkCalendar *calendar = GTK_CALENDAR (widget);
3017
3018   if (event->direction == GDK_SCROLL_UP) 
3019     {
3020       if (!GTK_WIDGET_HAS_FOCUS (widget))
3021         gtk_widget_grab_focus (widget);
3022       calendar_set_month_prev (calendar);
3023     }
3024   else if (event->direction == GDK_SCROLL_DOWN) 
3025     {
3026       if (!GTK_WIDGET_HAS_FOCUS (widget))
3027         gtk_widget_grab_focus (widget);
3028       calendar_set_month_next (calendar);
3029     }
3030   else
3031     return FALSE;
3032
3033   return TRUE;
3034 }
3035
3036 \f
3037 /****************************************
3038  *             Key handling              *
3039  ****************************************/
3040
3041 static void 
3042 move_focus (GtkCalendar *calendar, 
3043             gint         direction)
3044 {
3045   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3046  
3047   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3048       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
3049     {
3050       if (calendar->focus_col > 0)
3051           calendar->focus_col--;
3052       else if (calendar->focus_row > 0)
3053         {
3054           calendar->focus_col = 6;
3055           calendar->focus_row--;
3056         }
3057
3058       if (calendar->focus_col < 0)
3059         calendar->focus_col = 6;
3060       if (calendar->focus_row < 0)
3061         calendar->focus_row = 5;
3062     }
3063   else 
3064     {
3065       if (calendar->focus_col < 6)
3066         calendar->focus_col++;
3067       else if (calendar->focus_row < 5)
3068         {
3069           calendar->focus_col = 0;
3070           calendar->focus_row++;
3071         }
3072
3073       if (calendar->focus_col < 0)
3074         calendar->focus_col = 0;
3075       if (calendar->focus_row < 0)
3076         calendar->focus_row = 0;
3077     }
3078 }
3079
3080 static gboolean
3081 gtk_calendar_key_press (GtkWidget   *widget,
3082                         GdkEventKey *event)
3083 {
3084   GtkCalendar *calendar;
3085   gint return_val;
3086   gint old_focus_row;
3087   gint old_focus_col;
3088   gint row, col, day;
3089   
3090   calendar = GTK_CALENDAR (widget);
3091   return_val = FALSE;
3092   
3093   old_focus_row = calendar->focus_row;
3094   old_focus_col = calendar->focus_col;
3095
3096   switch (event->keyval)
3097     {
3098     case GDK_KP_Left:
3099     case GDK_Left:
3100       return_val = TRUE;
3101       if (event->state & GDK_CONTROL_MASK)
3102         calendar_set_month_prev (calendar);
3103       else
3104         {
3105           move_focus (calendar, -1);
3106           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3107           calendar_invalidate_day (calendar, calendar->focus_row,
3108                                    calendar->focus_col);
3109         }
3110       break;
3111     case GDK_KP_Right:
3112     case GDK_Right:
3113       return_val = TRUE;
3114       if (event->state & GDK_CONTROL_MASK)
3115         calendar_set_month_next (calendar);
3116       else
3117         {
3118           move_focus (calendar, 1);
3119           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3120           calendar_invalidate_day (calendar, calendar->focus_row,
3121                                    calendar->focus_col);
3122         }
3123       break;
3124     case GDK_KP_Up:
3125     case GDK_Up:
3126       return_val = TRUE;
3127       if (event->state & GDK_CONTROL_MASK)
3128         calendar_set_year_prev (calendar);
3129       else
3130         {
3131           if (calendar->focus_row > 0)
3132             calendar->focus_row--;
3133           if (calendar->focus_row < 0)
3134             calendar->focus_row = 5;
3135           if (calendar->focus_col < 0)
3136             calendar->focus_col = 6;
3137           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3138           calendar_invalidate_day (calendar, calendar->focus_row,
3139                                    calendar->focus_col);
3140         }
3141       break;
3142     case GDK_KP_Down:
3143     case GDK_Down:
3144       return_val = TRUE;
3145       if (event->state & GDK_CONTROL_MASK)
3146         calendar_set_year_next (calendar);
3147       else
3148         {
3149           if (calendar->focus_row < 5)
3150             calendar->focus_row++;
3151           if (calendar->focus_col < 0)
3152             calendar->focus_col = 0;
3153           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3154           calendar_invalidate_day (calendar, calendar->focus_row,
3155                                    calendar->focus_col);
3156         }
3157       break;
3158     case GDK_KP_Space:
3159     case GDK_space:
3160       row = calendar->focus_row;
3161       col = calendar->focus_col;
3162       
3163       if (row > -1 && col > -1)
3164         {
3165           return_val = TRUE;
3166
3167           day = calendar->day[row][col];
3168           if (calendar->day_month[row][col] == MONTH_PREV)
3169             calendar_set_month_prev (calendar);
3170           else if (calendar->day_month[row][col] == MONTH_NEXT)
3171             calendar_set_month_next (calendar);
3172
3173           calendar_select_and_focus_day (calendar, day);
3174         }
3175     }   
3176   
3177   return return_val;
3178 }
3179
3180 \f
3181 /****************************************
3182  *           Misc widget methods        *
3183  ****************************************/
3184
3185 static void
3186 calendar_set_background (GtkWidget *widget)
3187 {
3188   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3189   gint i;
3190   
3191   if (GTK_WIDGET_REALIZED (widget))
3192     {
3193       for (i = 0; i < 4; i++)
3194         {
3195           if (priv->arrow_win[i])
3196             gdk_window_set_background (priv->arrow_win[i], 
3197                                        HEADER_BG_COLOR (widget));
3198         }
3199       if (priv->header_win)
3200         gdk_window_set_background (priv->header_win, 
3201                                    HEADER_BG_COLOR (widget));
3202       if (priv->day_name_win)
3203         gdk_window_set_background (priv->day_name_win, 
3204                                    BACKGROUND_COLOR (widget));
3205       if (priv->week_win)
3206         gdk_window_set_background (priv->week_win,
3207                                    BACKGROUND_COLOR (widget));
3208       if (priv->main_win)
3209         gdk_window_set_background (priv->main_win,
3210                                    BACKGROUND_COLOR (widget));
3211       if (widget->window)
3212         gdk_window_set_background (widget->window,
3213                                    BACKGROUND_COLOR (widget)); 
3214     }
3215 }
3216
3217 static void
3218 gtk_calendar_style_set (GtkWidget *widget,
3219                         GtkStyle  *previous_style)
3220 {
3221   if (previous_style && GTK_WIDGET_REALIZED (widget))
3222     calendar_set_background (widget);
3223 }
3224
3225 static void
3226 gtk_calendar_state_changed (GtkWidget      *widget,
3227                             GtkStateType    previous_state)
3228 {
3229   GtkCalendar *calendar = GTK_CALENDAR (widget);
3230   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3231   int i;
3232   
3233   if (!GTK_WIDGET_IS_SENSITIVE (widget))
3234     {
3235       priv->in_drag = 0;
3236       calendar_stop_spinning (calendar);    
3237     }
3238
3239   for (i = 0; i < 4; i++)
3240     if (GTK_WIDGET_IS_SENSITIVE (widget))
3241       priv->arrow_state[i] = GTK_STATE_NORMAL;
3242     else 
3243       priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3244   
3245   calendar_set_background (widget);
3246 }
3247
3248 static void
3249 gtk_calendar_grab_notify (GtkWidget *widget,
3250                           gboolean   was_grabbed)
3251 {
3252   if (!was_grabbed)
3253     calendar_stop_spinning (GTK_CALENDAR (widget));
3254 }
3255
3256 static gboolean
3257 gtk_calendar_focus_out (GtkWidget     *widget,
3258                         GdkEventFocus *event)
3259 {
3260   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3261   GtkCalendar *calendar = GTK_CALENDAR (widget);
3262
3263   calendar_queue_refresh (calendar);
3264   calendar_stop_spinning (calendar);
3265   
3266   priv->in_drag = 0; 
3267
3268   return FALSE;
3269 }
3270
3271 \f
3272 /****************************************
3273  *          Drag and Drop               *
3274  ****************************************/
3275
3276 static void
3277 gtk_calendar_drag_data_get (GtkWidget        *widget,
3278                             GdkDragContext   *context,
3279                             GtkSelectionData *selection_data,
3280                             guint             info,
3281                             guint             time)
3282 {
3283   GtkCalendar *calendar = GTK_CALENDAR (widget);
3284   GDate *date;
3285   gchar str[128];
3286   gsize len;
3287
3288   date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3289   len = g_date_strftime (str, 127, "%x", date);
3290   gtk_selection_data_set_text (selection_data, str, len);
3291   
3292   g_free (date);
3293 }
3294
3295 /* Get/set whether drag_motion requested the drag data and
3296  * drag_data_received should thus not actually insert the data,
3297  * since the data doesn't result from a drop.
3298  */
3299 static void
3300 set_status_pending (GdkDragContext *context,
3301                     GdkDragAction   suggested_action)
3302 {
3303   g_object_set_data (G_OBJECT (context),
3304                      I_("gtk-calendar-status-pending"),
3305                      GINT_TO_POINTER (suggested_action));
3306 }
3307
3308 static GdkDragAction
3309 get_status_pending (GdkDragContext *context)
3310 {
3311   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3312                                              "gtk-calendar-status-pending"));
3313 }
3314
3315 static void
3316 gtk_calendar_drag_leave (GtkWidget      *widget,
3317                          GdkDragContext *context,
3318                          guint           time)
3319 {
3320   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3321
3322   priv->drag_highlight = 0;
3323   gtk_drag_unhighlight (widget);
3324   
3325 }
3326
3327 static gboolean
3328 gtk_calendar_drag_motion (GtkWidget      *widget,
3329                           GdkDragContext *context,
3330                           gint            x,
3331                           gint            y,
3332                           guint           time)
3333 {
3334   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3335   GdkAtom target;
3336   
3337   if (!priv->drag_highlight) 
3338     {
3339       priv->drag_highlight = 1;
3340       gtk_drag_highlight (widget);
3341     }
3342   
3343   target = gtk_drag_dest_find_target (widget, context, NULL);
3344   if (target == GDK_NONE || context->suggested_action == 0)
3345     gdk_drag_status (context, 0, time);
3346   else
3347     {
3348       set_status_pending (context, context->suggested_action);
3349       gtk_drag_get_data (widget, context, target, time);
3350     }
3351   
3352   return TRUE;
3353 }
3354
3355 static gboolean
3356 gtk_calendar_drag_drop (GtkWidget      *widget,
3357                         GdkDragContext *context,
3358                         gint            x,
3359                         gint            y,
3360                         guint           time)
3361 {
3362   GdkAtom target;
3363
3364   target = gtk_drag_dest_find_target (widget, context, NULL);  
3365   if (target != GDK_NONE)
3366     {
3367       gtk_drag_get_data (widget, context, 
3368                          target, 
3369                          time);
3370       return TRUE;
3371     }
3372
3373   return FALSE;
3374 }
3375
3376 static void
3377 gtk_calendar_drag_data_received (GtkWidget        *widget,
3378                                  GdkDragContext   *context,
3379                                  gint              x,
3380                                  gint              y,
3381                                  GtkSelectionData *selection_data,
3382                                  guint             info,
3383                                  guint             time)
3384 {
3385   GtkCalendar *calendar = GTK_CALENDAR (widget);
3386   guint day, month, year;
3387   gchar *str;
3388   GDate *date;
3389   GdkDragAction suggested_action;
3390
3391   suggested_action = get_status_pending (context);
3392
3393   if (suggested_action) 
3394     {
3395       set_status_pending (context, 0);
3396      
3397       /* We are getting this data due to a request in drag_motion,
3398        * rather than due to a request in drag_drop, so we are just
3399        * supposed to call drag_status, not actually paste in the
3400        * data.
3401        */
3402       str = (gchar*) gtk_selection_data_get_text (selection_data);
3403
3404       if (str) 
3405         {
3406           date = g_date_new ();
3407           g_date_set_parse (date, str);
3408           if (!g_date_valid (date)) 
3409               suggested_action = 0;
3410           g_date_free (date);
3411           g_free (str);
3412         }
3413       else
3414         suggested_action = 0;
3415
3416       gdk_drag_status (context, suggested_action, time);
3417
3418       return;
3419     }
3420
3421   date = g_date_new ();
3422   str = (gchar*) gtk_selection_data_get_text (selection_data);
3423   if (str) 
3424     {
3425       g_date_set_parse (date, str);
3426       g_free (str);
3427     }
3428   
3429   if (!g_date_valid (date)) 
3430     {
3431       g_warning ("Received invalid date data\n");
3432       g_date_free (date);       
3433       gtk_drag_finish (context, FALSE, FALSE, time);
3434       return;
3435     }
3436
3437   day = g_date_get_day (date);
3438   month = g_date_get_month (date);
3439   year = g_date_get_year (date);
3440   g_date_free (date);   
3441
3442   gtk_drag_finish (context, TRUE, FALSE, time);
3443
3444   
3445   g_object_freeze_notify (G_OBJECT (calendar));
3446   if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3447       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3448     gtk_calendar_select_month (calendar, month - 1, year);
3449   gtk_calendar_select_day (calendar, day);
3450   g_object_thaw_notify (G_OBJECT (calendar));  
3451 }
3452
3453 \f
3454 /****************************************
3455  *              Public API              *
3456  ****************************************/
3457
3458 /**
3459  * gtk_calendar_new:
3460  * 
3461  * Creates a new calendar, with the current date being selected. 
3462  * 
3463  * Return value: a newly #GtkCalendar widget
3464  **/
3465 GtkWidget*
3466 gtk_calendar_new (void)
3467 {
3468   return g_object_new (GTK_TYPE_CALENDAR, NULL);
3469 }
3470
3471 /**
3472  * gtk_calendar_display_options:
3473  * @calendar: a #GtkCalendar.
3474  * @flags: the display options to set.
3475  *
3476  * Sets display options (whether to display the heading and the month headings).
3477  * 
3478  * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3479  **/
3480 void
3481 gtk_calendar_display_options (GtkCalendar              *calendar,
3482                               GtkCalendarDisplayOptions flags)
3483 {
3484   gtk_calendar_set_display_options (calendar, flags);
3485 }
3486
3487 /**
3488  * gtk_calendar_get_display_options:
3489  * @calendar: a #GtkCalendar
3490  * 
3491  * Returns the current display options of @calendar. 
3492  * 
3493  * Return value: the display options.
3494  *
3495  * Since: 2.4
3496  **/
3497 GtkCalendarDisplayOptions 
3498 gtk_calendar_get_display_options (GtkCalendar         *calendar)
3499 {
3500   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3501
3502   return calendar->display_flags;
3503 }
3504
3505 /**
3506  * gtk_calendar_set_display_options:
3507  * @calendar: a #GtkCalendar
3508  * @flags: the display options to set
3509  * 
3510  * Sets display options (whether to display the heading and the month  
3511  * headings).
3512  *
3513  * Since: 2.4
3514  **/
3515 void
3516 gtk_calendar_set_display_options (GtkCalendar          *calendar,
3517                                   GtkCalendarDisplayOptions flags)
3518 {
3519   GtkWidget *widget = GTK_WIDGET (calendar);
3520   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3521   gint resize = 0;
3522   gint i;
3523   GtkCalendarDisplayOptions old_flags;
3524   
3525   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3526   
3527   old_flags = calendar->display_flags;
3528   
3529   if (GTK_WIDGET_REALIZED (widget))
3530     {
3531       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3532         {
3533           resize ++;
3534           if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3535               && (priv->header_win))
3536             {
3537               calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3538               calendar_realize_arrows (calendar);
3539             }
3540           else
3541             {
3542               for (i = 0; i < 4; i++)
3543                 {
3544                   if (priv->arrow_win[i])
3545                     {
3546                       gdk_window_set_user_data (priv->arrow_win[i], 
3547                                                 NULL);
3548                       gdk_window_destroy (priv->arrow_win[i]);
3549                       priv->arrow_win[i] = NULL;
3550                     }
3551                 }
3552             }
3553         }
3554       
3555       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3556         {
3557           resize++;
3558           
3559           if (flags & GTK_CALENDAR_SHOW_HEADING)
3560             {
3561               calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3562               calendar_realize_header (calendar);
3563             }
3564           else
3565             {
3566               for (i = 0; i < 4; i++)
3567                 {
3568                   if (priv->arrow_win[i])
3569                     {
3570                       gdk_window_set_user_data (priv->arrow_win[i], 
3571                                                 NULL);
3572                       gdk_window_destroy (priv->arrow_win[i]);
3573                       priv->arrow_win[i] = NULL;
3574                     }
3575                 }
3576               gdk_window_set_user_data (priv->header_win, NULL);
3577               gdk_window_destroy (priv->header_win);
3578               priv->header_win = NULL;
3579             }
3580         }
3581       
3582       
3583       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3584         {
3585           resize++;
3586           
3587           if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3588             {
3589               calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3590               calendar_realize_day_names (calendar);
3591             }
3592           else
3593             {
3594               gdk_window_set_user_data (priv->day_name_win, NULL);
3595               gdk_window_destroy (priv->day_name_win);
3596               priv->day_name_win = NULL;
3597             }
3598         }
3599       
3600       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3601         {
3602           resize++;
3603           
3604           if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3605             {
3606               calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3607               calendar_realize_week_numbers (calendar);
3608             }
3609           else
3610             {
3611               gdk_window_set_user_data (priv->week_win, NULL);
3612               gdk_window_destroy (priv->week_win);
3613               priv->week_win = NULL;
3614             }
3615         }
3616
3617       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3618         g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3619       
3620       calendar->display_flags = flags;
3621       if (resize)
3622         gtk_widget_queue_resize (GTK_WIDGET (calendar));
3623       
3624     } 
3625   else
3626     calendar->display_flags = flags;
3627   
3628   g_object_freeze_notify (G_OBJECT (calendar));
3629   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3630     g_object_notify (G_OBJECT (calendar), "show-heading");
3631   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3632     g_object_notify (G_OBJECT (calendar), "show-day-names");
3633   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3634     g_object_notify (G_OBJECT (calendar), "no-month-change");
3635   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3636     g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3637   g_object_thaw_notify (G_OBJECT (calendar));
3638 }
3639
3640 /**
3641  * gtk_calendar_select_month:
3642  * @calendar: a #GtkCalendar
3643  * @month: a month number between 0 and 11.
3644  * @year: the year the month is in.
3645  * 
3646  * Shifts the calendar to a different month.
3647  * 
3648  * Return value: %TRUE, always
3649  **/
3650 gboolean
3651 gtk_calendar_select_month (GtkCalendar *calendar,
3652                            guint        month,
3653                            guint        year)
3654 {
3655   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3656   g_return_val_if_fail (month <= 11, FALSE);
3657   
3658   calendar->month = month;
3659   calendar->year  = year;
3660   
3661   calendar_compute_days (calendar);
3662   calendar_queue_refresh (calendar);
3663
3664   g_object_freeze_notify (G_OBJECT (calendar));
3665   g_object_notify (G_OBJECT (calendar), "month");
3666   g_object_notify (G_OBJECT (calendar), "year");
3667   g_object_thaw_notify (G_OBJECT (calendar));
3668
3669   g_signal_emit (calendar,
3670                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3671                  0);
3672   return TRUE;
3673 }
3674
3675 /**
3676  * gtk_calendar_select_day:
3677  * @calendar: a #GtkCalendar.
3678  * @day: the day number between 1 and 31, or 0 to unselect 
3679  *   the currently selected day.
3680  * 
3681  * Selects a day from the current month.
3682  **/
3683 void
3684 gtk_calendar_select_day (GtkCalendar *calendar,
3685                          guint        day)
3686 {
3687   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3688   g_return_if_fail (day <= 31);
3689   
3690   /* Deselect the old day */
3691   if (calendar->selected_day > 0)
3692     {
3693       gint selected_day;
3694       
3695       selected_day = calendar->selected_day;
3696       calendar->selected_day = 0;
3697       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3698         calendar_invalidate_day_num (calendar, selected_day);
3699     }
3700   
3701   calendar->selected_day = day;
3702   
3703   /* Select the new day */
3704   if (day != 0)
3705     {
3706       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3707         calendar_invalidate_day_num (calendar, day);
3708     }
3709   
3710   g_object_notify (G_OBJECT (calendar), "day");
3711
3712   g_signal_emit (calendar,
3713                  gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3714                  0);
3715 }
3716
3717 /**
3718  * gtk_calendar_clear_marks:
3719  * @calendar: a #GtkCalendar
3720  * 
3721  * Remove all visual markers.
3722  **/
3723 void
3724 gtk_calendar_clear_marks (GtkCalendar *calendar)
3725 {
3726   guint day;
3727   
3728   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3729   
3730   for (day = 0; day < 31; day++)
3731     {
3732       calendar->marked_date[day] = FALSE;
3733     }
3734
3735   calendar->num_marked_dates = 0;
3736   calendar_queue_refresh (calendar);
3737 }
3738
3739 /**
3740  * gtk_calendar_mark_day:
3741  * @calendar: a #GtkCalendar 
3742  * @day: the day number to mark between 1 and 31.
3743  * 
3744  * Places a visual marker on a particular day.
3745  * 
3746  * Return value: %TRUE, always
3747  **/
3748 gboolean
3749 gtk_calendar_mark_day (GtkCalendar *calendar,
3750                        guint        day)
3751 {
3752   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3753   
3754   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3755     {
3756       calendar->marked_date[day - 1] = TRUE;
3757       calendar->num_marked_dates++;
3758       calendar_invalidate_day_num (calendar, day);
3759     }
3760   
3761   return TRUE;
3762 }
3763
3764 /**
3765  * gtk_calendar_unmark_day:
3766  * @calendar: a #GtkCalendar.
3767  * @day: the day number to unmark between 1 and 31.
3768  * 
3769  * Removes the visual marker from a particular day.
3770  * 
3771  * Return value: %TRUE, always
3772  **/
3773 gboolean
3774 gtk_calendar_unmark_day (GtkCalendar *calendar,
3775                          guint        day)
3776 {
3777   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3778   
3779   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3780     {
3781       calendar->marked_date[day - 1] = FALSE;
3782       calendar->num_marked_dates--;
3783       calendar_invalidate_day_num (calendar, day);
3784     }
3785   
3786   return TRUE;
3787 }
3788
3789 /**
3790  * gtk_calendar_get_date:
3791  * @calendar: a #GtkCalendar
3792  * @year: location to store the year number, or %NULL
3793  * @month: location to store the month number (between 0 and 11), or %NULL
3794  * @day: location to store the day number (between 1 and 31), or %NULL
3795  * 
3796  * Obtains the selected date from a #GtkCalendar.
3797  **/
3798 void
3799 gtk_calendar_get_date (GtkCalendar *calendar,
3800                        guint       *year,
3801                        guint       *month,
3802                        guint       *day)
3803 {
3804   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3805   
3806   if (year)
3807     *year = calendar->year;
3808   
3809   if (month)
3810     *month = calendar->month;
3811   
3812   if (day)
3813     *day = calendar->selected_day;
3814 }
3815
3816 /**
3817  * gtk_calendar_set_detail_func:
3818  * @calendar: a #GtkCalendar.
3819  * @func: a function providing details for each day.
3820  * @data: data to pass to @func invokations.
3821  * @destroy: a function for releasing @data.
3822  *
3823  * Installs a function which provides Pango markup with detail information
3824  * for each day. Examples for such details are holidays or appointments. That
3825  * information is shown below each day when #GtkCalendar:show-details is set.
3826  * A tooltip containing with full detail information is provided, if the entire
3827  * text should not fit into the details area, or if #GtkCalendar:show-details
3828  * is not set.
3829  *
3830  * The size of the details area can be restricted by setting the
3831  * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3832  * properties.
3833  *
3834  * Since: 2.16
3835  */
3836 void
3837 gtk_calendar_set_detail_func (GtkCalendar           *calendar,
3838                               GtkCalendarDetailFunc  func,
3839                               gpointer               data,
3840                               GDestroyNotify         destroy)
3841 {
3842   GtkCalendarPrivate *priv;
3843
3844   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3845
3846   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3847
3848   if (priv->detail_func_destroy)
3849     priv->detail_func_destroy (priv->detail_func_user_data);
3850
3851   priv->detail_func = func;
3852   priv->detail_func_user_data = data;
3853   priv->detail_func_destroy = destroy;
3854
3855   gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3856                               NULL != priv->detail_func);
3857   gtk_widget_queue_resize (GTK_WIDGET (calendar));
3858 }
3859
3860 /**
3861  * gtk_calendar_set_detail_width_chars:
3862  * @calendar: a #GtkCalendar.
3863  * @chars: detail width in characters.
3864  *
3865  * Updates the width of detail cells.
3866  * See #GtkCalendar:detail-width-chars.
3867  *
3868  * Since: 2.16
3869  */
3870 void
3871 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3872                                      gint         chars)
3873 {
3874   GtkCalendarPrivate *priv;
3875
3876   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3877
3878   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3879
3880   if (chars != priv->detail_width_chars)
3881     {
3882       priv->detail_width_chars = chars;
3883       g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3884       gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3885     }
3886 }
3887
3888 /**
3889  * gtk_calendar_set_detail_height_rows:
3890  * @calendar: a #GtkCalendar.
3891  * @rows: detail height in rows.
3892  *
3893  * Updates the height of detail cells.
3894  * See #GtkCalendar:detail-height-rows.
3895  *
3896  * Since: 2.16
3897  */
3898 void
3899 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3900                                      gint         rows)
3901 {
3902   GtkCalendarPrivate *priv;
3903
3904   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3905
3906   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3907
3908   if (rows != priv->detail_height_rows)
3909     {
3910       priv->detail_height_rows = rows;
3911       g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3912       gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3913     }
3914 }
3915
3916 /**
3917  * gtk_calendar_get_detail_width_chars:
3918  * @calendar: a #GtkCalendar.
3919  *
3920  * Queries the width of detail cells, in characters.
3921  * See #GtkCalendar:detail-width-chars.
3922  *
3923  * Since: 2.16
3924  *
3925  * Return value: The width of detail cells, in characters.
3926  */
3927 gint
3928 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3929 {
3930   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3931   return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3932 }
3933
3934 /**
3935  * gtk_calendar_get_detail_height_rows:
3936  * @calendar: a #GtkCalendar.
3937  *
3938  * Queries the height of detail cells, in rows.
3939  * See #GtkCalendar:detail-width-chars.
3940  *
3941  * Since: 2.16
3942  *
3943  * Return value: The height of detail cells, in rows.
3944  */
3945 gint
3946 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3947 {
3948   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3949   return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3950 }
3951
3952 /**
3953  * gtk_calendar_freeze:
3954  * @calendar: a #GtkCalendar
3955  * 
3956  * Does nothing. Previously locked the display of the calendar until
3957  * it was thawed with gtk_calendar_thaw().
3958  *
3959  * Deprecated: 2.8: 
3960  **/
3961 void
3962 gtk_calendar_freeze (GtkCalendar *calendar)
3963 {
3964   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3965 }
3966
3967 /**
3968  * gtk_calendar_thaw:
3969  * @calendar: a #GtkCalendar
3970  * 
3971  * Does nothing. Previously defrosted a calendar; all the changes made
3972  * since the last gtk_calendar_freeze() were displayed.
3973  *
3974  * Deprecated: 2.8: 
3975  **/
3976 void
3977 gtk_calendar_thaw (GtkCalendar *calendar)
3978 {
3979   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3980 }
3981
3982 #define __GTK_CALENDAR_C__
3983 #include "gtkaliasdef.c"