]> Pileus Git - ~andy/gtk/commitdiff
Improved DND support for GtkNotebook (#332991, Carlos Garnacho)
authorMatthias Clasen <mclasen@redhat.com>
Wed, 22 Mar 2006 20:17:23 +0000 (20:17 +0000)
committerMatthias Clasen <matthiasc@src.gnome.org>
Wed, 22 Mar 2006 20:17:23 +0000 (20:17 +0000)
2006-03-22  Matthias Clasen  <mclasen@redhat.com>

Improved DND support for GtkNotebook  (#332991, Carlos Garnacho)

* gtk/gtk.symbols:
* gtk/gtkdnd.h:
* gtk/gtkdnd.c: Add a track_motion flag on GtkDragDest
with getter and setter, for cases where the drag destination
is interested in drag motion events independent of targets.

* gtk/gtksettings.c (gtk_settings_class_init): Add a setting
for the timeout used when expanding during DND.

* gtk/gtknotebook.c: Use the track_motion flag to switch
notebook tabs when hovering over tabs during DND.

ChangeLog
ChangeLog.pre-2-10
gtk/gtk.symbols
gtk/gtkdnd.c
gtk/gtkdnd.h
gtk/gtknotebook.c
gtk/gtksettings.c

index 3b424f7bf832ad3f73239c99f47027b48694b4e9..f66998098d659813c675d0cb9469422a000a1458 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2006-03-22  Matthias Clasen  <mclasen@redhat.com>
+
+       Improved DND support for GtkNotebook  (#332991, Carlos Garnacho)
+       
+       * gtk/gtk.symbols: 
+       * gtk/gtkdnd.h: 
+       * gtk/gtkdnd.c: Add a track_motion flag on GtkDragDest
+       with getter and setter, for cases where the drag destination
+       is interested in drag motion events independent of targets.
+
+       * gtk/gtksettings.c (gtk_settings_class_init): Add a setting
+       for the timeout used when expanding during DND.
+
+       * gtk/gtknotebook.c: Use the track_motion flag to switch
+       notebook tabs when hovering over tabs during DND.
+       
 2006-03-22  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Use fg_gc to
index 3b424f7bf832ad3f73239c99f47027b48694b4e9..f66998098d659813c675d0cb9469422a000a1458 100644 (file)
@@ -1,3 +1,19 @@
+2006-03-22  Matthias Clasen  <mclasen@redhat.com>
+
+       Improved DND support for GtkNotebook  (#332991, Carlos Garnacho)
+       
+       * gtk/gtk.symbols: 
+       * gtk/gtkdnd.h: 
+       * gtk/gtkdnd.c: Add a track_motion flag on GtkDragDest
+       with getter and setter, for cases where the drag destination
+       is interested in drag motion events independent of targets.
+
+       * gtk/gtksettings.c (gtk_settings_class_init): Add a setting
+       for the timeout used when expanding during DND.
+
+       * gtk/gtknotebook.c: Use the track_motion flag to switch
+       notebook tabs when hovering over tabs during DND.
+       
 2006-03-22  Matthias Clasen  <mclasen@redhat.com>
 
        * gtk/gtktreeview.c (gtk_tree_view_bin_expose): Use fg_gc to
index a4520a3224ed87356cb9b040a83d36ca0d30ce02..24231aa871489e01b9ba4ee2ba75a9689d35c9ae 100644 (file)
@@ -996,6 +996,8 @@ gtk_drag_dest_get_target_list
 gtk_drag_dest_set
 gtk_drag_dest_set_proxy
 gtk_drag_dest_set_target_list
+gtk_drag_dest_set_track_motion
+gtk_drag_dest_get_track_motion
 gtk_drag_dest_unset
 gtk_drag_finish
 gtk_drag_get_data
index 66a836c7511e3bee3533df1b1e5437c57da24fbb..4ab402dcf471579849289745d32f37bd8c698042 100644 (file)
@@ -131,6 +131,7 @@ struct _GtkDragDestSite
   guint              do_proxy : 1;
   guint              proxy_coords : 1;
   guint              have_drag : 1;
+  guint              track_motion : 1;
 };
   
 struct _GtkDragDestInfo 
@@ -1026,6 +1027,8 @@ gtk_drag_dest_set_internal (GtkWidget       *widget,
       g_signal_handlers_disconnect_by_func (widget,
                                            gtk_drag_dest_hierarchy_changed,
                                            old_site);
+
+      site->track_motion = old_site->track_motion;
     }
 
   if (GTK_WIDGET_REALIZED (widget))
@@ -1272,6 +1275,61 @@ gtk_drag_dest_add_uri_targets (GtkWidget *widget)
   gtk_target_list_unref (target_list);
 }
 
+/**
+ * gtk_drag_dest_set_track_motion:
+ * @widget: a #GtkWidget that's a drag destination
+ * @track_motion: whether to accept all targets
+ * 
+ * Tells the widget to emit ::drag-motion and ::drag-leave
+ * events regardless of the targets and the %GTK_DEST_DEFAULT_MOTION
+ * flag. 
+ *
+ * This may be used when a widget wants to do generic
+ * actions regardless of the targets that the source offers.
+ *
+ * Since: 2.10
+ **/
+void
+gtk_drag_dest_set_track_motion (GtkWidget *widget,
+                               gboolean   track_motion)
+{
+  GtkDragDestSite *site;
+
+  g_return_if_fail (GTK_IS_WIDGET (widget));
+
+  site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
+  
+  g_return_if_fail (site != NULL);
+
+  site->track_motion = track_motion != FALSE;
+}
+
+/**
+ * gtk_drag_dest_get_track_motion:
+ * @widget: a #GtkWidget that's a drag destination
+ * 
+ * Returns whether the widget has been configured to always
+ * emit ::drag-motion signals.
+ * 
+ * Return Value: %TRUE if the widget always emits ::drag-motion events
+ *
+ * Since: 2.10
+ **/
+gboolean
+gtk_drag_dest_get_track_motion (GtkWidget *widget)
+{
+  GtkDragDestSite *site;
+
+  g_return_val_if_fail (GTK_IS_WIDGET (widget), FALSE);
+
+  site = g_object_get_data (G_OBJECT (widget), "gtk-drag-dest");
+
+  if (site)
+    return site->track_motion;
+
+  return FALSE;
+}
+
 /*************************************************************
  * _gtk_drag_dest_handle_event:
  *     Called from widget event handling code on Drag events
@@ -1352,7 +1410,7 @@ _gtk_drag_dest_handle_event (GtkWidget *toplevel,
        data.callback = (event->type == GDK_DRAG_MOTION) ?
          gtk_drag_dest_motion : gtk_drag_dest_drop;
        data.time = event->dnd.time;
-       
+
        gtk_drag_find_widget (toplevel, &data);
 
        if (info->widget && !data.found)
@@ -1856,9 +1914,9 @@ gtk_drag_dest_leave (GtkWidget      *widget,
       if ((site->flags & GTK_DEST_DEFAULT_HIGHLIGHT) && site->have_drag)
        gtk_drag_unhighlight (widget);
 
-      if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag)
-       g_signal_emit_by_name (widget, "drag_leave",
-                              context, time);
+      if (!(site->flags & GTK_DEST_DEFAULT_MOTION) || site->have_drag ||
+         site->track_motion)
+       g_signal_emit_by_name (widget, "drag_leave", context, time);
       
       site->have_drag = FALSE;
     }
@@ -1935,7 +1993,7 @@ gtk_drag_dest_motion (GtkWidget        *widget,
        {
          gint i;
          
-         for (i=0; i<8; i++)
+         for (i = 0; i < 8; i++)
            {
              if ((site->actions & (1 << i)) &&
                  (context->actions & (1 << i)))
@@ -1945,7 +2003,7 @@ gtk_drag_dest_motion (GtkWidget        *widget,
                }
            }
        }
-      
+
       if (action && gtk_drag_dest_find_target (widget, context, NULL))
        {
          if (!site->have_drag)
@@ -1954,13 +2012,14 @@ gtk_drag_dest_motion (GtkWidget      *widget,
              if (site->flags & GTK_DEST_DEFAULT_HIGHLIGHT)
                gtk_drag_highlight (widget);
            }
-         
+
          gdk_drag_status (context, action, time);
        }
       else
        {
          gdk_drag_status (context, 0, time);
-         return TRUE;
+         if (!site->track_motion)
+           return TRUE;
        }
     }
 
@@ -2055,13 +2114,13 @@ gtk_drag_dest_drop (GtkWidget        *widget,
       if (site->flags & GTK_DEST_DEFAULT_DROP)
        {
          GdkAtom target = gtk_drag_dest_find_target (widget, context, NULL);
-      
+
          if (target == GDK_NONE)
            {
              gtk_drag_finish (context, FALSE, FALSE, time);
              return TRUE;
            }
-         else
+         else 
            gtk_drag_get_data (widget, context, target, time);
        }
 
index e82d6c30630df1532d7c69951f983e4a143d1762..626ef00597fa6126043fb9469273cb132caaa4f0 100644 (file)
@@ -1,3 +1,4 @@
+/* -*- Mode: C; c-file-style: "gnu"; tab-width: 8 -*- */
 /* GTK - The GIMP Toolkit
  * Copyright (C) 1995-1997 Peter Mattis, Spencer Kimball and Josh MacDonald
  *
@@ -87,6 +88,10 @@ void           gtk_drag_dest_add_text_targets  (GtkWidget    *widget);
 void           gtk_drag_dest_add_image_targets (GtkWidget    *widget);
 void           gtk_drag_dest_add_uri_targets   (GtkWidget    *widget);
 
+void           gtk_drag_dest_set_track_motion  (GtkWidget *widget,
+                                               gboolean   track_motion);
+gboolean       gtk_drag_dest_get_track_motion  (GtkWidget *widget);
+
 /* Source side */
 
 void gtk_drag_source_set  (GtkWidget            *widget,
index 016d8eb6ecec66784a5ab2df147e77e67a60a338..b0017891f1ecef806cc9b062b569e74ea5354b82 100644 (file)
@@ -160,16 +160,21 @@ struct _GtkNotebookPrivate
   gint  mouse_y;
   gint  pressed_button;
   guint dnd_timer;
+  guint switch_tab_timer;
   GtkTargetList *source_targets;
   gboolean during_detach;
   gboolean has_scrolled;
 };
 
-static const GtkTargetEntry notebook_targets[] = {
+static const GtkTargetEntry notebook_source_targets [] = {
   { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 },
   { "application/x-rootwindow-drop", 0, 1 }
 };
 
+static const GtkTargetEntry notebook_dest_targets[] = {
+  { "GTK_NOTEBOOK_TAB", GTK_TARGET_SAME_APP, 0 }
+};
+
 #ifdef G_DISABLE_CHECKS
 #define CHECK_FIND_CHILD(notebook, child)                           \
  gtk_notebook_find_child (notebook, child, G_STRLOC)
@@ -251,6 +256,9 @@ static gboolean gtk_notebook_drag_motion     (GtkWidget        *widget,
                                              gint              x,
                                              gint              y,
                                              guint             time);
+static void gtk_notebook_drag_leave          (GtkWidget        *widget,
+                                             GdkDragContext   *context,
+                                             guint             time);
 static gboolean gtk_notebook_drag_drop       (GtkWidget        *widget,
                                              GdkDragContext   *context,
                                              gint              x,
@@ -501,6 +509,7 @@ gtk_notebook_class_init (GtkNotebookClass *class)
   widget_class->style_set = gtk_notebook_style_set;
   widget_class->drag_begin = gtk_notebook_drag_begin;
   widget_class->drag_motion = gtk_notebook_drag_motion;
+  widget_class->drag_leave = gtk_notebook_drag_leave;
   widget_class->drag_drop = gtk_notebook_drag_drop;
   widget_class->drag_data_get = gtk_notebook_drag_data_get;
   widget_class->drag_data_received = gtk_notebook_drag_data_received;
@@ -985,13 +994,16 @@ gtk_notebook_init (GtkNotebook *notebook)
   priv->group_id = -1;
   priv->pressed_button = -1;
   priv->dnd_timer = 0;
-  priv->source_targets = gtk_target_list_new (notebook_targets,
-                                             G_N_ELEMENTS (notebook_targets));
+  priv->switch_tab_timer = 0;
+  priv->source_targets = gtk_target_list_new (notebook_source_targets,
+                                             G_N_ELEMENTS (notebook_source_targets));
 
   gtk_drag_dest_set (GTK_WIDGET (notebook),
                     GTK_DEST_DEFAULT_MOTION | GTK_DEST_DEFAULT_DROP,
-                    notebook_targets, G_N_ELEMENTS (notebook_targets),
+                    notebook_dest_targets, G_N_ELEMENTS (notebook_dest_targets),
                      GDK_ACTION_MOVE);
+
+  gtk_drag_dest_set_track_motion (GTK_WIDGET (notebook), TRUE);
 }
 
 static gboolean
@@ -1266,7 +1278,13 @@ gtk_notebook_destroy (GtkObject *object)
       gtk_target_list_unref (priv->source_targets);
       priv->source_targets = NULL;
     }
-  
+
+  if (priv->switch_tab_timer)
+    {
+      g_source_remove (priv->switch_tab_timer);
+      priv->switch_tab_timer = 0;
+    }
+
   GTK_OBJECT_CLASS (parent_class)->destroy (object);
 }
 
@@ -2084,7 +2102,7 @@ gtk_notebook_do_arrow (GtkNotebook     *notebook,
 static gboolean
 gtk_notebook_arrow_button_press (GtkNotebook      *notebook,
                                 GtkNotebookArrow  arrow,
-                                GdkEventButton   *event)
+                                gint              button)
 {
   GtkWidget *widget = GTK_WIDGET (notebook);
   gboolean is_rtl = gtk_widget_get_direction (widget) == GTK_TEXT_DIR_RTL;
@@ -2094,10 +2112,10 @@ gtk_notebook_arrow_button_press (GtkNotebook      *notebook,
   if (!GTK_WIDGET_HAS_FOCUS (widget))
     gtk_widget_grab_focus (widget);
   
-  notebook->button = event->button;
+  notebook->button = button;
   notebook->click_child = arrow;
-  
-  if (event->button == 1)
+
+  if (button == 1)
     {
       gtk_notebook_do_arrow (notebook, arrow);
       
@@ -2114,9 +2132,9 @@ gtk_notebook_arrow_button_press (GtkNotebook      *notebook,
          notebook->need_timer = TRUE;
        }
     }
-  else if (event->button == 2)
+  else if (button == 2)
     gtk_notebook_page_select (notebook, TRUE);
-  else if (event->button == 3)
+  else if (button == 3)
     gtk_notebook_switch_focus_tab (notebook,
                                   gtk_notebook_search_page (notebook,
                                                             NULL,
@@ -2195,6 +2213,30 @@ gtk_notebook_scroll (GtkWidget      *widget,
   return TRUE;
 }
 
+static GList*
+get_tab_at_pos (GtkNotebook *notebook, gint x, gint y)
+{
+  GtkNotebookPage *page;
+  GList *children = notebook->children;
+
+  while (children)
+    {
+      page = children->data;
+      
+      if (GTK_WIDGET_VISIBLE (page->child) &&
+         page->tab_label && GTK_WIDGET_MAPPED (page->tab_label) &&
+         (x >= page->allocation.x) &&
+         (y >= page->allocation.y) &&
+         (x <= (page->allocation.x + page->allocation.width)) &&
+         (y <= (page->allocation.y + page->allocation.height)))
+       return children;
+
+      children = children->next;
+    }
+
+  return NULL;
+}
+
 static gboolean
 gtk_notebook_button_press (GtkWidget      *widget,
                           GdkEventButton *event)
@@ -2202,9 +2244,8 @@ gtk_notebook_button_press (GtkWidget      *widget,
   GtkNotebook *notebook = GTK_NOTEBOOK (widget);
   GtkNotebookPrivate *priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
   GtkNotebookPage *page;
-  GList *children;
+  GList *tab;
   GtkNotebookArrow arrow;
-  gint num;
   gint x, y;
 
   if (event->type != GDK_BUTTON_PRESS || !notebook->children ||
@@ -2216,7 +2257,7 @@ gtk_notebook_button_press (GtkWidget      *widget,
 
   arrow = gtk_notebook_get_arrow (notebook, x, y);
   if (arrow)
-    return gtk_notebook_arrow_button_press (notebook, arrow, event);
+    return gtk_notebook_arrow_button_press (notebook, arrow, event->button);
 
   if (event->button == 3 && notebook->menu)
     {
@@ -2229,41 +2270,28 @@ gtk_notebook_button_press (GtkWidget      *widget,
     return FALSE;
 
   notebook->button = event->button;
-  num = 0;
-  children = notebook->children;
 
-  while (children)
+  if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
     {
-      page = children->data;
-      
-      if (GTK_WIDGET_VISIBLE (page->child) &&
-         page->tab_label && GTK_WIDGET_MAPPED (page->tab_label) &&
-         (x >= page->allocation.x) &&
-         (y >= page->allocation.y) &&
-         (x <= (page->allocation.x + page->allocation.width)) &&
-         (y <= (page->allocation.y + page->allocation.height)))
-       {
-         gboolean page_changed = page != notebook->cur_page;
-         gboolean was_focus = gtk_widget_is_focus (widget);
+      gboolean page_changed, was_focus;
+
+      page = tab->data;
+      page_changed = page != notebook->cur_page;
+      was_focus = gtk_widget_is_focus (widget);
          
-         gtk_notebook_switch_focus_tab (notebook, children);
-         gtk_widget_grab_focus (widget);
+      gtk_notebook_switch_focus_tab (notebook, tab);
+      gtk_widget_grab_focus (widget);
          
-         if (page_changed && !was_focus)
-           gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
+      if (page_changed && !was_focus)
+       gtk_widget_child_focus (page->child, GTK_DIR_TAB_FORWARD);
          
-         /* save press to possibly begin a drag */
-         if (page->reorderable || page->detachable)
-           {
-             priv->during_detach = FALSE;
-             priv->pressed_button = event->button;
-             gtk_grab_add (widget);
-           }
-
-         break;
+      /* save press to possibly begin a drag */
+      if (page->reorderable || page->detachable)
+       {
+         priv->during_detach = FALSE;
+         priv->pressed_button = event->button;
+         gtk_grab_add (widget);
        }
-      children = children->next;
-      num++;
     }
 
   return TRUE;
@@ -2799,6 +2827,33 @@ gtk_notebook_drag_begin (GtkWidget        *widget,
   gtk_notebook_pages_allocate (GTK_NOTEBOOK (widget), DRAG_OPERATION_DETACH);
 }
 
+static gboolean
+gtk_notebook_switch_tab_timeout (gpointer data)
+{
+  GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
+  GList *tab;
+  gint x, y;
+
+  notebook = GTK_NOTEBOOK (data);
+  priv = GTK_NOTEBOOK_GET_PRIVATE (notebook);
+
+  priv->switch_tab_timer = 0;
+  x = priv->mouse_x;
+  y = priv->mouse_y;
+
+  if ((tab = get_tab_at_pos (notebook, x, y)) != NULL)
+    {
+      /* FIXME: hack, we don't want the
+       * focus to move fom the source widget
+       */
+      notebook->child_has_focus = FALSE;
+      gtk_notebook_switch_focus_tab (notebook, tab);
+    }
+
+  return FALSE;
+}
+
 static gboolean
 gtk_notebook_drag_motion (GtkWidget      *widget,
                          GdkDragContext *context,
@@ -2806,24 +2861,102 @@ gtk_notebook_drag_motion (GtkWidget      *widget,
                          gint            y,
                          guint           time)
 {
-  GtkWidget *source_widget;
-  gint widget_group, source_widget_group;
-  
-  source_widget = gtk_drag_get_source_widget (context);
-  
-  g_assert (source_widget);
+  GtkNotebook *notebook;
+  GtkNotebookPrivate *priv;
+  GdkRectangle position;
+  GtkSettings *settings;
+  GtkNotebookArrow arrow;
+  guint timeout;
+  GdkAtom target, tab_target;
+
+  notebook = GTK_NOTEBOOK (widget);
+  target = gtk_drag_dest_find_target (widget, context, NULL);
+  tab_target = gdk_atom_intern_static_string ("GTK_NOTEBOOK_TAB");
 
-  widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (widget));
-  source_widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (source_widget));
+  if (target == tab_target)
+    {
+      gint widget_group, source_widget_group;
+      GtkWidget *source_widget;
 
-  if (widget_group != -1 &&
-      source_widget_group != -1 &&
-      widget_group == source_widget_group)
-    return TRUE;
+      source_widget = gtk_drag_get_source_widget (context);
 
-  gdk_drag_status (context, 0, time);
+      g_assert (source_widget);
 
-  return FALSE;
+      widget_group = gtk_notebook_get_group_id (notebook);
+      source_widget_group = gtk_notebook_get_group_id (GTK_NOTEBOOK (source_widget));
+
+      if (widget_group != -1 &&
+         source_widget_group != -1 &&
+         widget_group == source_widget_group)
+       {
+         gdk_drag_status (context, GDK_ACTION_MOVE, time);
+         return TRUE;
+       }
+    }
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+  x += widget->allocation.x;
+  y += widget->allocation.y;
+
+  if (target == tab_target)
+    {
+      /* it's a tab, but doesn't share
+       * ID with this notebook */
+      gdk_drag_status (context, 0, time);
+    }
+
+  if (gtk_notebook_get_event_window_position (notebook, &position) &&
+      x >= position.x && x <= position.x + position.width &&
+      y >= position.y && y <= position.y + position.height)
+    {
+      priv->mouse_x = x;
+      priv->mouse_y = y;
+
+      arrow = gtk_notebook_get_arrow (notebook, x, y);
+
+      if (arrow)
+       return gtk_notebook_arrow_button_press (notebook, arrow, 1);
+
+      stop_scrolling (notebook);
+
+      if (!priv->switch_tab_timer)
+       {
+          settings = gtk_widget_get_settings (widget);
+
+          g_object_get (settings, "gtk-timeout-expand", &timeout, NULL);
+         priv->switch_tab_timer = g_timeout_add (timeout,
+                                                 (GSourceFunc) gtk_notebook_switch_tab_timeout,
+                                                 widget);
+       }
+    }
+  else
+    {
+      if (priv->switch_tab_timer)
+       {
+         g_source_remove (priv->switch_tab_timer);
+         priv->switch_tab_timer = 0;
+       }
+    }
+
+  return TRUE;
+}
+
+static void
+gtk_notebook_drag_leave (GtkWidget      *widget,
+                        GdkDragContext *context,
+                        guint           time)
+{
+  GtkNotebookPrivate *priv;
+
+  priv = GTK_NOTEBOOK_GET_PRIVATE (widget);
+
+  if (priv->switch_tab_timer)
+    {
+      g_source_remove (priv->switch_tab_timer);
+      priv->switch_tab_timer = 0;
+    }
+
+  stop_scrolling (GTK_NOTEBOOK (widget));
 }
 
 static gboolean
index ea2f375ec073614ed8516b5e3834c91250114f6b..1dd700cbc82bbfb60308ddb4d2c3ca874360a1da 100644 (file)
@@ -34,6 +34,7 @@
 
 #define DEFAULT_TIMEOUT_INITIAL 200
 #define DEFAULT_TIMEOUT_REPEAT   20
+#define DEFAULT_TIMEOUT_EXPAND  500
 
 typedef struct _GtkSettingsValuePrivate GtkSettingsValuePrivate;
 
@@ -87,6 +88,7 @@ enum {
   PROP_SHOW_UNICODE_MENU,
   PROP_TIMEOUT_INITIAL,
   PROP_TIMEOUT_REPEAT,
+  PROP_TIMEOUT_EXPAND,
   PROP_COLOR_SCHEME,
   PROP_ENABLE_ANIMATIONS,
   PROP_TOUCHSCREEN_MODE,
@@ -452,6 +454,16 @@ gtk_settings_class_init (GtkSettingsClass *class)
 
   g_assert (result == PROP_TIMEOUT_REPEAT);
 
+  result = settings_install_property_parser (class,
+                                            g_param_spec_int ("gtk-timeout-expand",
+                                                              P_("Expand timeout"),
+                                                              P_("Expand value for timeouts, when a widget is expanding a new region"),
+                                                              0, G_MAXINT, DEFAULT_TIMEOUT_EXPAND,
+                                                              GTK_PARAM_READWRITE),
+                                            NULL);
+
+  g_assert (result == PROP_TIMEOUT_EXPAND);
+
   /**
    * GtkSettings:gtk-color-scheme:
    *