]> Pileus Git - ~andy/gtk/blobdiff - gtk/gtkcalendar.c
consistently chain up using GTK_FOO_CLASS(parent_class)->bar(instance)
[~andy/gtk] / gtk / gtkcalendar.c
index 566d7ba88a993931599ff7a9bcea5274de983447..ff5003c6fbabc1a77b7c8a747b755c29ffacdb77 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 <windows.h>
 #endif
 
-#include <glib/gprintf.h>
-
-#undef GTK_DISABLE_DEPRECATED
 #include "gtkcalendar.h"
-#define GTK_DISABLE_DEPRECATED
-
 #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"
 
@@ -188,15 +183,17 @@ dates_difference(guint year1, guint mm1, guint 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 >> 8, 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,16 +646,16 @@ gtk_calendar_init (GtkCalendar *calendar)
   time_t secs;
   struct tm *tm;
   gint i;
-  char buffer[255];
 #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;
@@ -592,23 +677,11 @@ gtk_calendar_init (GtkCalendar *calendar)
        strftime ( buffer, sizeof (buffer), "%a", gmtime (&tmp_time));
        default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
 #else
-       if (G_WIN32_HAVE_WIDECHAR_API ())
-         {
-           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);
-         }
+       if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SABBREVDAYNAME1 + (i+6)%7,
+                            wbuffer, G_N_ELEMENTS (wbuffer)))
+         default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
        else
-         {
-           if (!GetLocaleInfoA (GetThreadLocale (),
-                                (LOCALE_SABBREVDAYNAME1 + (i+6)%7) | LOCALE_USE_CP_ACP,
-                                buffer, G_N_ELEMENTS (buffer)))
-             default_abbreviated_dayname[i] = g_strdup_printf ("(%d)", i);
-           else
-             default_abbreviated_dayname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
-         }
+         default_abbreviated_dayname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
 #endif
       }
   
@@ -620,23 +693,11 @@ gtk_calendar_init (GtkCalendar *calendar)
        strftime ( buffer, sizeof (buffer), "%B", gmtime (&tmp_time));
        default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
 #else
-       if (G_WIN32_HAVE_WIDECHAR_API ())
-         {
-           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);
-         }
+       if (!GetLocaleInfoW (GetThreadLocale (), LOCALE_SMONTHNAME1 + i,
+                            wbuffer, G_N_ELEMENTS (wbuffer)))
+         default_monthname[i] = g_strdup_printf ("(%d)", i);
        else
-         {
-           if (!GetLocaleInfoA (GetThreadLocale (),
-                                (LOCALE_SMONTHNAME1 + i) | LOCALE_USE_CP_ACP,
-                                buffer, G_N_ELEMENTS (buffer)))
-             default_monthname[i] = g_strdup_printf ("(%d)", i);
-           else
-             default_monthname[i] = g_locale_to_utf8 (buffer, -1, NULL, NULL, NULL);
-         }
+         default_monthname[i] = g_utf16_to_utf8 (wbuffer, -1, NULL, NULL, NULL);
 #endif
       }
   
@@ -651,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;
@@ -689,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.
    */
@@ -699,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 */
@@ -729,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);
@@ -739,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;
   
@@ -776,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,
@@ -805,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,
@@ -834,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
@@ -850,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;
   
@@ -1142,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
@@ -1153,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);
 }
 
@@ -1175,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
@@ -1231,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;
@@ -1243,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) 
     {
@@ -1274,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;
@@ -1355,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);
       
@@ -1542,9 +1656,85 @@ 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
@@ -1562,14 +1752,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,
@@ -1628,31 +1818,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), Q_("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,
@@ -1665,13 +1856,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), Q_("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
@@ -1708,6 +1971,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);
   
@@ -2016,10 +2280,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;
@@ -2072,7 +2337,18 @@ 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.  Don't include the
+       * "calendar:week:digits|" part in the translation.
+       *
+       * 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), Q_("calendar:week:digits|%d"), week);
       pango_layout_set_text (layout, buffer, -1);
       pango_layout_get_pixel_extents (layout, NULL, &logical_rect);
 
@@ -2129,6 +2405,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,
@@ -2138,20 +2422,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);
   
@@ -2187,25 +2475,100 @@ 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.  Don't include the "calendar:day:digits|"
+   * part in the translation.
+   *
+   * 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), Q_("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)
     {
@@ -2219,17 +2582,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
@@ -2285,7 +2650,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);
     }
 }
 
@@ -2360,8 +2725,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);
@@ -2375,16 +2738,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;
 }
 
@@ -2405,9 +2767,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);
     }
 }
 
@@ -2707,7 +3070,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)) 
     {
@@ -2718,6 +3081,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 
     {
@@ -2728,6 +3096,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;
     }
 }
 
@@ -2784,6 +3157,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);
@@ -2798,6 +3175,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);
@@ -2807,12 +3186,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)
@@ -2906,10 +3285,10 @@ gtk_calendar_focus_out (GtkWidget     *widget,
                        GdkEventFocus *event)
 {
   GtkCalendarPrivate *priv = GTK_CALENDAR_GET_PRIVATE (widget);
+  GtkCalendar *calendar = GTK_CALENDAR (widget);
 
-  gtk_widget_queue_draw (widget);
-
-  calendar_stop_spinning (GTK_CALENDAR (widget));
+  calendar_queue_refresh (calendar);
+  calendar_stop_spinning (calendar);
   
   priv->in_drag = 0; 
 
@@ -2989,7 +3368,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
     {
@@ -3047,7 +3426,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 ();
@@ -3066,7 +3446,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);
@@ -3264,6 +3644,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));
@@ -3306,8 +3689,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");
@@ -3381,8 +3763,7 @@ gtk_calendar_clear_marks (GtkCalendar *calendar)
     }
 
   calendar->num_marked_dates = 0;
-
-  gtk_widget_queue_draw (GTK_WIDGET (calendar));
+  calendar_queue_refresh (calendar);
 }
 
 /**
@@ -3462,6 +3843,142 @@ 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