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