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