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