]> Pileus Git - ~andy/gtk/blob - gtk/gtkcalendar.c
31b14c91d4ff021a70af38851e9b51da436e432c
[~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   GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
1661 }
1662
1663 static gchar*
1664 gtk_calendar_get_detail (GtkCalendar *calendar,
1665                          gint         row,
1666                          gint         column)
1667 {
1668   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
1669   gint year, month;
1670
1671   year = calendar->year;
1672   month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
1673
1674   if (month < 0)
1675     {
1676       month += 12;
1677       year -= 1;
1678     }
1679   else if (month > 11)
1680     {
1681       month -= 12;
1682       year += 1;
1683     }
1684
1685   return priv->detail_func (calendar,
1686                             year, month,
1687                             calendar->day[row][column],
1688                             priv->detail_func_user_data);
1689 }
1690
1691 static gboolean
1692 gtk_calendar_query_tooltip (GtkWidget  *widget,
1693                             gint        x,
1694                             gint        y,
1695                             gboolean    keyboard_mode,
1696                             GtkTooltip *tooltip)
1697 {
1698   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1699   GtkCalendar *calendar = GTK_CALENDAR (widget);
1700   gchar *detail = NULL;
1701   GdkRectangle day_rect;
1702
1703   if (priv->main_win)
1704     {
1705       gint x0, y0, row, col;
1706
1707       gdk_window_get_position (priv->main_win, &x0, &y0);
1708       col = calendar_column_from_x (calendar, x - x0);
1709       row = calendar_row_from_y (calendar, y - y0);
1710
1711       if (col != -1 && row != -1 &&
1712           (0 != (priv->detail_overflow[row] & (1 << col)) ||
1713            0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
1714         {
1715           detail = gtk_calendar_get_detail (calendar, row, col);
1716           calendar_day_rectangle (calendar, row, col, &day_rect);
1717
1718           day_rect.x += x0;
1719           day_rect.y += y0;
1720         }
1721     }
1722
1723   if (detail)
1724     {
1725       gtk_tooltip_set_tip_area (tooltip, &day_rect);
1726       gtk_tooltip_set_markup (tooltip, detail);
1727
1728       g_free (detail);
1729
1730       return TRUE;
1731     }
1732
1733   if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
1734     return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
1735
1736   return FALSE;
1737 }
1738
1739 \f
1740 /****************************************
1741  *       Size Request and Allocate      *
1742  ****************************************/
1743
1744 static void
1745 gtk_calendar_size_request (GtkWidget      *widget,
1746                            GtkRequisition *requisition)
1747 {
1748   GtkCalendar *calendar = GTK_CALENDAR (widget);
1749   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1750   PangoLayout *layout;
1751   PangoRectangle logical_rect;
1752
1753   gint height;
1754   gint i, r, c;
1755   gint calendar_margin = CALENDAR_MARGIN;
1756   gint header_width, main_width;
1757   gint max_header_height = 0;
1758   gint focus_width;
1759   gint focus_padding;
1760   gint max_detail_height;
1761
1762   gtk_widget_style_get (GTK_WIDGET (widget),
1763                         "focus-line-width", &focus_width,
1764                         "focus-padding", &focus_padding,
1765                         NULL);
1766
1767   layout = gtk_widget_create_pango_layout (widget, NULL);
1768   
1769   /*
1770    * Calculate the requisition  width for the widget.
1771    */
1772   
1773   /* Header width */
1774   
1775   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1776     {
1777       priv->max_month_width = 0;
1778       for (i = 0; i < 12; i++)
1779         {
1780           pango_layout_set_text (layout, default_monthname[i], -1);
1781           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1782           priv->max_month_width = MAX (priv->max_month_width,
1783                                                logical_rect.width + 8);
1784           max_header_height = MAX (max_header_height, logical_rect.height); 
1785         }
1786
1787       priv->max_year_width = 0;
1788       /* Translators:  This is a text measurement template.
1789        * Translate it to the widest year text. 
1790        * 
1791        * Don't include the prefix "year measurement template|" 
1792        * in the translation.
1793        *
1794        * If you don't understand this, leave it as "2000"
1795        */
1796       pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);   
1797       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1798       priv->max_year_width = MAX (priv->max_year_width,
1799                                   logical_rect.width + 8);
1800       max_header_height = MAX (max_header_height, logical_rect.height); 
1801     } 
1802   else 
1803     {
1804       priv->max_month_width = 0;
1805       priv->max_year_width = 0;
1806     }
1807   
1808   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
1809     header_width = (priv->max_month_width 
1810                     + priv->max_year_width
1811                     + 3 * 3);
1812   else
1813     header_width = (priv->max_month_width 
1814                     + priv->max_year_width
1815                     + 4 * priv->arrow_width + 3 * 3);
1816
1817   /* Mainwindow labels width */
1818   
1819   priv->max_day_char_width = 0;
1820   priv->max_day_char_ascent = 0;
1821   priv->max_day_char_descent = 0;
1822   priv->min_day_width = 0;
1823
1824   for (i = 0; i < 9; i++)
1825     {
1826       gchar buffer[32];
1827       g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), i * 11);
1828       pango_layout_set_text (layout, buffer, -1);         
1829       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1830       priv->min_day_width = MAX (priv->min_day_width,
1831                                          logical_rect.width);
1832
1833       priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
1834                                                PANGO_ASCENT (logical_rect));
1835       priv->max_day_char_descent = MAX (priv->max_day_char_descent, 
1836                                                 PANGO_DESCENT (logical_rect));
1837     }
1838   
1839   priv->max_label_char_ascent = 0;
1840   priv->max_label_char_descent = 0;
1841   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1842     for (i = 0; i < 7; i++)
1843       {
1844         pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
1845         pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
1846
1847         priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1848         priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
1849                                                    PANGO_ASCENT (logical_rect));
1850         priv->max_label_char_descent = MAX (priv->max_label_char_descent, 
1851                                                     PANGO_DESCENT (logical_rect));
1852       }
1853   
1854   priv->max_week_char_width = 0;
1855   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1856     for (i = 0; i < 9; i++)
1857       {
1858         gchar buffer[32];
1859         g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), i * 11);
1860         pango_layout_set_text (layout, buffer, -1);       
1861         pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1862         priv->max_week_char_width = MAX (priv->max_week_char_width,
1863                                            logical_rect.width / 2);
1864       }
1865   
1866   /* Calculate detail extents. Do this as late as possible since
1867    * pango_layout_set_markup is called which alters font settings. */
1868   max_detail_height = 0;
1869
1870   if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
1871     {
1872       gchar *markup, *tail;
1873
1874       if (priv->detail_width_chars || priv->detail_height_rows)
1875         {
1876           gint rows = MAX (1, priv->detail_height_rows) - 1;
1877           gsize len = priv->detail_width_chars + rows + 16;
1878
1879           markup = tail = g_alloca (len);
1880
1881           memcpy (tail,     "<small>", 7);
1882           tail += 7;
1883
1884           memset (tail, 'm', priv->detail_width_chars);
1885           tail += priv->detail_width_chars;
1886
1887           memset (tail, '\n', rows);
1888           tail += rows;
1889
1890           memcpy (tail,     "</small>", 9);
1891           tail += 9;
1892
1893           g_assert (len == (tail - markup));
1894
1895           pango_layout_set_markup (layout, markup, -1);
1896           pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1897
1898           if (priv->detail_width_chars)
1899             priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1900           if (priv->detail_height_rows)
1901             max_detail_height = MAX (max_detail_height, logical_rect.height);
1902         }
1903
1904       if (!priv->detail_width_chars || !priv->detail_height_rows)
1905         for (r = 0; r < 6; r++)
1906           for (c = 0; c < 7; c++)
1907             {
1908               gchar *detail = gtk_calendar_get_detail (calendar, r, c);
1909
1910               if (detail)
1911                 {
1912                   markup = g_strconcat ("<small>", detail, "</small>", NULL);
1913                   pango_layout_set_markup (layout, markup, -1);
1914
1915                   if (priv->detail_width_chars)
1916                     {
1917                       pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
1918                       pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
1919                     }
1920
1921                   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
1922
1923                   if (!priv->detail_width_chars)
1924                     priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
1925                   if (!priv->detail_height_rows)
1926                     max_detail_height = MAX (max_detail_height, logical_rect.height);
1927
1928                   g_free (markup);
1929                   g_free (detail);
1930                 }
1931             }
1932     }
1933
1934   /* We add one to max_day_char_width to be able to make the marked day "bold" */
1935   priv->max_day_char_width = priv->min_day_width / 2 + 1;
1936
1937   main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
1938                 + (priv->max_week_char_width
1939                    ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
1940                    : 0));
1941   
1942   
1943   requisition->width = MAX (header_width, main_width + INNER_BORDER * 2) + widget->style->xthickness * 2;
1944   
1945   /*
1946    * Calculate the requisition height for the widget.
1947    */
1948   
1949   if (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING)
1950     {
1951       priv->header_h = (max_header_height + CALENDAR_YSEP * 2);
1952     }
1953   else
1954     {
1955       priv->header_h = 0;
1956     }
1957   
1958   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
1959     {
1960       priv->day_name_h = (priv->max_label_char_ascent
1961                                   + priv->max_label_char_descent
1962                                   + 2 * (focus_padding + focus_width) + calendar_margin);
1963       calendar_margin = CALENDAR_YSEP;
1964     } 
1965   else
1966     {
1967       priv->day_name_h = 0;
1968     }
1969
1970   priv->main_h = (CALENDAR_MARGIN + calendar_margin
1971                           + 6 * (priv->max_day_char_ascent
1972                                  + priv->max_day_char_descent 
1973                                  + max_detail_height
1974                                  + 2 * (focus_padding + focus_width))
1975                           + DAY_YSEP * 5);
1976   
1977   height = (priv->header_h + priv->day_name_h 
1978             + priv->main_h);
1979   
1980   requisition->height = height + (widget->style->ythickness + INNER_BORDER) * 2;
1981
1982   g_object_unref (layout);
1983 }
1984
1985 static void
1986 gtk_calendar_size_allocate (GtkWidget     *widget,
1987                             GtkAllocation *allocation)
1988 {
1989   GtkCalendar *calendar = GTK_CALENDAR (widget);
1990   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
1991   gint xthickness = widget->style->xthickness;
1992   gint ythickness = widget->style->xthickness;
1993   guint i;
1994   
1995   widget->allocation = *allocation;
1996     
1997   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
1998     {
1999       priv->day_width = (priv->min_day_width
2000                          * ((allocation->width - (xthickness + INNER_BORDER) * 2
2001                              - (CALENDAR_MARGIN * 2) -  (DAY_XSEP * 6) - CALENDAR_XSEP * 2))
2002                          / (7 * priv->min_day_width + priv->max_week_char_width * 2));
2003       priv->week_width = ((allocation->width - (xthickness + INNER_BORDER) * 2
2004                            - (CALENDAR_MARGIN * 2) - (DAY_XSEP * 6) - CALENDAR_XSEP * 2 )
2005                           - priv->day_width * 7 + CALENDAR_MARGIN + CALENDAR_XSEP);
2006     } 
2007   else 
2008     {
2009       priv->day_width = (allocation->width
2010                          - (xthickness + INNER_BORDER) * 2
2011                          - (CALENDAR_MARGIN * 2)
2012                          - (DAY_XSEP * 6))/7;
2013       priv->week_width = 0;
2014     }
2015   
2016   if (GTK_WIDGET_REALIZED (widget))
2017     {
2018       gdk_window_move_resize (widget->window,
2019                               allocation->x, allocation->y,
2020                               allocation->width, allocation->height);
2021       if (priv->header_win)
2022         gdk_window_move_resize (priv->header_win,
2023                                 xthickness, ythickness,
2024                                 allocation->width - 2 * xthickness, priv->header_h);
2025
2026       for (i = 0 ; i < 4 ; i++)
2027         {
2028           if (priv->arrow_win[i])
2029             {
2030               GdkRectangle rect;
2031               calendar_arrow_rectangle (calendar, i, &rect);
2032           
2033               gdk_window_move_resize (priv->arrow_win[i],
2034                                       rect.x, rect.y, rect.width, rect.height);
2035             }
2036         }
2037       
2038       if (priv->day_name_win)
2039         gdk_window_move_resize (priv->day_name_win,
2040                                 xthickness + INNER_BORDER,
2041                                 priv->header_h + (widget->style->ythickness + INNER_BORDER),
2042                                 allocation->width - (xthickness + INNER_BORDER) * 2,
2043                                 priv->day_name_h);
2044       if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
2045         {
2046           if (priv->week_win)
2047             gdk_window_move_resize (priv->week_win,
2048                                     (xthickness + INNER_BORDER),
2049                                     priv->header_h + priv->day_name_h
2050                                     + (widget->style->ythickness + INNER_BORDER),
2051                                     priv->week_width,
2052                                     priv->main_h);
2053           gdk_window_move_resize (priv->main_win,
2054                                   priv->week_width + (xthickness + INNER_BORDER),
2055                                   priv->header_h + priv->day_name_h
2056                                   + (widget->style->ythickness + INNER_BORDER),
2057                                   allocation->width 
2058                                   - priv->week_width 
2059                                   - (xthickness + INNER_BORDER) * 2,
2060                                   priv->main_h);
2061         }
2062       else 
2063         {
2064           gdk_window_move_resize (priv->main_win,
2065                                   (xthickness + INNER_BORDER),
2066                                   priv->header_h + priv->day_name_h
2067                                   + (widget->style->ythickness + INNER_BORDER),
2068                                   allocation->width 
2069                                   - priv->week_width 
2070                                   - (xthickness + INNER_BORDER) * 2,
2071                                   priv->main_h);
2072           if (priv->week_win)
2073             gdk_window_move_resize (priv->week_win,
2074                                     allocation->width 
2075                                     - priv->week_width 
2076                                     - (xthickness + INNER_BORDER),
2077                                     priv->header_h + priv->day_name_h
2078                                     + (widget->style->ythickness + INNER_BORDER),
2079                                     priv->week_width,
2080                                     priv->main_h);
2081         }
2082     }
2083 }
2084
2085 \f
2086 /****************************************
2087  *              Repainting              *
2088  ****************************************/
2089
2090 static void
2091 calendar_paint_header (GtkCalendar *calendar)
2092 {
2093   GtkWidget *widget = GTK_WIDGET (calendar);
2094   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2095   cairo_t *cr;
2096   char buffer[255];
2097   int x, y;
2098   gint header_width;
2099   gint max_month_width;
2100   gint max_year_width;
2101   PangoLayout *layout;
2102   PangoRectangle logical_rect;
2103   gboolean year_left;
2104   time_t tmp_time;
2105   struct tm *tm;
2106   gchar *str;
2107
2108   if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
2109     year_left = priv->year_before;
2110   else
2111     year_left = !priv->year_before;
2112
2113   cr = gdk_cairo_create (priv->header_win);
2114   
2115   header_width = widget->allocation.width - 2 * widget->style->xthickness;
2116   
2117   max_month_width = priv->max_month_width;
2118   max_year_width = priv->max_year_width;
2119   
2120   gtk_paint_shadow (widget->style, priv->header_win,
2121                     GTK_STATE_NORMAL, GTK_SHADOW_OUT,
2122                     NULL, widget, "calendar",
2123                     0, 0, header_width, priv->header_h);
2124
2125   tmp_time = 1;  /* Jan 1 1970, 00:00:01 UTC */
2126   tm = gmtime (&tmp_time);
2127   tm->tm_year = calendar->year - 1900;
2128
2129   /* Translators: This dictates how the year is displayed in
2130    * gtkcalendar widget.  See strftime() manual for the format.
2131    * Use only ASCII in the translation.
2132    *
2133    * Also look for the msgid "year measurement template|2000".  
2134    * Translate that entry to a year with the widest output of this
2135    * msgid. 
2136    * 
2137    * Don't include the prefix "calendar year format|" in the 
2138    * translation. "%Y" is appropriate for most locales.
2139    */
2140   strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
2141   str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
2142   layout = gtk_widget_create_pango_layout (widget, str);
2143   g_free (str);
2144   
2145   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2146   
2147   /* Draw title */
2148   y = (priv->header_h - logical_rect.height) / 2;
2149   
2150   /* Draw year and its arrows */
2151   
2152   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2153     if (year_left)
2154       x = 3 + (max_year_width - logical_rect.width)/2;
2155     else
2156       x = header_width - (3 + max_year_width
2157                           - (max_year_width - logical_rect.width)/2);
2158   else
2159     if (year_left)
2160       x = 3 + priv->arrow_width + (max_year_width - logical_rect.width)/2;
2161     else
2162       x = header_width - (3 + priv->arrow_width + max_year_width
2163                           - (max_year_width - logical_rect.width)/2);
2164   
2165
2166   gdk_cairo_set_source_color (cr, HEADER_FG_COLOR (GTK_WIDGET (calendar)));
2167   cairo_move_to (cr, x, y);
2168   pango_cairo_show_layout (cr, layout);
2169   
2170   /* Draw month */
2171   g_snprintf (buffer, sizeof (buffer), "%s", default_monthname[calendar->month]);
2172   pango_layout_set_text (layout, buffer, -1);
2173   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2174
2175   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
2176     if (year_left)
2177       x = header_width - (3 + max_month_width
2178                           - (max_month_width - logical_rect.width)/2);      
2179     else
2180     x = 3 + (max_month_width - logical_rect.width) / 2;
2181   else
2182     if (year_left)
2183       x = header_width - (3 + priv->arrow_width + max_month_width
2184                           - (max_month_width - logical_rect.width)/2);
2185     else
2186     x = 3 + priv->arrow_width + (max_month_width - logical_rect.width)/2;
2187
2188   cairo_move_to (cr, x, y);
2189   pango_cairo_show_layout (cr, layout);
2190
2191   g_object_unref (layout);
2192   cairo_destroy (cr);
2193 }
2194
2195 static void
2196 calendar_paint_day_names (GtkCalendar *calendar)
2197 {
2198   GtkWidget *widget = GTK_WIDGET (calendar);
2199   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2200   cairo_t *cr;
2201   char buffer[255];
2202   int day,i;
2203   int day_width, cal_width;
2204   int day_wid_sep;
2205   PangoLayout *layout;
2206   PangoRectangle logical_rect;
2207   gint focus_padding;
2208   gint focus_width;
2209   
2210   cr = gdk_cairo_create (priv->day_name_win);
2211   
2212   gtk_widget_style_get (GTK_WIDGET (widget),
2213                         "focus-line-width", &focus_width,
2214                         "focus-padding", &focus_padding,
2215                         NULL);
2216   
2217   day_width = priv->day_width;
2218   cal_width = widget->allocation.width;
2219   day_wid_sep = day_width + DAY_XSEP;
2220   
2221   /*
2222    * Draw rectangles as inverted background for the labels.
2223    */
2224
2225   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2226   cairo_rectangle (cr,
2227                    CALENDAR_MARGIN, CALENDAR_MARGIN,
2228                    cal_width-CALENDAR_MARGIN * 2,
2229                    priv->day_name_h - CALENDAR_MARGIN);
2230   cairo_fill (cr);
2231   
2232   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
2233     {
2234       cairo_rectangle (cr, 
2235                        CALENDAR_MARGIN,
2236                        priv->day_name_h - CALENDAR_YSEP,
2237                        priv->week_width - CALENDAR_YSEP - CALENDAR_MARGIN,
2238                        CALENDAR_YSEP);
2239       cairo_fill (cr);
2240     }
2241   
2242   /*
2243    * Write the labels
2244    */
2245
2246   layout = gtk_widget_create_pango_layout (widget, NULL);
2247
2248   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2249   for (i = 0; i < 7; i++)
2250     {
2251       if (gtk_widget_get_direction (GTK_WIDGET (calendar)) == GTK_TEXT_DIR_RTL)
2252         day = 6 - i;
2253       else
2254         day = i;
2255       day = (day + priv->week_start) % 7;
2256       g_snprintf (buffer, sizeof (buffer), "%s", default_abbreviated_dayname[day]);
2257
2258       pango_layout_set_text (layout, buffer, -1);
2259       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2260
2261       cairo_move_to (cr, 
2262                      (CALENDAR_MARGIN +
2263                       + (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR ?
2264                          (priv->week_width + (priv->week_width ? CALENDAR_XSEP : 0))
2265                          : 0)
2266                       + day_wid_sep * i
2267                       + (day_width - logical_rect.width)/2),
2268                      CALENDAR_MARGIN + focus_width + focus_padding + logical_rect.y);
2269       pango_cairo_show_layout (cr, layout);
2270     }
2271   
2272   g_object_unref (layout);
2273   cairo_destroy (cr);
2274 }
2275
2276 static void
2277 calendar_paint_week_numbers (GtkCalendar *calendar)
2278 {
2279   GtkWidget *widget = GTK_WIDGET (calendar);
2280   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2281   cairo_t *cr;
2282
2283   guint week = 0, year;
2284   gint row, x_loc, y_loc;
2285   gint day_height;
2286   char buffer[32];
2287   PangoLayout *layout;
2288   PangoRectangle logical_rect;
2289   gint focus_padding;
2290   gint focus_width;
2291   
2292   cr = gdk_cairo_create (priv->week_win);
2293   
2294   gtk_widget_style_get (GTK_WIDGET (widget),
2295                         "focus-line-width", &focus_width,
2296                         "focus-padding", &focus_padding,
2297                         NULL);
2298   
2299   /*
2300    * Draw a rectangle as inverted background for the labels.
2301    */
2302
2303   gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2304   if (priv->day_name_win)
2305     cairo_rectangle (cr, 
2306                      CALENDAR_MARGIN,
2307                      0,
2308                      priv->week_width - CALENDAR_MARGIN,
2309                      priv->main_h - CALENDAR_MARGIN);
2310   else
2311     cairo_rectangle (cr,
2312                      CALENDAR_MARGIN,
2313                      CALENDAR_MARGIN,
2314                      priv->week_width - CALENDAR_MARGIN,
2315                      priv->main_h - 2 * CALENDAR_MARGIN);
2316   cairo_fill (cr);
2317   
2318   /*
2319    * Write the labels
2320    */
2321   
2322   layout = gtk_widget_create_pango_layout (widget, NULL);
2323   
2324   gdk_cairo_set_source_color (cr, SELECTED_FG_COLOR (widget));
2325   day_height = calendar_row_height (calendar);
2326   for (row = 0; row < 6; row++)
2327     {
2328       gboolean result;
2329       
2330       year = calendar->year;
2331       if (calendar->day[row][6] < 15 && row > 3 && calendar->month == 11)
2332         year++;
2333
2334       result = week_of_year (&week, &year,              
2335                              ((calendar->day[row][6] < 15 && row > 3 ? 1 : 0)
2336                               + calendar->month) % 12 + 1, calendar->day[row][6]);
2337       g_return_if_fail (result);
2338
2339       /* Translators: this defines whether the week numbers should use
2340        * localized digits or the ones used in English (0123...).
2341        *
2342        * Translate to "%Id" if you want to use localized digits, or
2343        * translate to "%d" otherwise.  Don't include the
2344        * "calendar:week:digits|" part in the translation.
2345        *
2346        * Note that translating this doesn't guarantee that you get localized
2347        * digits.  That needs support from your system and locale definition
2348        * too.
2349        */
2350       g_snprintf (buffer, sizeof (buffer), Q_("calendar:week:digits|%d"), week);
2351       pango_layout_set_text (layout, buffer, -1);
2352       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2353
2354       y_loc = calendar_top_y_for_row (calendar, row) + (day_height - logical_rect.height) / 2;
2355
2356       x_loc = (priv->week_width
2357                - logical_rect.width
2358                - CALENDAR_XSEP - focus_padding - focus_width);
2359
2360       cairo_move_to (cr, x_loc, y_loc);
2361       pango_cairo_show_layout (cr, layout);
2362     }
2363   
2364   g_object_unref (layout);
2365   cairo_destroy (cr);
2366 }
2367
2368 static void
2369 calendar_invalidate_day_num (GtkCalendar *calendar,
2370                              gint         day)
2371 {
2372   gint r, c, row, col;
2373   
2374   row = -1;
2375   col = -1;
2376   for (r = 0; r < 6; r++)
2377     for (c = 0; c < 7; c++)
2378       if (calendar->day_month[r][c] == MONTH_CURRENT &&
2379           calendar->day[r][c] == day)
2380         {
2381           row = r;
2382           col = c;
2383         }
2384   
2385   g_return_if_fail (row != -1);
2386   g_return_if_fail (col != -1);
2387   
2388   calendar_invalidate_day (calendar, row, col);
2389 }
2390
2391 static void
2392 calendar_invalidate_day (GtkCalendar *calendar,
2393                          gint         row,
2394                          gint         col)
2395 {
2396   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2397
2398   if (priv->main_win)
2399     {
2400       GdkRectangle day_rect;
2401       
2402       calendar_day_rectangle (calendar, row, col, &day_rect);
2403       gdk_window_invalidate_rect (priv->main_win, &day_rect, FALSE);
2404     }
2405 }
2406
2407 static gboolean
2408 is_color_attribute (PangoAttribute *attribute,
2409                     gpointer        data)
2410 {
2411   return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
2412           attribute->klass->type == PANGO_ATTR_BACKGROUND);
2413 }
2414
2415 static void
2416 calendar_paint_day (GtkCalendar *calendar,
2417                     gint             row,
2418                     gint             col)
2419 {
2420   GtkWidget *widget = GTK_WIDGET (calendar);
2421   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2422   cairo_t *cr;
2423   GdkColor *text_color;
2424   gchar *detail;
2425   gchar buffer[32];
2426   gint day;
2427   gint x_loc, y_loc;
2428   GdkRectangle day_rect;
2429
2430   PangoLayout *layout;
2431   PangoRectangle logical_rect;
2432   gboolean overflow = FALSE;
2433   gboolean show_details;
2434
2435   g_return_if_fail (row < 6);
2436   g_return_if_fail (col < 7);
2437
2438   cr = gdk_cairo_create (priv->main_win);
2439
2440   day = calendar->day[row][col];
2441   show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
2442
2443   calendar_day_rectangle (calendar, row, col, &day_rect);
2444   
2445   if (calendar->day_month[row][col] == MONTH_PREV)
2446     {
2447       text_color = PREV_MONTH_COLOR (widget);
2448     } 
2449   else if (calendar->day_month[row][col] == MONTH_NEXT)
2450     {
2451       text_color =  NEXT_MONTH_COLOR (widget);
2452     } 
2453   else 
2454     {
2455 #if 0      
2456       if (calendar->highlight_row == row && calendar->highlight_col == col)
2457         {
2458           cairo_set_source_color (cr, HIGHLIGHT_BG_COLOR (widget));
2459           gdk_cairo_rectangle (cr, &day_rect);
2460           cairo_fill (cr);
2461         }
2462 #endif     
2463       if (calendar->selected_day == day)
2464         {
2465           gdk_cairo_set_source_color (cr, SELECTED_BG_COLOR (widget));
2466           gdk_cairo_rectangle (cr, &day_rect);
2467           cairo_fill (cr);
2468         }
2469       if (calendar->selected_day == day)
2470         text_color = SELECTED_FG_COLOR (widget);
2471       else if (calendar->marked_date[day-1])
2472         text_color = MARKED_COLOR (widget);
2473       else
2474         text_color = NORMAL_DAY_COLOR (widget);
2475     }
2476
2477   /* Translators: this defines whether the day numbers should use
2478    * localized digits or the ones used in English (0123...).
2479    *
2480    * Translate to "%Id" if you want to use localized digits, or
2481    * translate to "%d" otherwise.  Don't include the "calendar:day:digits|"
2482    * part in the translation.
2483    *
2484    * Note that translating this doesn't guarantee that you get localized
2485    * digits.  That needs support from your system and locale definition
2486    * too.
2487    */
2488   g_snprintf (buffer, sizeof (buffer), Q_("calendar:day:digits|%d"), day);
2489
2490   /* Get extra information to show, if any: */
2491
2492   if (priv->detail_func)
2493     detail = gtk_calendar_get_detail (calendar, row, col);
2494   else
2495     detail = NULL;
2496
2497   layout = gtk_widget_create_pango_layout (widget, buffer);
2498   pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
2499   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
2500   
2501   x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
2502   y_loc = day_rect.y;
2503
2504   gdk_cairo_set_source_color (cr, text_color);
2505   cairo_move_to (cr, x_loc, y_loc);
2506   pango_cairo_show_layout (cr, layout);
2507
2508   if (calendar->day_month[row][col] == MONTH_CURRENT &&
2509      (calendar->marked_date[day-1] || (detail && !show_details)))
2510     {
2511       cairo_move_to (cr, x_loc - 1, y_loc);
2512       pango_cairo_show_layout (cr, layout);
2513     }
2514
2515   y_loc += priv->max_day_char_descent;
2516
2517   if (priv->detail_func && show_details)
2518     {
2519       cairo_save (cr);
2520
2521       if (calendar->selected_day == day)
2522         gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
2523       else if (calendar->day_month[row][col] == MONTH_CURRENT)
2524         gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
2525       else
2526         gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
2527
2528       cairo_set_line_width (cr, 1);
2529       cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
2530       cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
2531       cairo_stroke (cr);
2532
2533       cairo_restore (cr);
2534
2535       y_loc += 2;
2536     }
2537
2538   if (detail && show_details)
2539     {
2540       gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
2541       pango_layout_set_markup (layout, markup, -1);
2542       g_free (markup);
2543
2544       if (day == calendar->selected_day)
2545         {
2546           /* Stripping colors as they conflict with selection marking. */
2547
2548           PangoAttrList *attrs = pango_layout_get_attributes (layout);
2549           PangoAttrList *colors = NULL;
2550
2551           if (attrs)
2552             colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
2553           if (colors)
2554             pango_attr_list_unref (colors);
2555         }
2556
2557       pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
2558       pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
2559
2560       if (priv->detail_height_rows)
2561         {
2562           gint dy = day_rect.height - (y_loc - day_rect.y);
2563           pango_layout_set_height (layout, PANGO_SCALE * dy);
2564           pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
2565         }
2566
2567       cairo_move_to (cr, day_rect.x, y_loc);
2568       pango_cairo_show_layout (cr, layout);
2569     }
2570
2571   if (GTK_WIDGET_HAS_FOCUS (calendar) 
2572       && calendar->focus_row == row && calendar->focus_col == col)
2573     {
2574       GtkStateType state;
2575
2576       if (calendar->selected_day == day)
2577         state = GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE;
2578       else
2579         state = GTK_STATE_NORMAL;
2580       
2581       gtk_paint_focus (widget->style, 
2582                        priv->main_win,
2583                        state,
2584                        NULL, widget, "calendar-day",
2585                        day_rect.x,     day_rect.y, 
2586                        day_rect.width, day_rect.height);
2587     }
2588
2589   if (overflow)
2590     priv->detail_overflow[row] |= (1 << col);
2591   else
2592     priv->detail_overflow[row] &= ~(1 << col);
2593
2594   g_object_unref (layout);
2595   cairo_destroy (cr);
2596   g_free (detail);
2597 }
2598
2599 static void
2600 calendar_paint_main (GtkCalendar *calendar)
2601 {
2602   gint row, col;
2603   
2604   for (col = 0; col < 7; col++)
2605     for (row = 0; row < 6; row++)
2606       calendar_paint_day (calendar, row, col);
2607 }
2608
2609 static void
2610 calendar_invalidate_arrow (GtkCalendar *calendar,
2611                            guint        arrow)
2612 {
2613   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2614   GdkWindow *window;
2615   
2616   window = priv->arrow_win[arrow];
2617   if (window)
2618     gdk_window_invalidate_rect (window, NULL, FALSE);
2619 }
2620
2621 static void
2622 calendar_paint_arrow (GtkCalendar *calendar,
2623                       guint            arrow)
2624 {
2625   GtkWidget *widget = GTK_WIDGET (calendar);
2626   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2627   GdkWindow *window;
2628   
2629   window = priv->arrow_win[arrow];
2630   if (window)
2631     {
2632       cairo_t *cr = gdk_cairo_create (window);
2633       gint width, height;
2634       gint state;
2635         
2636       state = priv->arrow_state[arrow];
2637
2638       gdk_cairo_set_source_color (cr, &widget->style->bg[state]);
2639       cairo_paint (cr);
2640       cairo_destroy (cr);
2641       
2642       gdk_drawable_get_size (window, &width, &height);
2643       if (arrow == ARROW_MONTH_LEFT || arrow == ARROW_YEAR_LEFT)
2644         gtk_paint_arrow (widget->style, window, state, 
2645                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2646                          GTK_ARROW_LEFT, TRUE, 
2647                          width/2 - 3, height/2 - 4, 8, 8);
2648       else 
2649         gtk_paint_arrow (widget->style, window, state, 
2650                          GTK_SHADOW_OUT, NULL, widget, "calendar",
2651                          GTK_ARROW_RIGHT, TRUE, 
2652                          width/2 - 4, height/2 - 4, 8, 8);
2653     }
2654 }
2655
2656 static gboolean
2657 gtk_calendar_expose (GtkWidget      *widget,
2658                      GdkEventExpose *event)
2659 {
2660   GtkCalendar *calendar = GTK_CALENDAR (widget);
2661   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2662   int i;
2663
2664   if (GTK_WIDGET_DRAWABLE (widget))
2665     {
2666       if (event->window == priv->main_win)
2667         calendar_paint_main (calendar);
2668       
2669       if (event->window == priv->header_win)
2670         calendar_paint_header (calendar);
2671
2672       for (i = 0; i < 4; i++)
2673         if (event->window == priv->arrow_win[i])
2674           calendar_paint_arrow (calendar, i);
2675       
2676       if (event->window == priv->day_name_win)
2677         calendar_paint_day_names (calendar);
2678       
2679       if (event->window == priv->week_win)
2680         calendar_paint_week_numbers (calendar);
2681       if (event->window == widget->window)
2682         {
2683           gtk_paint_shadow (widget->style, widget->window, GTK_WIDGET_STATE (widget),
2684                             GTK_SHADOW_IN, NULL, widget, "calendar",
2685                             0, 0, widget->allocation.width, widget->allocation.height);
2686         }
2687     }
2688   
2689   return FALSE;
2690 }
2691
2692 \f
2693 /****************************************
2694  *           Mouse handling             *
2695  ****************************************/
2696
2697 static void
2698 calendar_arrow_action (GtkCalendar *calendar,
2699                        guint        arrow)
2700 {
2701   switch (arrow)
2702     {
2703     case ARROW_YEAR_LEFT:
2704       calendar_set_year_prev (calendar);
2705       break;
2706     case ARROW_YEAR_RIGHT:
2707       calendar_set_year_next (calendar);
2708       break;
2709     case ARROW_MONTH_LEFT:
2710       calendar_set_month_prev (calendar);
2711       break;
2712     case ARROW_MONTH_RIGHT:
2713       calendar_set_month_next (calendar);
2714       break;
2715     default:;
2716       /* do nothing */
2717     }
2718 }
2719
2720 static gboolean
2721 calendar_timer (gpointer data)
2722 {
2723   GtkCalendar *calendar = data;
2724   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2725   gboolean retval = FALSE;
2726   
2727   if (priv->timer)
2728     {
2729       calendar_arrow_action (calendar, priv->click_child);
2730
2731       if (priv->need_timer)
2732         {
2733           GtkSettings *settings;
2734           guint        timeout;
2735
2736           settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2737           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
2738
2739           priv->need_timer = FALSE;
2740           priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2741                                             timeout * SCROLL_DELAY_FACTOR,
2742                                             (GSourceFunc) calendar_timer,
2743                                             (gpointer) calendar, NULL);
2744         }
2745       else 
2746         retval = TRUE;
2747     }
2748
2749   return retval;
2750 }
2751
2752 static void
2753 calendar_start_spinning (GtkCalendar *calendar,
2754                          gint         click_child)
2755 {
2756   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2757
2758   priv->click_child = click_child;
2759   
2760   if (!priv->timer)
2761     {
2762       GtkSettings *settings;
2763       guint        timeout;
2764
2765       settings = gtk_widget_get_settings (GTK_WIDGET (calendar));
2766       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
2767
2768       priv->need_timer = TRUE;
2769       priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
2770                                         timeout,
2771                                         (GSourceFunc) calendar_timer,
2772                                         (gpointer) calendar, NULL);
2773     }
2774 }
2775
2776 static void
2777 calendar_stop_spinning (GtkCalendar *calendar)
2778 {
2779   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2780
2781   if (priv->timer)
2782     {
2783       g_source_remove (priv->timer);
2784       priv->timer = 0;
2785       priv->need_timer = FALSE;
2786     }
2787 }
2788
2789 static void
2790 calendar_main_button_press (GtkCalendar    *calendar,
2791                             GdkEventButton *event)
2792 {
2793   GtkWidget *widget = GTK_WIDGET (calendar);
2794   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
2795   gint x, y;
2796   gint row, col;
2797   gint day_month;
2798   gint day;
2799   
2800   x = (gint) (event->x);
2801   y = (gint) (event->y);
2802   
2803   row = calendar_row_from_y (calendar, y);
2804   col = calendar_column_from_x (calendar, x);
2805
2806   /* If row or column isn't found, just return. */
2807   if (row == -1 || col == -1)
2808     return;
2809   
2810   day_month = calendar->day_month[row][col];
2811
2812   if (event->type == GDK_BUTTON_PRESS)
2813     {
2814       day = calendar->day[row][col];
2815       
2816       if (day_month == MONTH_PREV)
2817         calendar_set_month_prev (calendar);
2818       else if (day_month == MONTH_NEXT)
2819         calendar_set_month_next (calendar);
2820       
2821       if (!GTK_WIDGET_HAS_FOCUS (widget))
2822         gtk_widget_grab_focus (widget);
2823           
2824       if (event->button == 1) 
2825         {
2826           priv->in_drag = 1;
2827           priv->drag_start_x = x;
2828           priv->drag_start_y = y;
2829         }
2830
2831       calendar_select_and_focus_day (calendar, day);
2832     }
2833   else if (event->type == GDK_2BUTTON_PRESS)
2834     {
2835       priv->in_drag = 0;
2836       if (day_month == MONTH_CURRENT)
2837         g_signal_emit (calendar,
2838                        gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL],
2839                        0);
2840     }
2841 }
2842
2843 static gboolean
2844 gtk_calendar_button_press (GtkWidget      *widget,
2845                            GdkEventButton *event)
2846 {
2847   GtkCalendar *calendar = GTK_CALENDAR (widget);
2848   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2849   gint arrow = -1;
2850   
2851   if (event->window == priv->main_win)
2852     calendar_main_button_press (calendar, event);
2853
2854   if (!GTK_WIDGET_HAS_FOCUS (widget))
2855     gtk_widget_grab_focus (widget);
2856
2857   for (arrow = ARROW_YEAR_LEFT; arrow <= ARROW_MONTH_RIGHT; arrow++)
2858     {
2859       if (event->window == priv->arrow_win[arrow])
2860         {
2861           
2862           /* only call the action on single click, not double */
2863           if (event->type == GDK_BUTTON_PRESS)
2864             {
2865               if (event->button == 1)
2866                 calendar_start_spinning (calendar, arrow);
2867
2868               calendar_arrow_action (calendar, arrow);        
2869             }
2870
2871           return TRUE;
2872         }
2873     }
2874
2875   return FALSE;
2876 }
2877
2878 static gboolean
2879 gtk_calendar_button_release (GtkWidget    *widget,
2880                              GdkEventButton *event)
2881 {
2882   GtkCalendar *calendar = GTK_CALENDAR (widget);
2883   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2884
2885   if (event->button == 1) 
2886     {
2887       calendar_stop_spinning (calendar);
2888
2889       if (priv->in_drag)
2890         priv->in_drag = 0;
2891     }
2892
2893   return TRUE;
2894 }
2895
2896 static gboolean
2897 gtk_calendar_motion_notify (GtkWidget      *widget,
2898                             GdkEventMotion *event)
2899 {
2900   GtkCalendar *calendar = GTK_CALENDAR (widget);
2901   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2902   gint event_x, event_y;
2903   gint row, col;
2904   gint old_row, old_col;
2905   
2906   event_x = (gint) (event->x);
2907   event_y = (gint) (event->y);
2908   
2909   if (event->window == priv->main_win)
2910     {
2911       
2912       if (priv->in_drag) 
2913         {
2914           if (gtk_drag_check_threshold (widget,
2915                                         priv->drag_start_x, priv->drag_start_y,
2916                                         event->x, event->y))
2917             {
2918               GdkDragContext *context;
2919               GtkTargetList *target_list = gtk_target_list_new (NULL, 0);
2920               gtk_target_list_add_text_targets (target_list, 0);
2921               context = gtk_drag_begin (widget, target_list, GDK_ACTION_COPY,
2922                                         1, (GdkEvent *)event);
2923
2924           
2925               priv->in_drag = 0;
2926               
2927               gtk_target_list_unref (target_list);
2928               gtk_drag_set_icon_default (context);
2929             }
2930         }
2931       else 
2932         {
2933           row = calendar_row_from_y (calendar, event_y);
2934           col = calendar_column_from_x (calendar, event_x);
2935           
2936           if (row != calendar->highlight_row || calendar->highlight_col != col)
2937             {
2938               old_row = calendar->highlight_row;
2939               old_col = calendar->highlight_col;
2940               if (old_row > -1 && old_col > -1)
2941                 {
2942                   calendar->highlight_row = -1;
2943                   calendar->highlight_col = -1;
2944                   calendar_invalidate_day (calendar, old_row, old_col);
2945                 }
2946               
2947               calendar->highlight_row = row;
2948               calendar->highlight_col = col;
2949               
2950               if (row > -1 && col > -1)
2951                 calendar_invalidate_day (calendar, row, col);
2952             }
2953         }
2954     }
2955   return TRUE;
2956 }
2957
2958 static gboolean
2959 gtk_calendar_enter_notify (GtkWidget        *widget,
2960                            GdkEventCrossing *event)
2961 {
2962   GtkCalendar *calendar = GTK_CALENDAR (widget);
2963   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2964   
2965   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
2966     {
2967       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_PRELIGHT;
2968       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
2969     }
2970   
2971   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
2972     {
2973       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_PRELIGHT;
2974       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
2975     }
2976   
2977   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
2978     {
2979       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_PRELIGHT;
2980       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
2981     }
2982   
2983   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
2984     {
2985       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_PRELIGHT;
2986       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
2987     }
2988   
2989   return TRUE;
2990 }
2991
2992 static gboolean
2993 gtk_calendar_leave_notify (GtkWidget        *widget,
2994                            GdkEventCrossing *event)
2995 {
2996   GtkCalendar *calendar = GTK_CALENDAR (widget);
2997   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
2998   gint row;
2999   gint col;
3000   
3001   if (event->window == priv->main_win)
3002     {
3003       row = calendar->highlight_row;
3004       col = calendar->highlight_col;
3005       calendar->highlight_row = -1;
3006       calendar->highlight_col = -1;
3007       if (row > -1 && col > -1)
3008         calendar_invalidate_day (calendar, row, col);
3009     }
3010   
3011   if (event->window == priv->arrow_win[ARROW_MONTH_LEFT])
3012     {
3013       priv->arrow_state[ARROW_MONTH_LEFT] = GTK_STATE_NORMAL;
3014       calendar_invalidate_arrow (calendar, ARROW_MONTH_LEFT);
3015     }
3016   
3017   if (event->window == priv->arrow_win[ARROW_MONTH_RIGHT])
3018     {
3019       priv->arrow_state[ARROW_MONTH_RIGHT] = GTK_STATE_NORMAL;
3020       calendar_invalidate_arrow (calendar, ARROW_MONTH_RIGHT);
3021     }
3022   
3023   if (event->window == priv->arrow_win[ARROW_YEAR_LEFT])
3024     {
3025       priv->arrow_state[ARROW_YEAR_LEFT] = GTK_STATE_NORMAL;
3026       calendar_invalidate_arrow (calendar, ARROW_YEAR_LEFT);
3027     }
3028   
3029   if (event->window == priv->arrow_win[ARROW_YEAR_RIGHT])
3030     {
3031       priv->arrow_state[ARROW_YEAR_RIGHT] = GTK_STATE_NORMAL;
3032       calendar_invalidate_arrow (calendar, ARROW_YEAR_RIGHT);
3033     }
3034   
3035   return TRUE;
3036 }
3037
3038 static gboolean
3039 gtk_calendar_scroll (GtkWidget      *widget,
3040                      GdkEventScroll *event)
3041 {
3042   GtkCalendar *calendar = GTK_CALENDAR (widget);
3043
3044   if (event->direction == GDK_SCROLL_UP) 
3045     {
3046       if (!GTK_WIDGET_HAS_FOCUS (widget))
3047         gtk_widget_grab_focus (widget);
3048       calendar_set_month_prev (calendar);
3049     }
3050   else if (event->direction == GDK_SCROLL_DOWN) 
3051     {
3052       if (!GTK_WIDGET_HAS_FOCUS (widget))
3053         gtk_widget_grab_focus (widget);
3054       calendar_set_month_next (calendar);
3055     }
3056   else
3057     return FALSE;
3058
3059   return TRUE;
3060 }
3061
3062 \f
3063 /****************************************
3064  *             Key handling              *
3065  ****************************************/
3066
3067 static void 
3068 move_focus (GtkCalendar *calendar, 
3069             gint         direction)
3070 {
3071   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
3072  
3073   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
3074       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
3075     {
3076       if (calendar->focus_col > 0)
3077           calendar->focus_col--;
3078       else if (calendar->focus_row > 0)
3079         {
3080           calendar->focus_col = 6;
3081           calendar->focus_row--;
3082         }
3083
3084       if (calendar->focus_col < 0)
3085         calendar->focus_col = 6;
3086       if (calendar->focus_row < 0)
3087         calendar->focus_row = 5;
3088     }
3089   else 
3090     {
3091       if (calendar->focus_col < 6)
3092         calendar->focus_col++;
3093       else if (calendar->focus_row < 5)
3094         {
3095           calendar->focus_col = 0;
3096           calendar->focus_row++;
3097         }
3098
3099       if (calendar->focus_col < 0)
3100         calendar->focus_col = 0;
3101       if (calendar->focus_row < 0)
3102         calendar->focus_row = 0;
3103     }
3104 }
3105
3106 static gboolean
3107 gtk_calendar_key_press (GtkWidget   *widget,
3108                         GdkEventKey *event)
3109 {
3110   GtkCalendar *calendar;
3111   gint return_val;
3112   gint old_focus_row;
3113   gint old_focus_col;
3114   gint row, col, day;
3115   
3116   calendar = GTK_CALENDAR (widget);
3117   return_val = FALSE;
3118   
3119   old_focus_row = calendar->focus_row;
3120   old_focus_col = calendar->focus_col;
3121
3122   switch (event->keyval)
3123     {
3124     case GDK_KP_Left:
3125     case GDK_Left:
3126       return_val = TRUE;
3127       if (event->state & GDK_CONTROL_MASK)
3128         calendar_set_month_prev (calendar);
3129       else
3130         {
3131           move_focus (calendar, -1);
3132           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3133           calendar_invalidate_day (calendar, calendar->focus_row,
3134                                    calendar->focus_col);
3135         }
3136       break;
3137     case GDK_KP_Right:
3138     case GDK_Right:
3139       return_val = TRUE;
3140       if (event->state & GDK_CONTROL_MASK)
3141         calendar_set_month_next (calendar);
3142       else
3143         {
3144           move_focus (calendar, 1);
3145           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3146           calendar_invalidate_day (calendar, calendar->focus_row,
3147                                    calendar->focus_col);
3148         }
3149       break;
3150     case GDK_KP_Up:
3151     case GDK_Up:
3152       return_val = TRUE;
3153       if (event->state & GDK_CONTROL_MASK)
3154         calendar_set_year_prev (calendar);
3155       else
3156         {
3157           if (calendar->focus_row > 0)
3158             calendar->focus_row--;
3159           if (calendar->focus_row < 0)
3160             calendar->focus_row = 5;
3161           if (calendar->focus_col < 0)
3162             calendar->focus_col = 6;
3163           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3164           calendar_invalidate_day (calendar, calendar->focus_row,
3165                                    calendar->focus_col);
3166         }
3167       break;
3168     case GDK_KP_Down:
3169     case GDK_Down:
3170       return_val = TRUE;
3171       if (event->state & GDK_CONTROL_MASK)
3172         calendar_set_year_next (calendar);
3173       else
3174         {
3175           if (calendar->focus_row < 5)
3176             calendar->focus_row++;
3177           if (calendar->focus_col < 0)
3178             calendar->focus_col = 0;
3179           calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
3180           calendar_invalidate_day (calendar, calendar->focus_row,
3181                                    calendar->focus_col);
3182         }
3183       break;
3184     case GDK_KP_Space:
3185     case GDK_space:
3186       row = calendar->focus_row;
3187       col = calendar->focus_col;
3188       
3189       if (row > -1 && col > -1)
3190         {
3191           return_val = TRUE;
3192
3193           day = calendar->day[row][col];
3194           if (calendar->day_month[row][col] == MONTH_PREV)
3195             calendar_set_month_prev (calendar);
3196           else if (calendar->day_month[row][col] == MONTH_NEXT)
3197             calendar_set_month_next (calendar);
3198
3199           calendar_select_and_focus_day (calendar, day);
3200         }
3201     }   
3202   
3203   return return_val;
3204 }
3205
3206 \f
3207 /****************************************
3208  *           Misc widget methods        *
3209  ****************************************/
3210
3211 static void
3212 calendar_set_background (GtkWidget *widget)
3213 {
3214   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3215   gint i;
3216   
3217   if (GTK_WIDGET_REALIZED (widget))
3218     {
3219       for (i = 0; i < 4; i++)
3220         {
3221           if (priv->arrow_win[i])
3222             gdk_window_set_background (priv->arrow_win[i], 
3223                                        HEADER_BG_COLOR (widget));
3224         }
3225       if (priv->header_win)
3226         gdk_window_set_background (priv->header_win, 
3227                                    HEADER_BG_COLOR (widget));
3228       if (priv->day_name_win)
3229         gdk_window_set_background (priv->day_name_win, 
3230                                    BACKGROUND_COLOR (widget));
3231       if (priv->week_win)
3232         gdk_window_set_background (priv->week_win,
3233                                    BACKGROUND_COLOR (widget));
3234       if (priv->main_win)
3235         gdk_window_set_background (priv->main_win,
3236                                    BACKGROUND_COLOR (widget));
3237       if (widget->window)
3238         gdk_window_set_background (widget->window,
3239                                    BACKGROUND_COLOR (widget)); 
3240     }
3241 }
3242
3243 static void
3244 gtk_calendar_style_set (GtkWidget *widget,
3245                         GtkStyle  *previous_style)
3246 {
3247   if (previous_style && GTK_WIDGET_REALIZED (widget))
3248     calendar_set_background (widget);
3249 }
3250
3251 static void
3252 gtk_calendar_state_changed (GtkWidget      *widget,
3253                             GtkStateType    previous_state)
3254 {
3255   GtkCalendar *calendar = GTK_CALENDAR (widget);
3256   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3257   int i;
3258   
3259   if (!GTK_WIDGET_IS_SENSITIVE (widget))
3260     {
3261       priv->in_drag = 0;
3262       calendar_stop_spinning (calendar);    
3263     }
3264
3265   for (i = 0; i < 4; i++)
3266     if (GTK_WIDGET_IS_SENSITIVE (widget))
3267       priv->arrow_state[i] = GTK_STATE_NORMAL;
3268     else 
3269       priv->arrow_state[i] = GTK_STATE_INSENSITIVE;
3270   
3271   calendar_set_background (widget);
3272 }
3273
3274 static void
3275 gtk_calendar_grab_notify (GtkWidget *widget,
3276                           gboolean   was_grabbed)
3277 {
3278   if (!was_grabbed)
3279     calendar_stop_spinning (GTK_CALENDAR (widget));
3280 }
3281
3282 static gboolean
3283 gtk_calendar_focus_out (GtkWidget     *widget,
3284                         GdkEventFocus *event)
3285 {
3286   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3287   GtkCalendar *calendar = GTK_CALENDAR (widget);
3288
3289   calendar_queue_refresh (calendar);
3290   calendar_stop_spinning (calendar);
3291   
3292   priv->in_drag = 0; 
3293
3294   return FALSE;
3295 }
3296
3297 \f
3298 /****************************************
3299  *          Drag and Drop               *
3300  ****************************************/
3301
3302 static void
3303 gtk_calendar_drag_data_get (GtkWidget        *widget,
3304                             GdkDragContext   *context,
3305                             GtkSelectionData *selection_data,
3306                             guint             info,
3307                             guint             time)
3308 {
3309   GtkCalendar *calendar = GTK_CALENDAR (widget);
3310   GDate *date;
3311   gchar str[128];
3312   gsize len;
3313
3314   date = g_date_new_dmy (calendar->selected_day, calendar->month + 1, calendar->year);
3315   len = g_date_strftime (str, 127, "%x", date);
3316   gtk_selection_data_set_text (selection_data, str, len);
3317   
3318   g_free (date);
3319 }
3320
3321 /* Get/set whether drag_motion requested the drag data and
3322  * drag_data_received should thus not actually insert the data,
3323  * since the data doesn't result from a drop.
3324  */
3325 static void
3326 set_status_pending (GdkDragContext *context,
3327                     GdkDragAction   suggested_action)
3328 {
3329   g_object_set_data (G_OBJECT (context),
3330                      I_("gtk-calendar-status-pending"),
3331                      GINT_TO_POINTER (suggested_action));
3332 }
3333
3334 static GdkDragAction
3335 get_status_pending (GdkDragContext *context)
3336 {
3337   return GPOINTER_TO_INT (g_object_get_data (G_OBJECT (context),
3338                                              "gtk-calendar-status-pending"));
3339 }
3340
3341 static void
3342 gtk_calendar_drag_leave (GtkWidget      *widget,
3343                          GdkDragContext *context,
3344                          guint           time)
3345 {
3346   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3347
3348   priv->drag_highlight = 0;
3349   gtk_drag_unhighlight (widget);
3350   
3351 }
3352
3353 static gboolean
3354 gtk_calendar_drag_motion (GtkWidget      *widget,
3355                           GdkDragContext *context,
3356                           gint            x,
3357                           gint            y,
3358                           guint           time)
3359 {
3360   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
3361   GdkAtom target;
3362   
3363   if (!priv->drag_highlight) 
3364     {
3365       priv->drag_highlight = 1;
3366       gtk_drag_highlight (widget);
3367     }
3368   
3369   target = gtk_drag_dest_find_target (widget, context, NULL);
3370   if (target == GDK_NONE || context->suggested_action == 0)
3371     gdk_drag_status (context, 0, time);
3372   else
3373     {
3374       set_status_pending (context, context->suggested_action);
3375       gtk_drag_get_data (widget, context, target, time);
3376     }
3377   
3378   return TRUE;
3379 }
3380
3381 static gboolean
3382 gtk_calendar_drag_drop (GtkWidget      *widget,
3383                         GdkDragContext *context,
3384                         gint            x,
3385                         gint            y,
3386                         guint           time)
3387 {
3388   GdkAtom target;
3389
3390   target = gtk_drag_dest_find_target (widget, context, NULL);  
3391   if (target != GDK_NONE)
3392     {
3393       gtk_drag_get_data (widget, context, 
3394                          target, 
3395                          time);
3396       return TRUE;
3397     }
3398
3399   return FALSE;
3400 }
3401
3402 static void
3403 gtk_calendar_drag_data_received (GtkWidget        *widget,
3404                                  GdkDragContext   *context,
3405                                  gint              x,
3406                                  gint              y,
3407                                  GtkSelectionData *selection_data,
3408                                  guint             info,
3409                                  guint             time)
3410 {
3411   GtkCalendar *calendar = GTK_CALENDAR (widget);
3412   guint day, month, year;
3413   gchar *str;
3414   GDate *date;
3415   GdkDragAction suggested_action;
3416
3417   suggested_action = get_status_pending (context);
3418
3419   if (suggested_action) 
3420     {
3421       set_status_pending (context, 0);
3422      
3423       /* We are getting this data due to a request in drag_motion,
3424        * rather than due to a request in drag_drop, so we are just
3425        * supposed to call drag_status, not actually paste in the
3426        * data.
3427        */
3428       str = (gchar*) gtk_selection_data_get_text (selection_data);
3429
3430       if (str) 
3431         {
3432           date = g_date_new ();
3433           g_date_set_parse (date, str);
3434           if (!g_date_valid (date)) 
3435               suggested_action = 0;
3436           g_date_free (date);
3437           g_free (str);
3438         }
3439       else
3440         suggested_action = 0;
3441
3442       gdk_drag_status (context, suggested_action, time);
3443
3444       return;
3445     }
3446
3447   date = g_date_new ();
3448   str = (gchar*) gtk_selection_data_get_text (selection_data);
3449   if (str) 
3450     {
3451       g_date_set_parse (date, str);
3452       g_free (str);
3453     }
3454   
3455   if (!g_date_valid (date)) 
3456     {
3457       g_warning ("Received invalid date data\n");
3458       g_date_free (date);       
3459       gtk_drag_finish (context, FALSE, FALSE, time);
3460       return;
3461     }
3462
3463   day = g_date_get_day (date);
3464   month = g_date_get_month (date);
3465   year = g_date_get_year (date);
3466   g_date_free (date);   
3467
3468   gtk_drag_finish (context, TRUE, FALSE, time);
3469
3470   
3471   g_object_freeze_notify (G_OBJECT (calendar));
3472   if (!(calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3473       && (calendar->display_flags & GTK_CALENDAR_SHOW_HEADING))
3474     gtk_calendar_select_month (calendar, month - 1, year);
3475   gtk_calendar_select_day (calendar, day);
3476   g_object_thaw_notify (G_OBJECT (calendar));  
3477 }
3478
3479 \f
3480 /****************************************
3481  *              Public API              *
3482  ****************************************/
3483
3484 /**
3485  * gtk_calendar_new:
3486  * 
3487  * Creates a new calendar, with the current date being selected. 
3488  * 
3489  * Return value: a newly #GtkCalendar widget
3490  **/
3491 GtkWidget*
3492 gtk_calendar_new (void)
3493 {
3494   return g_object_new (GTK_TYPE_CALENDAR, NULL);
3495 }
3496
3497 /**
3498  * gtk_calendar_display_options:
3499  * @calendar: a #GtkCalendar.
3500  * @flags: the display options to set.
3501  *
3502  * Sets display options (whether to display the heading and the month headings).
3503  * 
3504  * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
3505  **/
3506 void
3507 gtk_calendar_display_options (GtkCalendar              *calendar,
3508                               GtkCalendarDisplayOptions flags)
3509 {
3510   gtk_calendar_set_display_options (calendar, flags);
3511 }
3512
3513 /**
3514  * gtk_calendar_get_display_options:
3515  * @calendar: a #GtkCalendar
3516  * 
3517  * Returns the current display options of @calendar. 
3518  * 
3519  * Return value: the display options.
3520  *
3521  * Since: 2.4
3522  **/
3523 GtkCalendarDisplayOptions 
3524 gtk_calendar_get_display_options (GtkCalendar         *calendar)
3525 {
3526   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3527
3528   return calendar->display_flags;
3529 }
3530
3531 /**
3532  * gtk_calendar_set_display_options:
3533  * @calendar: a #GtkCalendar
3534  * @flags: the display options to set
3535  * 
3536  * Sets display options (whether to display the heading and the month  
3537  * headings).
3538  *
3539  * Since: 2.4
3540  **/
3541 void
3542 gtk_calendar_set_display_options (GtkCalendar          *calendar,
3543                                   GtkCalendarDisplayOptions flags)
3544 {
3545   GtkWidget *widget = GTK_WIDGET (calendar);
3546   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3547   gint resize = 0;
3548   gint i;
3549   GtkCalendarDisplayOptions old_flags;
3550   
3551   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3552   
3553   old_flags = calendar->display_flags;
3554   
3555   if (GTK_WIDGET_REALIZED (widget))
3556     {
3557       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3558         {
3559           resize ++;
3560           if (! (flags & GTK_CALENDAR_NO_MONTH_CHANGE)
3561               && (priv->header_win))
3562             {
3563               calendar->display_flags &= ~GTK_CALENDAR_NO_MONTH_CHANGE;
3564               calendar_realize_arrows (calendar);
3565             }
3566           else
3567             {
3568               for (i = 0; i < 4; i++)
3569                 {
3570                   if (priv->arrow_win[i])
3571                     {
3572                       gdk_window_set_user_data (priv->arrow_win[i], 
3573                                                 NULL);
3574                       gdk_window_destroy (priv->arrow_win[i]);
3575                       priv->arrow_win[i] = NULL;
3576                     }
3577                 }
3578             }
3579         }
3580       
3581       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3582         {
3583           resize++;
3584           
3585           if (flags & GTK_CALENDAR_SHOW_HEADING)
3586             {
3587               calendar->display_flags |= GTK_CALENDAR_SHOW_HEADING;
3588               calendar_realize_header (calendar);
3589             }
3590           else
3591             {
3592               for (i = 0; i < 4; i++)
3593                 {
3594                   if (priv->arrow_win[i])
3595                     {
3596                       gdk_window_set_user_data (priv->arrow_win[i], 
3597                                                 NULL);
3598                       gdk_window_destroy (priv->arrow_win[i]);
3599                       priv->arrow_win[i] = NULL;
3600                     }
3601                 }
3602               gdk_window_set_user_data (priv->header_win, NULL);
3603               gdk_window_destroy (priv->header_win);
3604               priv->header_win = NULL;
3605             }
3606         }
3607       
3608       
3609       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3610         {
3611           resize++;
3612           
3613           if (flags & GTK_CALENDAR_SHOW_DAY_NAMES)
3614             {
3615               calendar->display_flags |= GTK_CALENDAR_SHOW_DAY_NAMES;
3616               calendar_realize_day_names (calendar);
3617             }
3618           else
3619             {
3620               gdk_window_set_user_data (priv->day_name_win, NULL);
3621               gdk_window_destroy (priv->day_name_win);
3622               priv->day_name_win = NULL;
3623             }
3624         }
3625       
3626       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3627         {
3628           resize++;
3629           
3630           if (flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3631             {
3632               calendar->display_flags |= GTK_CALENDAR_SHOW_WEEK_NUMBERS;
3633               calendar_realize_week_numbers (calendar);
3634             }
3635           else
3636             {
3637               gdk_window_set_user_data (priv->week_win, NULL);
3638               gdk_window_destroy (priv->week_win);
3639               priv->week_win = NULL;
3640             }
3641         }
3642
3643       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
3644         g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
3645       
3646       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
3647         resize++;
3648
3649       calendar->display_flags = flags;
3650       if (resize)
3651         gtk_widget_queue_resize (GTK_WIDGET (calendar));
3652       
3653     } 
3654   else
3655     calendar->display_flags = flags;
3656   
3657   g_object_freeze_notify (G_OBJECT (calendar));
3658   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_HEADING)
3659     g_object_notify (G_OBJECT (calendar), "show-heading");
3660   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DAY_NAMES)
3661     g_object_notify (G_OBJECT (calendar), "show-day-names");
3662   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_NO_MONTH_CHANGE)
3663     g_object_notify (G_OBJECT (calendar), "no-month-change");
3664   if ((old_flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
3665     g_object_notify (G_OBJECT (calendar), "show-week-numbers");
3666   g_object_thaw_notify (G_OBJECT (calendar));
3667 }
3668
3669 /**
3670  * gtk_calendar_select_month:
3671  * @calendar: a #GtkCalendar
3672  * @month: a month number between 0 and 11.
3673  * @year: the year the month is in.
3674  * 
3675  * Shifts the calendar to a different month.
3676  * 
3677  * Return value: %TRUE, always
3678  **/
3679 gboolean
3680 gtk_calendar_select_month (GtkCalendar *calendar,
3681                            guint        month,
3682                            guint        year)
3683 {
3684   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3685   g_return_val_if_fail (month <= 11, FALSE);
3686   
3687   calendar->month = month;
3688   calendar->year  = year;
3689   
3690   calendar_compute_days (calendar);
3691   calendar_queue_refresh (calendar);
3692
3693   g_object_freeze_notify (G_OBJECT (calendar));
3694   g_object_notify (G_OBJECT (calendar), "month");
3695   g_object_notify (G_OBJECT (calendar), "year");
3696   g_object_thaw_notify (G_OBJECT (calendar));
3697
3698   g_signal_emit (calendar,
3699                  gtk_calendar_signals[MONTH_CHANGED_SIGNAL],
3700                  0);
3701   return TRUE;
3702 }
3703
3704 /**
3705  * gtk_calendar_select_day:
3706  * @calendar: a #GtkCalendar.
3707  * @day: the day number between 1 and 31, or 0 to unselect 
3708  *   the currently selected day.
3709  * 
3710  * Selects a day from the current month.
3711  **/
3712 void
3713 gtk_calendar_select_day (GtkCalendar *calendar,
3714                          guint        day)
3715 {
3716   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3717   g_return_if_fail (day <= 31);
3718   
3719   /* Deselect the old day */
3720   if (calendar->selected_day > 0)
3721     {
3722       gint selected_day;
3723       
3724       selected_day = calendar->selected_day;
3725       calendar->selected_day = 0;
3726       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3727         calendar_invalidate_day_num (calendar, selected_day);
3728     }
3729   
3730   calendar->selected_day = day;
3731   
3732   /* Select the new day */
3733   if (day != 0)
3734     {
3735       if (GTK_WIDGET_DRAWABLE (GTK_WIDGET (calendar)))
3736         calendar_invalidate_day_num (calendar, day);
3737     }
3738   
3739   g_object_notify (G_OBJECT (calendar), "day");
3740
3741   g_signal_emit (calendar,
3742                  gtk_calendar_signals[DAY_SELECTED_SIGNAL],
3743                  0);
3744 }
3745
3746 /**
3747  * gtk_calendar_clear_marks:
3748  * @calendar: a #GtkCalendar
3749  * 
3750  * Remove all visual markers.
3751  **/
3752 void
3753 gtk_calendar_clear_marks (GtkCalendar *calendar)
3754 {
3755   guint day;
3756   
3757   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3758   
3759   for (day = 0; day < 31; day++)
3760     {
3761       calendar->marked_date[day] = FALSE;
3762     }
3763
3764   calendar->num_marked_dates = 0;
3765   calendar_queue_refresh (calendar);
3766 }
3767
3768 /**
3769  * gtk_calendar_mark_day:
3770  * @calendar: a #GtkCalendar 
3771  * @day: the day number to mark between 1 and 31.
3772  * 
3773  * Places a visual marker on a particular day.
3774  * 
3775  * Return value: %TRUE, always
3776  **/
3777 gboolean
3778 gtk_calendar_mark_day (GtkCalendar *calendar,
3779                        guint        day)
3780 {
3781   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3782   
3783   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == FALSE)
3784     {
3785       calendar->marked_date[day - 1] = TRUE;
3786       calendar->num_marked_dates++;
3787       calendar_invalidate_day_num (calendar, day);
3788     }
3789   
3790   return TRUE;
3791 }
3792
3793 /**
3794  * gtk_calendar_unmark_day:
3795  * @calendar: a #GtkCalendar.
3796  * @day: the day number to unmark between 1 and 31.
3797  * 
3798  * Removes the visual marker from a particular day.
3799  * 
3800  * Return value: %TRUE, always
3801  **/
3802 gboolean
3803 gtk_calendar_unmark_day (GtkCalendar *calendar,
3804                          guint        day)
3805 {
3806   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), FALSE);
3807   
3808   if (day >= 1 && day <= 31 && calendar->marked_date[day-1] == TRUE)
3809     {
3810       calendar->marked_date[day - 1] = FALSE;
3811       calendar->num_marked_dates--;
3812       calendar_invalidate_day_num (calendar, day);
3813     }
3814   
3815   return TRUE;
3816 }
3817
3818 /**
3819  * gtk_calendar_get_date:
3820  * @calendar: a #GtkCalendar
3821  * @year: location to store the year number, or %NULL
3822  * @month: location to store the month number (between 0 and 11), or %NULL
3823  * @day: location to store the day number (between 1 and 31), or %NULL
3824  * 
3825  * Obtains the selected date from a #GtkCalendar.
3826  **/
3827 void
3828 gtk_calendar_get_date (GtkCalendar *calendar,
3829                        guint       *year,
3830                        guint       *month,
3831                        guint       *day)
3832 {
3833   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3834   
3835   if (year)
3836     *year = calendar->year;
3837   
3838   if (month)
3839     *month = calendar->month;
3840   
3841   if (day)
3842     *day = calendar->selected_day;
3843 }
3844
3845 /**
3846  * gtk_calendar_set_detail_func:
3847  * @calendar: a #GtkCalendar.
3848  * @func: a function providing details for each day.
3849  * @data: data to pass to @func invokations.
3850  * @destroy: a function for releasing @data.
3851  *
3852  * Installs a function which provides Pango markup with detail information
3853  * for each day. Examples for such details are holidays or appointments. That
3854  * information is shown below each day when #GtkCalendar:show-details is set.
3855  * A tooltip containing with full detail information is provided, if the entire
3856  * text should not fit into the details area, or if #GtkCalendar:show-details
3857  * is not set.
3858  *
3859  * The size of the details area can be restricted by setting the
3860  * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
3861  * properties.
3862  *
3863  * Since: 2.14
3864  */
3865 void
3866 gtk_calendar_set_detail_func (GtkCalendar           *calendar,
3867                               GtkCalendarDetailFunc  func,
3868                               gpointer               data,
3869                               GDestroyNotify         destroy)
3870 {
3871   GtkCalendarPrivate *priv;
3872
3873   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3874
3875   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3876
3877   if (priv->detail_func_destroy)
3878     priv->detail_func_destroy (priv->detail_func_user_data);
3879
3880   priv->detail_func = func;
3881   priv->detail_func_user_data = data;
3882   priv->detail_func_destroy = destroy;
3883
3884   gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
3885                               NULL != priv->detail_func);
3886   gtk_widget_queue_resize (GTK_WIDGET (calendar));
3887 }
3888
3889 /**
3890  * gtk_calendar_set_detail_width_chars:
3891  * @calendar: a #GtkCalendar.
3892  * @chars: detail width in characters.
3893  *
3894  * Updates the width of detail cells.
3895  * See #GtkCalendar:detail-width-chars.
3896  *
3897  * Since: 2.14
3898  */
3899 void
3900 gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
3901                                      gint         chars)
3902 {
3903   GtkCalendarPrivate *priv;
3904
3905   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3906
3907   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3908
3909   if (chars != priv->detail_width_chars)
3910     {
3911       priv->detail_width_chars = chars;
3912       g_object_notify (G_OBJECT (calendar), "detail-width-chars");
3913       gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3914     }
3915 }
3916
3917 /**
3918  * gtk_calendar_set_detail_height_rows:
3919  * @calendar: a #GtkCalendar.
3920  * @rows: detail height in rows.
3921  *
3922  * Updates the height of detail cells.
3923  * See #GtkCalendar:detail-height-rows.
3924  *
3925  * Since: 2.14
3926  */
3927 void
3928 gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
3929                                      gint         rows)
3930 {
3931   GtkCalendarPrivate *priv;
3932
3933   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3934
3935   priv = GTK_CALENDAR_GET_PRIVATE (calendar);
3936
3937   if (rows != priv->detail_height_rows)
3938     {
3939       priv->detail_height_rows = rows;
3940       g_object_notify (G_OBJECT (calendar), "detail-height-rows");
3941       gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
3942     }
3943 }
3944
3945 /**
3946  * gtk_calendar_get_detail_width_chars:
3947  * @calendar: a #GtkCalendar.
3948  *
3949  * Queries the width of detail cells, in characters.
3950  * See #GtkCalendar:detail-width-chars.
3951  *
3952  * Since: 2.14
3953  *
3954  * Return value: The width of detail cells, in characters.
3955  */
3956 gint
3957 gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
3958 {
3959   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3960   return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
3961 }
3962
3963 /**
3964  * gtk_calendar_get_detail_height_rows:
3965  * @calendar: a #GtkCalendar.
3966  *
3967  * Queries the height of detail cells, in rows.
3968  * See #GtkCalendar:detail-width-chars.
3969  *
3970  * Since: 2.14
3971  *
3972  * Return value: The height of detail cells, in rows.
3973  */
3974 gint
3975 gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
3976 {
3977   g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
3978   return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
3979 }
3980
3981 /**
3982  * gtk_calendar_freeze:
3983  * @calendar: a #GtkCalendar
3984  * 
3985  * Does nothing. Previously locked the display of the calendar until
3986  * it was thawed with gtk_calendar_thaw().
3987  *
3988  * Deprecated: 2.8: 
3989  **/
3990 void
3991 gtk_calendar_freeze (GtkCalendar *calendar)
3992 {
3993   g_return_if_fail (GTK_IS_CALENDAR (calendar));
3994 }
3995
3996 /**
3997  * gtk_calendar_thaw:
3998  * @calendar: a #GtkCalendar
3999  * 
4000  * Does nothing. Previously defrosted a calendar; all the changes made
4001  * since the last gtk_calendar_freeze() were displayed.
4002  *
4003  * Deprecated: 2.8: 
4004  **/
4005 void
4006 gtk_calendar_thaw (GtkCalendar *calendar)
4007 {
4008   g_return_if_fail (GTK_IS_CALENDAR (calendar));
4009 }
4010
4011 #define __GTK_CALENDAR_C__
4012 #include "gtkaliasdef.c"