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