]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcalendar.c
Bug 457086 - numpad does not work when the Thai-Lao input method is used
[~andy/gtk] / gtk / gtkcalendar.c
index 34d76269e1f12f7118bd83d2bff9dc0e6abe5a4f..20b2d86c443c0a330eb06d0702de554e765b9705 100644 (file)
@@ -29,7 +29,7 @@
  * GTK+ at ftp://ftp.gtk.org/pub/gtk/. 
  */
 
-#include <config.h>
+#include "config.h"
 
 #ifdef HAVE_SYS_TIME_H
 #include <sys/time.h>
 #include <string.h>
 #include <stdlib.h>
 #include <time.h>
-#include <glib/gprintf.h>
 
-#undef GTK_DISABLE_DEPRECATED
-#include "gtkcalendar.h"
-#define GTK_DISABLE_DEPRECATED
+#include <glib.h>
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
 
+#include "gtkcalendar.h"
 #include "gtkdnd.h"
 #include "gtkintl.h"
 #include "gtkmain.h"
 #include "gtkmarshalers.h"
+#include "gtktooltip.h"
 #include "gtkprivate.h"
-#include "gtkintl.h"
 #include "gdk/gdkkeysyms.h"
 #include "gtkalias.h"
 
 /***************************************************************************/
-/* The following date routines are taken from the lib_date package.  Keep
- * them separate in case we want to update them if a newer lib_date comes
- * out with fixes.  */
-
-typedef         unsigned   int     N_int;
-typedef         unsigned   long    N_long;
-typedef         signed     long    Z_long;
-typedef enum { false = FALSE , true = TRUE } boolean;
-
-#define and        &&      /* logical (boolean) operators: lower case */
-#define or         ||
+/* The following date routines are taken from the lib_date package. 
+ * They have been minimally edited to avoid conflict with types defined
+ * in win32 headers.
+ */
 
-static const N_int month_length[2][13] =
+static const guint month_length[2][13] =
 {
   { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 },
   { 0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }
 };
 
-static const N_int days_in_months[2][14] =
+static const guint days_in_months[2][14] =
 {
   { 0, 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
   { 0, 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
 };
 
-static Z_long  calc_days(N_int year, N_int mm, N_int dd);
-static N_int   day_of_week(N_int year, N_int mm, N_int dd);
-static Z_long  dates_difference(N_int year1, N_int mm1, N_int dd1,
-                               N_int year2, N_int mm2, N_int dd2);
-static N_int   weeks_in_year(N_int year);
+static glong  calc_days(guint year, guint mm, guint dd);
+static guint  day_of_week(guint year, guint mm, guint dd);
+static glong  dates_difference(guint year1, guint mm1, guint dd1,
+                              guint year2, guint mm2, guint dd2);
+static guint  weeks_in_year(guint year);
 
-static boolean 
-leap(N_int year)
+static gboolean 
+leap (guint year)
 {
-  return((((year % 4) == 0) and ((year % 100) != 0)) or ((year % 400) == 0));
+  return((((year % 4) == 0) && ((year % 100) != 0)) || ((year % 400) == 0));
 }
 
-static N_int 
-day_of_week(N_int year, N_int mm, N_int dd)
+static guint 
+day_of_week (guint year, guint mm, guint dd)
 {
-  Z_long  days;
+  glong  days;
   
   days = calc_days(year, mm, dd);
   if (days > 0L)
@@ -104,53 +99,53 @@ day_of_week(N_int year, N_int mm, N_int dd)
       days %= 7L;
       days++;
     }
-  return( (N_int) days );
+  return( (guint) days );
 }
 
-static N_int weeks_in_year(N_int year)
+static guint weeks_in_year(guint year)
 {
-  return(52 + ((day_of_week(year,1,1)==4) or (day_of_week(year,12,31)==4)));
+  return(52 + ((day_of_week(year,1,1)==4) || (day_of_week(year,12,31)==4)));
 }
 
-static boolean 
-check_date(N_int year, N_int mm, N_int dd)
+static gboolean 
+check_date(guint year, guint mm, guint dd)
 {
-  if (year < 1) return(false);
-  if ((mm < 1) or (mm > 12)) return(false);
-  if ((dd < 1) or (dd > month_length[leap(year)][mm])) return(false);
-  return(true);
+  if (year < 1) return FALSE;
+  if ((mm < 1) || (mm > 12)) return FALSE;
+  if ((dd < 1) || (dd > month_length[leap(year)][mm])) return FALSE;
+  return TRUE;
 }
 
-static N_int 
-week_number(N_int year, N_int mm, N_int dd)
+static guint 
+week_number(guint year, guint mm, guint dd)
 {
-  N_int first;
+  guint first;
   
   first = day_of_week(year,1,1) - 1;
-  return( (N_int) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
+  return( (guint) ( (dates_difference(year,1,1, year,mm,dd) + first) / 7L ) +
          (first < 4) );
 }
 
-static Z_long 
-year_to_days(N_int year)
+static glong 
+year_to_days(guint year)
 {
   return( year * 365L + (year / 4) - (year / 100) + (year / 400) );
 }
 
 
-static Z_long 
-calc_days(N_int year, N_int mm, N_int dd)
+static glong 
+calc_days(guint year, guint mm, guint dd)
 {
-  boolean lp;
+  gboolean lp;
   
   if (year < 1) return(0L);
-  if ((mm < 1) or (mm > 12)) return(0L);
-  if ((dd < 1) or (dd > month_length[(lp = leap(year))][mm])) return(0L);
+  if ((mm < 1) || (mm > 12)) return(0L);
+  if ((dd < 1) || (dd > month_length[(lp = leap(year))][mm])) return(0L);
   return( year_to_days(--year) + days_in_months[lp][mm] + dd );
 }
 
-static boolean 
-week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
+static gboolean 
+week_of_year(guint *week, guint *year, guint mm, guint dd)
 {
   if (check_date(*year,mm,dd))
     {
@@ -162,14 +157,14 @@ week_of_year(N_int *week, N_int *year, N_int mm, N_int dd)
          *week = 1;
          (*year)++;
        }
-      return(true);
+      return TRUE;
     }
-  return(false);
+  return FALSE;
 }
 
-static Z_long 
-dates_difference(N_int year1, N_int mm1, N_int dd1,
-                N_int year2, N_int mm2, N_int dd2)
+static glong 
+dates_difference(guint year1, guint mm1, guint dd1,
+                guint year2, guint mm2, guint dd2)
 {
   return( calc_days(year2, mm2, dd2) - calc_days(year1, mm1, dd1) );
 }
@@ -188,15 +183,17 @@ dates_difference(N_int year1, N_int mm1, N_int dd1,
 #define DAY_XSEP                0 /* not really good for small calendar */
 #define DAY_YSEP                0 /* not really good for small calendar */
 
+#define SCROLL_DELAY_FACTOR      5
+
 /* Color usage */
 #define HEADER_FG_COLOR(widget)                 (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
 #define HEADER_BG_COLOR(widget)                 (& (widget)->style->bg[GTK_WIDGET_STATE (widget)])
 #define SELECTED_BG_COLOR(widget)       (& (widget)->style->base[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
 #define SELECTED_FG_COLOR(widget)       (& (widget)->style->text[GTK_WIDGET_HAS_FOCUS (widget) ? GTK_STATE_SELECTED : GTK_STATE_ACTIVE])
-#define NORMAL_DAY_COLOR(widget)        (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
+#define NORMAL_DAY_COLOR(widget)        (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
 #define PREV_MONTH_COLOR(widget)        (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
 #define NEXT_MONTH_COLOR(widget)        (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
-#define MARKED_COLOR(widget)            (& (widget)->style->fg[GTK_WIDGET_STATE (widget)])
+#define MARKED_COLOR(widget)            (& (widget)->style->text[GTK_WIDGET_STATE (widget)])
 #define BACKGROUND_COLOR(widget)        (& (widget)->style->base[GTK_WIDGET_STATE (widget)])
 #define HIGHLIGHT_BACK_COLOR(widget)    (& (widget)->style->mid[GTK_WIDGET_STATE (widget)])
 
@@ -234,7 +231,9 @@ enum
   PROP_SHOW_DAY_NAMES,
   PROP_NO_MONTH_CHANGE,
   PROP_SHOW_WEEK_NUMBERS,
-  PROP_LAST
+  PROP_SHOW_DETAILS,
+  PROP_DETAIL_WIDTH_CHARS,
+  PROP_DETAIL_HEIGHT_ROWS
 };
 
 static guint gtk_calendar_signals[LAST_SIGNAL] = { 0 };
@@ -282,6 +281,16 @@ struct _GtkCalendarPrivate
 
   gint drag_start_x;
   gint drag_start_y;
+
+  /* Optional callback, used to display extra information for each day. */
+  GtkCalendarDetailFunc detail_func;
+  gpointer              detail_func_user_data;
+  GDestroyNotify        detail_func_destroy;
+
+  /* Size requistion for details provided by the hook. */
+  gint detail_height_rows;
+  gint detail_width_chars;
+  gint detail_overflow[6];
 };
 
 #define GTK_CALENDAR_GET_PRIVATE(widget)  (GTK_CALENDAR (widget)->priv)
@@ -327,6 +336,11 @@ static void     gtk_calendar_state_changed  (GtkWidget        *widget,
                                             GtkStateType      previous_state);
 static void     gtk_calendar_style_set      (GtkWidget        *widget,
                                             GtkStyle         *previous_style);
+static gboolean gtk_calendar_query_tooltip  (GtkWidget        *widget,
+                                            gint              x,
+                                            gint              y,
+                                            gboolean          keyboard_mode,
+                                            GtkTooltip       *tooltip);
 
 static void     gtk_calendar_drag_data_get      (GtkWidget        *widget,
                                                 GdkDragContext   *context,
@@ -406,6 +420,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
   widget_class->state_changed = gtk_calendar_state_changed;
   widget_class->grab_notify = gtk_calendar_grab_notify;
   widget_class->focus_out_event = gtk_calendar_focus_out;
+  widget_class->query_tooltip = gtk_calendar_query_tooltip;
 
   widget_class->drag_data_get = gtk_calendar_drag_data_get;
   widget_class->drag_motion = gtk_calendar_drag_motion;
@@ -413,13 +428,26 @@ gtk_calendar_class_init (GtkCalendarClass *class)
   widget_class->drag_drop = gtk_calendar_drag_drop;
   widget_class->drag_data_received = gtk_calendar_drag_data_received;
   
+  /**
+   * GtkCalendar:year:
+   *
+   * The selected year. 
+   * This property gets initially set to the current year.
+   */  
   g_object_class_install_property (gobject_class,
                                    PROP_YEAR,
                                    g_param_spec_int ("year",
                                                     P_("Year"),
                                                     P_("The selected year"),
-                                                    0, G_MAXINT, 0,
+                                                    0, G_MAXINT >> 9, 0,
                                                     GTK_PARAM_READWRITE));
+
+  /**
+   * GtkCalendar:month:
+   *
+   * The selected month (as a number between 0 and 11). 
+   * This property gets initially set to the current month.
+   */
   g_object_class_install_property (gobject_class,
                                    PROP_MONTH,
                                    g_param_spec_int ("month",
@@ -427,6 +455,14 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                                                     P_("The selected month (as a number between 0 and 11)"),
                                                     0, 11, 0,
                                                     GTK_PARAM_READWRITE));
+
+  /**
+   * GtkCalendar:day:
+   *
+   * The selected day (as a number between 1 and 31, or 0 
+   * to unselect the currently selected day).
+   * This property gets initially set to the current day.
+   */
   g_object_class_install_property (gobject_class,
                                    PROP_DAY,
                                    g_param_spec_int ("day",
@@ -494,8 +530,57 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                                                         FALSE,
                                                         GTK_PARAM_READWRITE));
 
+/**
+ * GtkCalendar:detail-width-chars:
+ *
+ * Width of a detail cell, in characters.
+ * A value of 0 allows any width. See gtk_calendar_set_detail_func().
+ *
+ * Since: 2.14
+ */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DETAIL_WIDTH_CHARS,
+                                   g_param_spec_int ("detail-width-chars",
+                                                    P_("Details Width"),
+                                                    P_("Details width in characters"),
+                                                    0, 127, 0,
+                                                    GTK_PARAM_READWRITE));
+
+/**
+ * GtkCalendar:detail-height-rows:
+ *
+ * Height of a detail cell, in rows.
+ * A value of 0 allows any width. See gtk_calendar_set_detail_func().
+ *
+ * Since: 2.14
+ */
+  g_object_class_install_property (gobject_class,
+                                   PROP_DETAIL_HEIGHT_ROWS,
+                                   g_param_spec_int ("detail-height-rows",
+                                                    P_("Details Height"),
+                                                    P_("Details height in rows"),
+                                                    0, 127, 0,
+                                                    GTK_PARAM_READWRITE));
+
+/**
+ * GtkCalendar:show-details:
+ *
+ * Determines whether details are shown directly in the widget, or if they are
+ * available only as tooltip. When this property is set days with details are
+ * marked.
+ *
+ * Since: 2.14
+ */
+  g_object_class_install_property (gobject_class,
+                                   PROP_SHOW_DETAILS,
+                                   g_param_spec_boolean ("show-details",
+                                                        P_("Show Details"),
+                                                        P_("If TRUE, details are shown"),
+                                                        TRUE,
+                                                        GTK_PARAM_READWRITE));
+
   gtk_calendar_signals[MONTH_CHANGED_SIGNAL] =
-    g_signal_new (I_("month_changed"),
+    g_signal_new (I_("month-changed"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, month_changed),
@@ -503,7 +588,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[DAY_SELECTED_SIGNAL] =
-    g_signal_new (I_("day_selected"),
+    g_signal_new (I_("day-selected"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, day_selected),
@@ -511,7 +596,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[DAY_SELECTED_DOUBLE_CLICK_SIGNAL] =
-    g_signal_new (I_("day_selected_double_click"),
+    g_signal_new (I_("day-selected-double-click"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, day_selected_double_click),
@@ -519,7 +604,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[PREV_MONTH_SIGNAL] =
-    g_signal_new (I_("prev_month"),
+    g_signal_new (I_("prev-month"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, prev_month),
@@ -527,7 +612,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[NEXT_MONTH_SIGNAL] =
-    g_signal_new (I_("next_month"),
+    g_signal_new (I_("next-month"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, next_month),
@@ -535,7 +620,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[PREV_YEAR_SIGNAL] =
-    g_signal_new (I_("prev_year"),
+    g_signal_new (I_("prev-year"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, prev_year),
@@ -543,7 +628,7 @@ gtk_calendar_class_init (GtkCalendarClass *class)
                  _gtk_marshal_VOID__VOID,
                  G_TYPE_NONE, 0);
   gtk_calendar_signals[NEXT_YEAR_SIGNAL] =
-    g_signal_new (I_("next_year"),
+    g_signal_new (I_("next-year"),
                  G_OBJECT_CLASS_TYPE (gobject_class),
                  G_SIGNAL_RUN_FIRST,
                  G_STRUCT_OFFSET (GtkCalendarClass, next_year),
@@ -561,12 +646,16 @@ gtk_calendar_init (GtkCalendar *calendar)
   time_t secs;
   struct tm *tm;
   gint i;
+#ifdef G_OS_WIN32
+  wchar_t wbuffer[100];
+#else
   char buffer[255];
   time_t tmp_time;
+#endif
   GtkCalendarPrivate *priv;
   gchar *year_before;
 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
-  gchar *langinfo;
+  union { unsigned int word; char *string; } langinfo;
   gint week_1stday = 0;
   gint first_weekday = 1;
   guint week_origin;
@@ -583,17 +672,33 @@ gtk_calendar_init (GtkCalendar *calendar)
   if (!default_abbreviated_dayname[0])
     for (i=0; i<7; i++)
       {
+#ifndef G_OS_WIN32
        tmp_time= (i+3)*86400;
        strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
        default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
+#else
+       if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
+                            wbuffer, G_N_ELEMENTS (wbuffer)))
+         default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
+       else
+         default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
+#endif
       }
   
   if (!default_monthname[0])
     for (i=0; i<12; i++)
       {
+#ifndef G_OS_WIN32
        tmp_time=i*2764800;
        strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
        default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
+#else
+       if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
+                            wbuffer, G_N_ELEMENTS (wbuffer)))
+         default_monthname[i] = g_strdup_printf ("(%d)", i);
+       else
+         default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
+#endif
       }
   
   /* Set defaults */
@@ -607,8 +712,9 @@ gtk_calendar_init (GtkCalendar *calendar)
   calendar->num_marked_dates = 0;
   calendar->selected_day = tm->tm_mday;
   
-  calendar->display_flags = ( GTK_CALENDAR_SHOW_HEADING | 
-                             GTK_CALENDAR_SHOW_DAY_NAMES );
+  calendar->display_flags = (GTK_CALENDAR_SHOW_HEADING |
+                            GTK_CALENDAR_SHOW_DAY_NAMES |
+                            GTK_CALENDAR_SHOW_DETAILS);
   
   calendar->highlight_row = -1;
   calendar->highlight_col = -1;
@@ -645,7 +751,7 @@ gtk_calendar_init (GtkCalendar *calendar)
    * Do *not* translate it to anything else, if it
    * it isn't calendar:YM or calendar:MY it will not work.
    *
-   * Note that this flipping is in top the text direction flipping,
+   * Note that this flipping is in top of the text direction flipping,
    * so if you have a default text direction of RTL and YM, then
    * the year will appear on the right.
    */
@@ -655,11 +761,25 @@ gtk_calendar_init (GtkCalendar *calendar)
   else if (strcmp (year_before, "calendar:MY") != 0)
     g_warning ("Whoever translated calendar:MY did so wrongly.\n");
 
+#ifdef G_OS_WIN32
+  priv->week_start = 0;
+  week_start = NULL;
+
+  if (GetLocaleInfoW (GetThreadLocale (), LOCALE_IFIRSTDAYOFWEEK,
+                     wbuffer, G_N_ELEMENTS (wbuffer)))
+    week_start = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
+      
+  if (week_start != NULL)
+    {
+      priv->week_start = (week_start[0] - '0' + 1) % 7;
+      g_free(week_start);
+    }
+#else
 #ifdef HAVE__NL_TIME_FIRST_WEEKDAY
-  langinfo = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
-  first_weekday = langinfo[0];
-  langinfo = nl_langinfo (_NL_TIME_WEEK_1STDAY);
-  week_origin = GPOINTER_TO_INT (langinfo);
+  langinfo.string = nl_langinfo (_NL_TIME_FIRST_WEEKDAY);
+  first_weekday = langinfo.string[0];
+  langinfo.string = nl_langinfo (_NL_TIME_WEEK_1STDAY);
+  week_origin = langinfo.word;
   if (week_origin == 19971130) /* Sunday */
     week_1stday = 0;
   else if (week_origin == 19971201) /* Monday */
@@ -685,6 +805,7 @@ gtk_calendar_init (GtkCalendar *calendar)
       g_warning ("Whoever translated calendar:week_start:0 did so wrongly.\n");
       priv->week_start = 0;
     }
+#endif
 #endif
 
   calendar_compute_days (calendar);
@@ -695,13 +816,24 @@ gtk_calendar_init (GtkCalendar *calendar)
  *          Utility Functions           *
  ****************************************/
 
+static void
+calendar_queue_refresh (GtkCalendar *calendar)
+{
+  GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
+
+  if (!(priv->detail_func) ||
+      !(calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS) ||
+       (priv->detail_width_chars && priv->detail_height_rows))
+    gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  else
+    gtk_widget_queue_resize (GTK_WIDGET (calendar));
+}
+
 static void
 calendar_set_month_next (GtkCalendar *calendar)
 {
   gint month_len;
-  
-  g_return_if_fail (GTK_IS_WIDGET (calendar));
-  
+
   if (calendar->display_flags & GTK_CALENDAR_NO_MONTH_CHANGE)
     return;
   
@@ -732,16 +864,14 @@ calendar_set_month_next (GtkCalendar *calendar)
   else
     gtk_calendar_select_day (calendar, calendar->selected_day);
 
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 static void
 calendar_set_year_prev (GtkCalendar *calendar)
 {
   gint month_len;
-  
-  g_return_if_fail (GTK_IS_WIDGET (calendar));
-  
+
   calendar->year--;
   calendar_compute_days (calendar);
   g_signal_emit (calendar,
@@ -761,16 +891,14 @@ calendar_set_year_prev (GtkCalendar *calendar)
   else
     gtk_calendar_select_day (calendar, calendar->selected_day);
   
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 static void
 calendar_set_year_next (GtkCalendar *calendar)
 {
   gint month_len;
-  
-  g_return_if_fail (GTK_IS_WIDGET (calendar));
-  
+
   calendar->year++;
   calendar_compute_days (calendar);
   g_signal_emit (calendar,
@@ -790,7 +918,7 @@ calendar_set_year_next (GtkCalendar *calendar)
   else
     gtk_calendar_select_day (calendar, calendar->selected_day);
   
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 static void
@@ -806,8 +934,6 @@ calendar_compute_days (GtkCalendar *calendar)
   gint col;
   gint day;
 
-  g_return_if_fail (GTK_IS_CALENDAR (calendar));
-
   year = calendar->year;
   month = calendar->month + 1;
   
@@ -1098,7 +1224,7 @@ calendar_set_month_prev (GtkCalendar *calendar)
       gtk_calendar_select_day (calendar, calendar->selected_day);
     }
 
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 \f
@@ -1109,14 +1235,24 @@ calendar_set_month_prev (GtkCalendar *calendar)
 static void
 gtk_calendar_finalize (GObject *object)
 {
-  (* G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize) (object);
+  G_OBJECT_CLASS (gtk_calendar_parent_class)->finalize (object);
 }
 
 static void
 gtk_calendar_destroy (GtkObject *object)
 {
+  GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
+
   calendar_stop_spinning (GTK_CALENDAR (object));
   
+  /* Call the destroy function for the extra display callback: */
+  if (priv->detail_func_destroy && priv->detail_func_user_data)
+    {
+      priv->detail_func_destroy (priv->detail_func_user_data);
+      priv->detail_func_user_data = NULL;
+      priv->detail_func_destroy = NULL;
+    }
+
   GTK_OBJECT_CLASS (gtk_calendar_parent_class)->destroy (object);
 }
 
@@ -1131,7 +1267,7 @@ calendar_set_display_option (GtkCalendar              *calendar,
     flags = calendar->display_flags | flag;
   else
     flags = calendar->display_flags & ~flag; 
-  gtk_calendar_display_options (calendar, flags);
+  gtk_calendar_set_display_options (calendar, flags);
 }
 
 static gboolean
@@ -1187,6 +1323,19 @@ gtk_calendar_set_property (GObject      *object,
                                   GTK_CALENDAR_SHOW_WEEK_NUMBERS,
                                   g_value_get_boolean (value));
       break;
+    case PROP_SHOW_DETAILS:
+      calendar_set_display_option (calendar,
+                                  GTK_CALENDAR_SHOW_DETAILS,
+                                  g_value_get_boolean (value));
+      break;
+    case PROP_DETAIL_WIDTH_CHARS:
+      gtk_calendar_set_detail_width_chars (calendar,
+                                           g_value_get_int (value));
+      break;
+    case PROP_DETAIL_HEIGHT_ROWS:
+      gtk_calendar_set_detail_height_rows (calendar,
+                                           g_value_get_int (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1199,9 +1348,8 @@ gtk_calendar_get_property (GObject      *object,
                           GValue       *value,
                           GParamSpec   *pspec)
 {
-  GtkCalendar *calendar;
-
-  calendar = GTK_CALENDAR (object);
+  GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (object);
+  GtkCalendar *calendar = GTK_CALENDAR (object);
 
   switch (prop_id) 
     {
@@ -1230,6 +1378,16 @@ gtk_calendar_get_property (GObject      *object,
       g_value_set_boolean (value, calendar_get_display_option (calendar,
                                                               GTK_CALENDAR_SHOW_WEEK_NUMBERS));
       break;
+    case PROP_SHOW_DETAILS:
+      g_value_set_boolean (value, calendar_get_display_option (calendar,
+                                                              GTK_CALENDAR_SHOW_DETAILS));
+      break;
+    case PROP_DETAIL_WIDTH_CHARS:
+      g_value_set_int (value, priv->detail_width_chars);
+      break;
+    case PROP_DETAIL_HEIGHT_ROWS:
+      g_value_set_int (value, priv->detail_height_rows);
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1311,7 +1469,7 @@ calendar_realize_header (GtkCalendar *calendar)
       attributes.x = widget->style->xthickness;
       attributes.y = widget->style->ythickness;
       attributes.width = widget->allocation.width - 2 * attributes.x;
-      attributes.height = priv->header_h - 2 * attributes.y;
+      attributes.height = priv->header_h;
       priv->header_win = gdk_window_new (widget->window,
                                         &attributes, attributes_mask);
       
@@ -1384,7 +1542,10 @@ calendar_realize_week_numbers (GtkCalendar *calendar)
       attributes.event_mask = gtk_widget_get_events (widget) | GDK_EXPOSURE_MASK;
       
       attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_COLORMAP;
-      attributes.x = widget->style->xthickness + INNER_BORDER;
+      if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
+       attributes.x = widget->style->xthickness + INNER_BORDER;
+      else 
+       attributes.x = widget->allocation.width - priv->week_width - (widget->style->xthickness + INNER_BORDER);
       attributes.y = (priv->header_h + priv->day_name_h 
                      + (widget->style->ythickness + INNER_BORDER));
       attributes.width = priv->week_width;
@@ -1440,11 +1601,18 @@ gtk_calendar_realize (GtkWidget *widget)
                            | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
                            | GDK_POINTER_MOTION_MASK | GDK_LEAVE_NOTIFY_MASK);
   
-  attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_LTR) 
+    attributes.x = priv->week_width + (widget->style->ythickness + INNER_BORDER);
+  else
+    attributes.x = widget->style->ythickness + INNER_BORDER;
+
   attributes.y = (priv->header_h + priv->day_name_h 
                  + (widget->style->ythickness + INNER_BORDER));
   attributes.width = (widget->allocation.width - attributes.x 
                      - (widget->style->xthickness + INNER_BORDER));
+  if (gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL)
+    attributes.width -= priv->week_width;
+
   attributes.height = priv->main_h;
   priv->main_win = gdk_window_new (widget->window,
                                   &attributes, attributes_mask);
@@ -1498,9 +1666,84 @@ gtk_calendar_unrealize (GtkWidget *widget)
       gdk_window_destroy (priv->day_name_win);
       priv->day_name_win = NULL;      
     }
-  
-  if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize)
-    (* GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize) (widget);
+
+  GTK_WIDGET_CLASS (gtk_calendar_parent_class)->unrealize (widget);
+}
+
+static gchar*
+gtk_calendar_get_detail (GtkCalendar *calendar,
+                         gint         row,
+                         gint         column)
+{
+  GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
+  gint year, month;
+
+  year = calendar->year;
+  month = calendar->month + calendar->day_month[row][column] - MONTH_CURRENT;
+
+  if (month < 0)
+    {
+      month += 12;
+      year -= 1;
+    }
+  else if (month > 11)
+    {
+      month -= 12;
+      year += 1;
+    }
+
+  return priv->detail_func (calendar,
+                            year, month,
+                            calendar->day[row][column],
+                            priv->detail_func_user_data);
+}
+
+static gboolean
+gtk_calendar_query_tooltip (GtkWidget  *widget,
+                            gint        x,
+                            gint        y,
+                            gboolean    keyboard_mode,
+                            GtkTooltip *tooltip)
+{
+  GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
+  GtkCalendar *calendar = GTK_CALENDAR (widget);
+  gchar *detail = NULL;
+  GdkRectangle day_rect;
+
+  if (priv->main_win)
+    {
+      gint x0, y0, row, col;
+
+      gdk_window_get_position (priv->main_win, &x0, &y0);
+      col = calendar_column_from_x (calendar, x - x0);
+      row = calendar_row_from_y (calendar, y - y0);
+
+      if (col != -1 && row != -1 &&
+          (0 != (priv->detail_overflow[row] & (1 << col)) ||
+           0 == (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS)))
+        {
+          detail = gtk_calendar_get_detail (calendar, row, col);
+          calendar_day_rectangle (calendar, row, col, &day_rect);
+
+          day_rect.x += x0;
+          day_rect.y += y0;
+        }
+    }
+
+  if (detail)
+    {
+      gtk_tooltip_set_tip_area (tooltip, &day_rect);
+      gtk_tooltip_set_markup (tooltip, detail);
+
+      g_free (detail);
+
+      return TRUE;
+    }
+
+  if (GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip)
+    return GTK_WIDGET_CLASS (gtk_calendar_parent_class)->query_tooltip (widget, x, y, keyboard_mode, tooltip);
+
+  return FALSE;
 }
 
 \f
@@ -1518,14 +1761,14 @@ gtk_calendar_size_request (GtkWidget      *widget,
   PangoRectangle logical_rect;
 
   gint height;
-  gint i;
-  gchar buffer[255];
+  gint i, r, c;
   gint calendar_margin = CALENDAR_MARGIN;
   gint header_width, main_width;
   gint max_header_height = 0;
   gint focus_width;
   gint focus_padding;
-  
+  gint max_detail_height;
+
   gtk_widget_style_get (GTK_WIDGET (widget),
                        "focus-line-width", &focus_width,
                        "focus-padding", &focus_padding,
@@ -1553,14 +1796,11 @@ gtk_calendar_size_request (GtkWidget      *widget,
 
       priv->max_year_width = 0;
       /* Translators:  This is a text measurement template.
-       * Translate it to the widest year text. 
-       * 
-       * Don't include the prefix "year measurement template|" 
-       * in the translation.
+       * Translate it to the widest year text
        *
        * If you don't understand this, leave it as "2000"
        */
-      pango_layout_set_text (layout, Q_("year measurement template|2000"), -1);          
+      pango_layout_set_text (layout, C_("year measurement template", "2000"), -1);       
       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
       priv->max_year_width = MAX (priv->max_year_width,
                                  logical_rect.width + 8);
@@ -1584,31 +1824,32 @@ gtk_calendar_size_request (GtkWidget      *widget,
   /* Mainwindow labels width */
   
   priv->max_day_char_width = 0;
+  priv->max_day_char_ascent = 0;
+  priv->max_day_char_descent = 0;
   priv->min_day_width = 0;
-  priv->max_label_char_ascent = 0;
-  priv->max_label_char_descent = 0;
 
   for (i = 0; i < 9; i++)
     {
-      g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
+      gchar buffer[32];
+      g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), i * 11);
       pango_layout_set_text (layout, buffer, -1);        
       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
       priv->min_day_width = MAX (priv->min_day_width,
                                         logical_rect.width);
 
-      priv->max_day_char_ascent = MAX (priv->max_label_char_ascent,
+      priv->max_day_char_ascent = MAX (priv->max_day_char_ascent,
                                               PANGO_ASCENT (logical_rect));
-      priv->max_day_char_descent = MAX (priv->max_label_char_descent, 
+      priv->max_day_char_descent = MAX (priv->max_day_char_descent, 
                                                PANGO_DESCENT (logical_rect));
     }
-  /* We add one to max_day_char_width to be able to make the marked day "bold" */
-  priv->max_day_char_width = priv->min_day_width / 2 + 1;
   
+  priv->max_label_char_ascent = 0;
+  priv->max_label_char_descent = 0;
   if (calendar->display_flags & GTK_CALENDAR_SHOW_DAY_NAMES)
     for (i = 0; i < 7; i++)
       {
        pango_layout_set_text (layout, default_abbreviated_dayname[i], -1);
-       pango_layout_line_get_pixel_extents (pango_layout_get_lines (layout)->data, NULL, &logical_rect);
+       pango_layout_line_get_pixel_extents (pango_layout_get_lines_readonly (layout)->data, NULL, &logical_rect);
 
        priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
        priv->max_label_char_ascent = MAX (priv->max_label_char_ascent,
@@ -1621,13 +1862,85 @@ gtk_calendar_size_request (GtkWidget      *widget,
   if (calendar->display_flags & GTK_CALENDAR_SHOW_WEEK_NUMBERS)
     for (i = 0; i < 9; i++)
       {
-       g_snprintf (buffer, sizeof (buffer), "%d%d", i, i);
+       gchar buffer[32];
+       g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), i * 11);
        pango_layout_set_text (layout, buffer, -1);       
        pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
        priv->max_week_char_width = MAX (priv->max_week_char_width,
-                                                logical_rect.width / 2);
+                                          logical_rect.width / 2);
       }
   
+  /* Calculate detail extents. Do this as late as possible since
+   * pango_layout_set_markup is called which alters font settings. */
+  max_detail_height = 0;
+
+  if (priv->detail_func && (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS))
+    {
+      gchar *markup, *tail;
+
+      if (priv->detail_width_chars || priv->detail_height_rows)
+        {
+          gint rows = MAX (1, priv->detail_height_rows) - 1;
+          gsize len = priv->detail_width_chars + rows + 16;
+
+          markup = tail = g_alloca (len);
+
+          memcpy (tail,     "<small>", 7);
+          tail += 7;
+
+          memset (tail, 'm', priv->detail_width_chars);
+          tail += priv->detail_width_chars;
+
+          memset (tail, '\n', rows);
+          tail += rows;
+
+          memcpy (tail,     "</small>", 9);
+          tail += 9;
+
+          g_assert (len == (tail - markup));
+
+          pango_layout_set_markup (layout, markup, -1);
+          pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
+
+          if (priv->detail_width_chars)
+            priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
+          if (priv->detail_height_rows)
+            max_detail_height = MAX (max_detail_height, logical_rect.height);
+        }
+
+      if (!priv->detail_width_chars || !priv->detail_height_rows)
+        for (r = 0; r < 6; r++)
+          for (c = 0; c < 7; c++)
+            {
+              gchar *detail = gtk_calendar_get_detail (calendar, r, c);
+
+              if (detail)
+                {
+                  markup = g_strconcat ("<small>", detail, "</small>", NULL);
+                  pango_layout_set_markup (layout, markup, -1);
+
+                  if (priv->detail_width_chars)
+                    {
+                      pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
+                      pango_layout_set_width (layout, PANGO_SCALE * priv->min_day_width);
+                    }
+
+                  pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
+
+                  if (!priv->detail_width_chars)
+                    priv->min_day_width = MAX (priv->min_day_width, logical_rect.width);
+                  if (!priv->detail_height_rows)
+                    max_detail_height = MAX (max_detail_height, logical_rect.height);
+
+                  g_free (markup);
+                  g_free (detail);
+                }
+            }
+    }
+
+  /* We add one to max_day_char_width to be able to make the marked day "bold" */
+  priv->max_day_char_width = priv->min_day_width / 2 + 1;
+
   main_width = (7 * (priv->min_day_width + (focus_padding + focus_width) * 2) + (DAY_XSEP * 6) + CALENDAR_MARGIN * 2
                + (priv->max_week_char_width
                   ? priv->max_week_char_width * 2 + (focus_padding + focus_width) * 2 + CALENDAR_XSEP * 2
@@ -1664,6 +1977,7 @@ gtk_calendar_size_request (GtkWidget        *widget,
   priv->main_h = (CALENDAR_MARGIN + calendar_margin
                          + 6 * (priv->max_day_char_ascent
                                 + priv->max_day_char_descent 
+                                 + max_detail_height
                                 + 2 * (focus_padding + focus_width))
                          + DAY_YSEP * 5);
   
@@ -1823,14 +2137,13 @@ calendar_paint_header (GtkCalendar *calendar)
    * gtkcalendar widget.  See strftime() manual for the format.
    * Use only ASCII in the translation.
    *
-   * Also look for the msgid "year measurement template|2000".  
+   * Also look for the msgid "2000".
    * Translate that entry to a year with the widest output of this
-   * msgid. 
-   * 
-   * Don't include the prefix "calendar year format|" in the 
-   * translation. "%Y" is appropriate for most locales.
+   * msgid.
+   *
+   * "%Y" is appropriate for most locales.
    */
-  strftime (buffer, sizeof (buffer), Q_("calendar year format|%Y"), tm);
+  strftime (buffer, sizeof (buffer), C_("calendar year format", "%Y"), tm);
   str = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
   layout = gtk_widget_create_pango_layout (widget, str);
   g_free (str);
@@ -1972,10 +2285,11 @@ calendar_paint_week_numbers (GtkCalendar *calendar)
   GtkWidget *widget = GTK_WIDGET (calendar);
   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
   cairo_t *cr;
-  gint row, week = 0, year;
-  gint x_loc;
-  char buffer[3];
-  gint y_loc, day_height;
+
+  guint week = 0, year;
+  gint row, x_loc, y_loc;
+  gint day_height;
+  char buffer[32];
   PangoLayout *layout;
   PangoRectangle logical_rect;
   gint focus_padding;
@@ -2028,7 +2342,17 @@ calendar_paint_week_numbers (GtkCalendar *calendar)
                              + calendar->month) % 12 + 1, calendar->day[row][6]);
       g_return_if_fail (result);
 
-      g_snprintf (buffer, sizeof (buffer), "%d", week);
+      /* Translators: this defines whether the week numbers should use
+       * localized digits or the ones used in English (0123...).
+       *
+       * Translate to "%Id" if you want to use localized digits, or
+       * translate to "%d" otherwise.
+       *
+       * Note that translating this doesn't guarantee that you get localized
+       * digits. That needs support from your system and locale definition
+       * too.
+       */
+      g_snprintf (buffer, sizeof (buffer), C_("calendar:week:digits", "%d"), week);
       pango_layout_set_text (layout, buffer, -1);
       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
@@ -2085,6 +2409,14 @@ calendar_invalidate_day (GtkCalendar *calendar,
     }
 }
 
+static gboolean
+is_color_attribute (PangoAttribute *attribute,
+                    gpointer        data)
+{
+  return (attribute->klass->type == PANGO_ATTR_FOREGROUND ||
+          attribute->klass->type == PANGO_ATTR_BACKGROUND);
+}
+
 static void
 calendar_paint_day (GtkCalendar *calendar,
                    gint             row,
@@ -2094,20 +2426,24 @@ calendar_paint_day (GtkCalendar *calendar,
   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
   cairo_t *cr;
   GdkColor *text_color;
-  gchar buffer[255];
+  gchar *detail;
+  gchar buffer[32];
   gint day;
   gint x_loc, y_loc;
   GdkRectangle day_rect;
 
   PangoLayout *layout;
   PangoRectangle logical_rect;
-  
+  gboolean overflow = FALSE;
+  gboolean show_details;
+
   g_return_if_fail (row < 6);
   g_return_if_fail (col < 7);
 
   cr = gdk_cairo_create (priv->main_win);
 
   day = calendar->day[row][col];
+  show_details = (calendar->display_flags & GTK_CALENDAR_SHOW_DETAILS);
 
   calendar_day_rectangle (calendar, row, col, &day_rect);
   
@@ -2143,25 +2479,99 @@ calendar_paint_day (GtkCalendar *calendar,
        text_color = NORMAL_DAY_COLOR (widget);
     }
 
-  g_snprintf (buffer, sizeof (buffer), "%d", day);
+  /* Translators: this defines whether the day numbers should use
+   * localized digits or the ones used in English (0123...).
+   *
+   * Translate to "%Id" if you want to use localized digits, or
+   * translate to "%d" otherwise.
+   *
+   * Note that translating this doesn't guarantee that you get localized
+   * digits. That needs support from your system and locale definition
+   * too.
+   */
+  g_snprintf (buffer, sizeof (buffer), C_("calendar:day:digits", "%d"), day);
+
+  /* Get extra information to show, if any: */
+
+  if (priv->detail_func)
+    detail = gtk_calendar_get_detail (calendar, row, col);
+  else
+    detail = NULL;
+
   layout = gtk_widget_create_pango_layout (widget, buffer);
+  pango_layout_set_alignment (layout, PANGO_ALIGN_CENTER);
   pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
   
-  x_loc = day_rect.x + day_rect.width / 2 + priv->max_day_char_width;
-  x_loc -= logical_rect.width;
-  y_loc = day_rect.y + (day_rect.height - logical_rect.height) / 2;
-  
+  x_loc = day_rect.x + (day_rect.width - logical_rect.width) / 2;
+  y_loc = day_rect.y;
+
   gdk_cairo_set_source_color (cr, text_color);
   cairo_move_to (cr, x_loc, y_loc);
   pango_cairo_show_layout (cr, layout);
-    
-  if (calendar->marked_date[day-1]
-      && calendar->day_month[row][col] == MONTH_CURRENT)
+
+  if (calendar->day_month[row][col] == MONTH_CURRENT &&
+     (calendar->marked_date[day-1] || (detail && !show_details)))
     {
       cairo_move_to (cr, x_loc - 1, y_loc);
       pango_cairo_show_layout (cr, layout);
     }
 
+  y_loc += priv->max_day_char_descent;
+
+  if (priv->detail_func && show_details)
+    {
+      cairo_save (cr);
+
+      if (calendar->selected_day == day)
+        gdk_cairo_set_source_color (cr, &widget->style->text[GTK_STATE_ACTIVE]);
+      else if (calendar->day_month[row][col] == MONTH_CURRENT)
+        gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_ACTIVE]);
+      else
+        gdk_cairo_set_source_color (cr, &widget->style->base[GTK_STATE_INSENSITIVE]);
+
+      cairo_set_line_width (cr, 1);
+      cairo_move_to (cr, day_rect.x + 2, y_loc + 0.5);
+      cairo_line_to (cr, day_rect.x + day_rect.width - 2, y_loc + 0.5);
+      cairo_stroke (cr);
+
+      cairo_restore (cr);
+
+      y_loc += 2;
+    }
+
+  if (detail && show_details)
+    {
+      gchar *markup = g_strconcat ("<small>", detail, "</small>", NULL);
+      pango_layout_set_markup (layout, markup, -1);
+      g_free (markup);
+
+      if (day == calendar->selected_day)
+        {
+          /* Stripping colors as they conflict with selection marking. */
+
+          PangoAttrList *attrs = pango_layout_get_attributes (layout);
+          PangoAttrList *colors = NULL;
+
+          if (attrs)
+            colors = pango_attr_list_filter (attrs, is_color_attribute, NULL);
+          if (colors)
+            pango_attr_list_unref (colors);
+        }
+
+      pango_layout_set_wrap (layout, PANGO_WRAP_WORD_CHAR);
+      pango_layout_set_width (layout, PANGO_SCALE * day_rect.width);
+
+      if (priv->detail_height_rows)
+        {
+          gint dy = day_rect.height - (y_loc - day_rect.y);
+          pango_layout_set_height (layout, PANGO_SCALE * dy);
+          pango_layout_set_ellipsize (layout, PANGO_ELLIPSIZE_END);
+        }
+
+      cairo_move_to (cr, day_rect.x, y_loc);
+      pango_cairo_show_layout (cr, layout);
+    }
+
   if (GTK_WIDGET_HAS_FOCUS (calendar) 
       && calendar->focus_row == row && calendar->focus_col == col)
     {
@@ -2175,17 +2585,19 @@ calendar_paint_day (GtkCalendar *calendar,
       gtk_paint_focus (widget->style, 
                       priv->main_win,
                       state,
-#if 0
-                      (calendar->selected_day == day) 
-                         ? GTK_STATE_SELECTED : GTK_STATE_NORMAL, 
-#endif
                       NULL, widget, "calendar-day",
                       day_rect.x,     day_rect.y, 
                       day_rect.width, day_rect.height);
     }
 
+  if (overflow)
+    priv->detail_overflow[row] |= (1 << col);
+  else
+    priv->detail_overflow[row] &= ~(1 << col);
+
   g_object_unref (layout);
   cairo_destroy (cr);
+  g_free (detail);
 }
 
 static void
@@ -2241,7 +2653,7 @@ calendar_paint_arrow (GtkCalendar *calendar,
        gtk_paint_arrow (widget->style, window, state, 
                         GTK_SHADOW_OUT, NULL, widget, "calendar",
                         GTK_ARROW_RIGHT, TRUE, 
-                        width/2 - 2, height/2 - 4, 8, 8);
+                        width/2 - 4, height/2 - 4, 8, 8);
     }
 }
 
@@ -2316,8 +2728,6 @@ calendar_timer (gpointer data)
   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (calendar);
   gboolean retval = FALSE;
   
-  GDK_THREADS_ENTER ();
-
   if (priv->timer)
     {
       calendar_arrow_action (calendar, priv->click_child);
@@ -2331,16 +2741,15 @@ calendar_timer (gpointer data)
           g_object_get (settings, "gtk-timeout-repeat", &timeout, NULL);
 
          priv->need_timer = FALSE;
-         priv->timer = g_timeout_add (timeout,
-                                       (GSourceFunc) calendar_timer,
-                                       (gpointer) calendar);
+         priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
+                                           timeout * SCROLL_DELAY_FACTOR,
+                                           (GSourceFunc) calendar_timer,
+                                           (gpointer) calendar, NULL);
        }
       else 
        retval = TRUE;
     }
 
-  GDK_THREADS_LEAVE ();
-
   return retval;
 }
 
@@ -2361,9 +2770,10 @@ calendar_start_spinning (GtkCalendar *calendar,
       g_object_get (settings, "gtk-timeout-initial", &timeout, NULL);
 
       priv->need_timer = TRUE;
-      priv->timer = g_timeout_add (timeout,
-                                  calendar_timer,
-                                  calendar);
+      priv->timer = gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT_IDLE,
+                                       timeout,
+                                       (GSourceFunc) calendar_timer,
+                                       (gpointer) calendar, NULL);
     }
 }
 
@@ -2663,7 +3073,7 @@ move_focus (GtkCalendar *calendar,
            gint         direction)
 {
   GtkTextDirection text_dir = gtk_widget_get_direction (GTK_WIDGET (calendar));
-
   if ((text_dir == GTK_TEXT_DIR_LTR && direction == -1) ||
       (text_dir == GTK_TEXT_DIR_RTL && direction == 1)) 
     {
@@ -2674,6 +3084,11 @@ move_focus (GtkCalendar *calendar,
          calendar->focus_col = 6;
          calendar->focus_row--;
        }
+
+      if (calendar->focus_col < 0)
+        calendar->focus_col = 6;
+      if (calendar->focus_row < 0)
+        calendar->focus_row = 5;
     }
   else 
     {
@@ -2684,6 +3099,11 @@ move_focus (GtkCalendar *calendar,
          calendar->focus_col = 0;
          calendar->focus_row++;
        }
+
+      if (calendar->focus_col < 0)
+        calendar->focus_col = 0;
+      if (calendar->focus_row < 0)
+        calendar->focus_row = 0;
     }
 }
 
@@ -2740,6 +3160,10 @@ gtk_calendar_key_press (GtkWidget   *widget,
        {
          if (calendar->focus_row > 0)
            calendar->focus_row--;
+          if (calendar->focus_row < 0)
+            calendar->focus_row = 5;
+          if (calendar->focus_col < 0)
+            calendar->focus_col = 6;
          calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
          calendar_invalidate_day (calendar, calendar->focus_row,
                                   calendar->focus_col);
@@ -2754,6 +3178,8 @@ gtk_calendar_key_press (GtkWidget   *widget,
        {
          if (calendar->focus_row < 5)
            calendar->focus_row++;
+          if (calendar->focus_col < 0)
+            calendar->focus_col = 0;
          calendar_invalidate_day (calendar, old_focus_row, old_focus_col);
          calendar_invalidate_day (calendar, calendar->focus_row,
                                   calendar->focus_col);
@@ -2763,12 +3189,12 @@ gtk_calendar_key_press (GtkWidget   *widget,
     case GDK_space:
       row = calendar->focus_row;
       col = calendar->focus_col;
-      day = calendar->day[row][col];
       
       if (row > -1 && col > -1)
        {
          return_val = TRUE;
 
+          day = calendar->day[row][col];
          if (calendar->day_month[row][col] == MONTH_PREV)
            calendar_set_month_prev (calendar);
          else if (calendar->day_month[row][col] == MONTH_NEXT)
@@ -2862,8 +3288,10 @@ gtk_calendar_focus_out (GtkWidget     *widget,
                        GdkEventFocus *event)
 {
   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
+  GtkCalendar *calendar = GTK_CALENDAR (widget);
 
-  calendar_stop_spinning (GTK_CALENDAR (widget));
+  calendar_queue_refresh (calendar);
+  calendar_stop_spinning (calendar);
   
   priv->in_drag = 0; 
 
@@ -2943,7 +3371,7 @@ gtk_calendar_drag_motion (GtkWidget      *widget,
     }
   
   target = gtk_drag_dest_find_target (widget, context, NULL);
-  if (target == GDK_NONE)
+  if (target == GDK_NONE || context->suggested_action == 0)
     gdk_drag_status (context, 0, time);
   else
     {
@@ -3001,7 +3429,8 @@ gtk_calendar_drag_data_received (GtkWidget        *widget,
        * supposed to call drag_status, not actually paste in the
        * data.
        */
-      str = gtk_selection_data_get_text (selection_data);
+      str = (gchar*) gtk_selection_data_get_text (selection_data);
+
       if (str) 
        {
          date = g_date_new ();
@@ -3020,7 +3449,7 @@ gtk_calendar_drag_data_received (GtkWidget        *widget,
     }
 
   date = g_date_new ();
-  str = gtk_selection_data_get_text (selection_data);
+  str = (gchar*) gtk_selection_data_get_text (selection_data);
   if (str) 
     {
       g_date_set_parse (date, str);
@@ -3076,7 +3505,7 @@ gtk_calendar_new (void)
  *
  * Sets display options (whether to display the heading and the month headings).
  * 
- * Deprecated: Use gtk_calendar_set_display_options() instead
+ * Deprecated: 2.4: Use gtk_calendar_set_display_options() instead
  **/
 void
 gtk_calendar_display_options (GtkCalendar             *calendar,
@@ -3218,6 +3647,9 @@ gtk_calendar_set_display_options (GtkCalendar            *calendar,
       if ((flags ^ calendar->display_flags) & GTK_CALENDAR_WEEK_START_MONDAY)
        g_warning ("GTK_CALENDAR_WEEK_START_MONDAY is ignored; the first day of the week is determined from the locale");
       
+      if ((flags ^ calendar->display_flags) & GTK_CALENDAR_SHOW_DETAILS)
+        resize++;
+
       calendar->display_flags = flags;
       if (resize)
        gtk_widget_queue_resize (GTK_WIDGET (calendar));
@@ -3260,8 +3692,7 @@ gtk_calendar_select_month (GtkCalendar *calendar,
   calendar->year  = year;
   
   calendar_compute_days (calendar);
-  
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 
   g_object_freeze_notify (G_OBJECT (calendar));
   g_object_notify (G_OBJECT (calendar), "month");
@@ -3335,8 +3766,7 @@ gtk_calendar_clear_marks (GtkCalendar *calendar)
     }
 
   calendar->num_marked_dates = 0;
-
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 /**
@@ -3416,12 +3846,150 @@ gtk_calendar_get_date (GtkCalendar *calendar,
     *day = calendar->selected_day;
 }
 
+/**
+ * gtk_calendar_set_detail_func:
+ * @calendar: a #GtkCalendar.
+ * @func: a function providing details for each day.
+ * @data: data to pass to @func invokations.
+ * @destroy: a function for releasing @data.
+ *
+ * Installs a function which provides Pango markup with detail information
+ * for each day. Examples for such details are holidays or appointments. That
+ * information is shown below each day when #GtkCalendar:show-details is set.
+ * A tooltip containing with full detail information is provided, if the entire
+ * text should not fit into the details area, or if #GtkCalendar:show-details
+ * is not set.
+ *
+ * The size of the details area can be restricted by setting the
+ * #GtkCalendar:detail-width-chars and #GtkCalendar:detail-height-rows
+ * properties.
+ *
+ * Since: 2.14
+ */
+void
+gtk_calendar_set_detail_func (GtkCalendar           *calendar,
+                              GtkCalendarDetailFunc  func,
+                              gpointer               data,
+                              GDestroyNotify         destroy)
+{
+  GtkCalendarPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CALENDAR (calendar));
+
+  priv = GTK_CALENDAR_GET_PRIVATE (calendar);
+
+  if (priv->detail_func_destroy)
+    priv->detail_func_destroy (priv->detail_func_user_data);
+
+  priv->detail_func = func;
+  priv->detail_func_user_data = data;
+  priv->detail_func_destroy = destroy;
+
+  gtk_widget_set_has_tooltip (GTK_WIDGET (calendar),
+                              NULL != priv->detail_func);
+  gtk_widget_queue_resize (GTK_WIDGET (calendar));
+}
+
+/**
+ * gtk_calendar_set_detail_width_chars:
+ * @calendar: a #GtkCalendar.
+ * @chars: detail width in characters.
+ *
+ * Updates the width of detail cells.
+ * See #GtkCalendar:detail-width-chars.
+ *
+ * Since: 2.14
+ */
+void
+gtk_calendar_set_detail_width_chars (GtkCalendar *calendar,
+                                     gint         chars)
+{
+  GtkCalendarPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CALENDAR (calendar));
+
+  priv = GTK_CALENDAR_GET_PRIVATE (calendar);
+
+  if (chars != priv->detail_width_chars)
+    {
+      priv->detail_width_chars = chars;
+      g_object_notify (G_OBJECT (calendar), "detail-width-chars");
+      gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
+    }
+}
+
+/**
+ * gtk_calendar_set_detail_height_rows:
+ * @calendar: a #GtkCalendar.
+ * @rows: detail height in rows.
+ *
+ * Updates the height of detail cells.
+ * See #GtkCalendar:detail-height-rows.
+ *
+ * Since: 2.14
+ */
+void
+gtk_calendar_set_detail_height_rows (GtkCalendar *calendar,
+                                     gint         rows)
+{
+  GtkCalendarPrivate *priv;
+
+  g_return_if_fail (GTK_IS_CALENDAR (calendar));
+
+  priv = GTK_CALENDAR_GET_PRIVATE (calendar);
+
+  if (rows != priv->detail_height_rows)
+    {
+      priv->detail_height_rows = rows;
+      g_object_notify (G_OBJECT (calendar), "detail-height-rows");
+      gtk_widget_queue_resize_no_redraw (GTK_WIDGET (calendar));
+    }
+}
+
+/**
+ * gtk_calendar_get_detail_width_chars:
+ * @calendar: a #GtkCalendar.
+ *
+ * Queries the width of detail cells, in characters.
+ * See #GtkCalendar:detail-width-chars.
+ *
+ * Since: 2.14
+ *
+ * Return value: The width of detail cells, in characters.
+ */
+gint
+gtk_calendar_get_detail_width_chars (GtkCalendar *calendar)
+{
+  g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
+  return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_width_chars;
+}
+
+/**
+ * gtk_calendar_get_detail_height_rows:
+ * @calendar: a #GtkCalendar.
+ *
+ * Queries the height of detail cells, in rows.
+ * See #GtkCalendar:detail-width-chars.
+ *
+ * Since: 2.14
+ *
+ * Return value: The height of detail cells, in rows.
+ */
+gint
+gtk_calendar_get_detail_height_rows (GtkCalendar *calendar)
+{
+  g_return_val_if_fail (GTK_IS_CALENDAR (calendar), 0);
+  return GTK_CALENDAR_GET_PRIVATE (calendar)->detail_height_rows;
+}
+
 /**
  * gtk_calendar_freeze:
  * @calendar: a #GtkCalendar
  * 
  * Does nothing. Previously locked the display of the calendar until
  * it was thawed with gtk_calendar_thaw().
+ *
+ * Deprecated: 2.8: 
  **/
 void
 gtk_calendar_freeze (GtkCalendar *calendar)
@@ -3435,6 +4003,8 @@ gtk_calendar_freeze (GtkCalendar *calendar)
  * 
  * Does nothing. Previously defrosted a calendar; all the changes made
  * since the last gtk_calendar_freeze() were displayed.
+ *
+ * Deprecated: 2.8: 
  **/
 void
 gtk_calendar_thaw (GtkCalendar *calendar)