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