[libegg] Added event boxes to EggSpreadTableDnd



commit d15ba17536e30ca26e6013eebbd5ce601c9cf996
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Tue Aug 30 21:03:07 2011 +0200

    Added event boxes to EggSpreadTableDnd
    
    This commit makes the EggSpreadTableDnd own event boxes internally
    for all added children and adds some new apis:
    
      - egg_spread_table_dnd_insert_child/remove_child()
        Custom api to add/remove children since normal container apis are
        reserved for internal use by the spread table.
    
      - egg_spread_table_set/get_steal_events()
        An added 'steal-events' property controls whether the spread-table
        should trap all pointer events over the added children (temporally
        allows drag'n'drop of widgets that also handle events).

 libegg/spreadtable/eggspreadtablednd.c |  252 +++++++++++++++++++++++++++++---
 libegg/spreadtable/eggspreadtablednd.h |   14 ++-
 2 files changed, 243 insertions(+), 23 deletions(-)
---
diff --git a/libegg/spreadtable/eggspreadtablednd.c b/libegg/spreadtable/eggspreadtablednd.c
index 2b7360a..a1008c8 100644
--- a/libegg/spreadtable/eggspreadtablednd.c
+++ b/libegg/spreadtable/eggspreadtablednd.c
@@ -30,6 +30,23 @@
 #define DEFAULT_LINES 2
 #define P_(msgid) (msgid)
 
+
+enum {
+  PROP_0,
+  PROP_STEAL_EVENTS
+};
+
+
+/* GObjectClass */
+static void          egg_spread_table_dnd_get_property      (GObject            *object,
+							     guint               prop_id,
+							     GValue             *value,
+							     GParamSpec         *pspec);
+static void          egg_spread_table_dnd_set_property      (GObject            *object,
+							     guint               prop_id,
+							     const GValue       *value,
+							     GParamSpec         *pspec);
+
 /* GtkWidgetClass */
 static void          egg_spread_table_dnd_realize            (GtkWidget         *widget);
 static gboolean      egg_spread_table_dnd_motion             (GtkWidget         *widget,
@@ -77,7 +94,7 @@ static void          egg_spread_table_dnd_remove             (GtkContainer
 							      GtkWidget         *child);
 
 /* EggSpreadTableClass */
-static void          egg_spread_table_dnd_insert_child       (EggSpreadTable    *spread_table,
+static void          egg_spread_table_dnd_insert_child_impl  (EggSpreadTable    *spread_table,
 							      GtkWidget         *child,
 							      gint               index);
 static gint          egg_spread_table_dnd_build_segments     (EggSpreadTable    *table,
@@ -146,6 +163,9 @@ struct _EggSpreadTableDndPrivate {
 
   guint      dragging : 1; /* Whether the drag'n'drop operation is currently active over this table */
 
+  guint      steal_events : 1; /* Whether to steal all child events (causes the event-boxes to
+				* place thier event window above all children) */
+
   gint       disappearing; /* Count of placeholders that are currently disappearing */
 
   /* These states are used to trigger a drag operation on a child widget with no window */
@@ -164,6 +184,7 @@ enum {
   LAST_SIGNAL
 };
 
+static GQuark		    dnd_table_child_quark = 0;
 static guint                dnd_table_signals [LAST_SIGNAL] = { 0 };
 static GdkAtom              dnd_target_atom_child = GDK_NONE;
 static const GtkTargetEntry dnd_targets[] = {
@@ -177,10 +198,14 @@ G_DEFINE_TYPE (EggSpreadTableDnd, egg_spread_table_dnd, EGG_TYPE_SPREAD_TABLE)
 static void
 egg_spread_table_dnd_class_init (EggSpreadTableDndClass *class)
 {
+  GObjectClass        *gobject_class   = G_OBJECT_CLASS (class);
   GtkWidgetClass      *widget_class    = GTK_WIDGET_CLASS (class);
   GtkContainerClass   *container_class = GTK_CONTAINER_CLASS (class);
   EggSpreadTableClass *spread_class    = EGG_SPREAD_TABLE_CLASS (class);
 
+  gobject_class->get_property        = egg_spread_table_dnd_get_property;
+  gobject_class->set_property        = egg_spread_table_dnd_set_property;
+
   widget_class->realize              = egg_spread_table_dnd_realize;
   widget_class->button_press_event   = egg_spread_table_dnd_button_press;
   widget_class->button_release_event = egg_spread_table_dnd_button_release;
@@ -200,12 +225,29 @@ egg_spread_table_dnd_class_init (EggSpreadTableDndClass *class)
 
   container_class->remove    = egg_spread_table_dnd_remove;
 
-  spread_class->insert_child            = egg_spread_table_dnd_insert_child;
+  spread_class->insert_child            = egg_spread_table_dnd_insert_child_impl;
   spread_class->build_segments_for_size = egg_spread_table_dnd_build_segments;
 
   class->widget_drop_possible = egg_spread_table_dnd_drop_possible;
 
   /**
+   * EggSpreadTableDnd:steal-events:
+   *
+   * Whether the table should steal all pointer events from added children
+   * for the purpose of Drag'n'Drop.
+   *
+   */
+  g_object_class_install_property (gobject_class,
+                                   PROP_STEAL_EVENTS,
+                                   g_param_spec_boolean ("steal-events",
+							 P_("Steal Events"),
+							 P_("Whether the table should steal all pointer "
+							    "events from children"),
+							 FALSE,
+							 G_PARAM_READABLE | G_PARAM_WRITABLE));
+
+
+  /**
    * EggSpreadTableDnd::widget-drop-possible
    *
    * Emitted to check if a widget can be dropped into this spread table.
@@ -229,6 +271,7 @@ egg_spread_table_dnd_class_init (EggSpreadTableDndClass *class)
 
 
   dnd_target_atom_child = gdk_atom_intern_static_string (dnd_targets[0].target);
+  dnd_table_child_quark = g_quark_from_static_string ("egg-spread-table-dnd-child");
 
   g_type_class_add_private (class, sizeof (EggSpreadTableDndPrivate));
 }
@@ -259,6 +302,47 @@ egg_spread_table_dnd_init (EggSpreadTableDnd *spread_table)
 }
 
 /*****************************************************
+ *                  GObectClass                      *
+ *****************************************************/
+static void
+egg_spread_table_dnd_get_property (GObject      *object,
+				   guint         prop_id,
+				   GValue       *value,
+				   GParamSpec   *pspec)
+{
+  EggSpreadTableDnd *table = EGG_SPREAD_TABLE_DND (object);
+
+  switch (prop_id)
+    {
+    case PROP_STEAL_EVENTS:
+      g_value_set_boolean (value, table->priv->steal_events);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+static void
+egg_spread_table_dnd_set_property (GObject      *object,
+				   guint         prop_id,
+				   const GValue *value,
+				   GParamSpec   *pspec)
+{
+  EggSpreadTableDnd *table = EGG_SPREAD_TABLE_DND (object);
+
+  switch (prop_id)
+    {
+    case PROP_STEAL_EVENTS:
+      egg_spread_table_dnd_set_steal_events (table, g_value_get_boolean (value));
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+}
+
+/*****************************************************
  *                 GtkWidgetClass                    *
  *****************************************************/
 static void
@@ -716,6 +800,9 @@ egg_spread_table_dnd_drag_motion (GtkWidget         *widget,
 	  get_placeholder_size (spread_table, &width, &height);
 
 	  spread_table->priv->drop_target = egg_placeholder_new (width, height);
+	  g_object_set_qdata (G_OBJECT (spread_table->priv->drop_target),
+			      dnd_table_child_quark, GINT_TO_POINTER (TRUE));
+
 	  egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table),
 					 spread_table->priv->drop_target, index);
 	  adjust_line_segment (spread_table, line, 1);
@@ -813,6 +900,14 @@ static void
 egg_spread_table_dnd_remove (GtkContainer *container,
 			     GtkWidget    *child)
 {
+  if (GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (child), dnd_table_child_quark)) == FALSE)
+    {
+      g_message ("Refusing to remove child widget from EggSpreadTableDnd directly, "
+		 "use egg_spread_table_dnd_remove_child() instead.");
+      return;
+    }
+
+
   /* Disconnect dnd */
   if (!EGG_IS_PLACEHOLDER (child))
     {
@@ -831,10 +926,17 @@ egg_spread_table_dnd_remove (GtkContainer *container,
  *               EggSpreadTableClass                 *
  *****************************************************/
 static void
-egg_spread_table_dnd_insert_child (EggSpreadTable *spread_table,
-				   GtkWidget      *child,
-				   gint            index)
+egg_spread_table_dnd_insert_child_impl (EggSpreadTable *spread_table,
+					GtkWidget      *child,
+					gint            index)
 {
+  if (GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (child), dnd_table_child_quark)) == FALSE)
+    {
+      g_message ("Refusing to add child widget to an EggSpreadTableDnd directly, "
+		 "use egg_spread_table_dnd_insert_child() instead.");
+      return;
+    }
+
   EGG_SPREAD_TABLE_CLASS (egg_spread_table_dnd_parent_class)->insert_child (spread_table, child, index);
 
   /* Connect dnd */
@@ -930,31 +1032,41 @@ static void
 set_drag_icon (GtkWidget      *widget,
 	       GdkDragContext *context)
 {
-  GtkWidget       *toplevel;
+  GtkAllocation    allocation;
   cairo_surface_t *surface;
   cairo_t         *cr;
-  gint             x, y;
+  GtkStyleContext *style;
+  GdkPixbuf       *pixbuf;
+  gint             hot_x, hot_y;
 
-  toplevel = gtk_widget_get_toplevel (widget);
+  /* XXX Force allocate here ? need to absolutely have an allocated widget
+   * for this to work (gtk_widget_draw() needs that). */
 
-  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32,
-					gtk_widget_get_allocated_width (widget),
-					gtk_widget_get_allocated_height (widget));
-  cr      = cairo_create (surface);
+  gtk_widget_get_allocation (widget, &allocation);
+  gtk_widget_get_pointer (widget, &hot_x, &hot_y);
 
-  gtk_widget_translate_coordinates (widget, toplevel, 0, 0, &x, &y);
+  surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, allocation.width, allocation.height);
+  cr      = cairo_create (surface);
 
-  cairo_translate (cr, -x, -y);
+  /* Synthetically render a background */
+  style = gtk_widget_get_style_context (widget);
+  gtk_style_context_save (style);
+  gtk_style_context_add_class (style, GTK_STYLE_CLASS_BACKGROUND);
+  gtk_render_background (style, cr, 0, 0, allocation.width, allocation.height);
+  gtk_style_context_restore (style);
 
-  gtk_widget_draw (toplevel, cr);
+  /* Draw the actual widget, this might or might not draw the background */
+  gtk_widget_draw (widget, cr);
 
-  gtk_drag_set_icon_surface (context, surface);
+  /* Make a pixbuf and use that (just to take advantage of the 'hot_x'/'hot_y' parameters) */
+  pixbuf = gdk_pixbuf_get_from_surface (surface, 0, 0, allocation.width, allocation.height);
+  gtk_drag_set_icon_pixbuf (context, pixbuf, hot_x, hot_y);
 
+  g_object_unref (pixbuf);
   cairo_destroy (cr);
   cairo_surface_destroy (surface);
 }
 
-
 static void
 drag_begin (GtkWidget         *widget,
 	    GdkDragContext    *context,
@@ -963,6 +1075,9 @@ drag_begin (GtkWidget         *widget,
   GtkAllocation   allocation;
   gint            drop_index;
 
+  /* Set the icon for the drag */
+  set_drag_icon (widget, context);
+
   /* Mark the spread table for an active drag */
   lock_table (spread_table);
   spread_table->priv->dragging = TRUE;
@@ -981,6 +1096,9 @@ drag_begin (GtkWidget         *widget,
   /* Create a placeholder of the correct dimentions and insert it at the drag origin */
   gtk_widget_get_allocation (widget, &allocation);
   spread_table->priv->drop_target = egg_placeholder_new (allocation.width, allocation.height);
+  g_object_set_qdata (G_OBJECT (spread_table->priv->drop_target),
+		      dnd_table_child_quark, GINT_TO_POINTER (TRUE));
+
 
   egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table),
 				 spread_table->priv->drop_target,
@@ -990,9 +1108,6 @@ drag_begin (GtkWidget         *widget,
   adjust_line_segment (spread_table,
 		       get_child_line (spread_table, spread_table->priv->drop_target), 1);
 
-  /* Set the icon for the drag */
-  set_drag_icon (spread_table->priv->drag_child, context);
-
   /* Hide the drag child (we cant remove it because it needs a GdkWindow in the mean time) */
   gtk_widget_hide (spread_table->priv->drag_child);
 }
@@ -1280,3 +1395,100 @@ egg_spread_table_dnd_new (GtkOrientation orientation,
 				    "lines", lines,
 				    NULL);
 }
+
+/**
+ * egg_spread_table_dnd_insert_child:
+ * @table: An #EggSpreadTableDnd
+ * @child: The child widget to insert.
+ *
+ * Adds a child widget to an #EggSpreadTableDnd.
+ *
+ * <note><para>Regular #GtkContainer apis and #EggSpreadTable
+ * apis are inappropriate for adding children as those
+ * are reserved for internal use by the #EggSpreadTableDnd.</para></note>
+ */
+void
+egg_spread_table_dnd_insert_child (EggSpreadTableDnd *table,
+				   GtkWidget         *child,
+				   gint               index)
+{
+  GtkWidget *event_box;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE_DND (table));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  event_box = gtk_event_box_new ();
+  gtk_event_box_set_above_child (GTK_EVENT_BOX (event_box), table->priv->steal_events);
+  g_object_set_qdata (G_OBJECT (event_box), dnd_table_child_quark, GINT_TO_POINTER (TRUE));
+  gtk_widget_show (event_box);
+
+  gtk_container_add (GTK_CONTAINER (event_box), child);
+  egg_spread_table_insert_child (EGG_SPREAD_TABLE (table), event_box, index);
+}
+
+/**
+ * egg_spread_table_dnd_remove_child:
+ * @table: An #EggSpreadTableDnd
+ * @child: The child widget to insert.
+ *
+ * Adds a child widget to an #EggSpreadTableDnd.
+ *
+ * <note><para>Regular #GtkContainer apis and #EggSpreadTable
+ * apis are inappropriate for removing children as those
+ * are reserved for internal use by the #EggSpreadTableDnd.</para></note>
+ */
+void
+egg_spread_table_dnd_remove_child (EggSpreadTableDnd *table,
+				   GtkWidget         *child)
+{
+  GtkWidget *event_box;
+
+  g_return_if_fail (EGG_IS_SPREAD_TABLE_DND (table));
+  g_return_if_fail (GTK_IS_WIDGET (child));
+
+  event_box = gtk_widget_get_parent (child);
+
+  if (GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (event_box), dnd_table_child_quark)) == FALSE)
+    {
+      g_message ("Bad hierarchy encountered in egg_spread_table_dnd_remove_child().");
+      return;
+    }
+
+  /* unparent the user's child and remove the intermediate spread-table owned event-box. */
+  gtk_container_remove (GTK_CONTAINER (event_box), child);
+  gtk_container_remove (GTK_CONTAINER (table), event_box);
+}
+
+static void
+flip_event_windows (GtkEventBox       *child,
+		    EggSpreadTableDnd *table)
+{
+  /* Besides the internally owned event boxes, only EggPlaceholders can exist 
+   * as direct children of the EggSpreadTableDnd */
+  if (GTK_IS_EVENT_BOX (child))
+    gtk_event_box_set_above_child (child, table->priv->steal_events);
+}
+
+void
+egg_spread_table_dnd_set_steal_events (EggSpreadTableDnd *table,
+				       gboolean           steal_events)
+{
+  g_return_if_fail (EGG_IS_SPREAD_TABLE_DND (table));
+
+  if (table->priv->steal_events != steal_events)
+    {
+      table->priv->steal_events = steal_events;
+
+      gtk_container_forall (GTK_CONTAINER (table), (GtkCallback)flip_event_windows, table);
+
+      g_object_notify (G_OBJECT (table), "steal-events");
+    }
+}
+
+gboolean
+egg_spread_table_dnd_get_steal_events (EggSpreadTableDnd *table)
+{
+  g_return_val_if_fail (EGG_IS_SPREAD_TABLE_DND (table), FALSE);
+
+  return table->priv->steal_events;
+}
diff --git a/libegg/spreadtable/eggspreadtablednd.h b/libegg/spreadtable/eggspreadtablednd.h
index 36c323b..dba5dc7 100644
--- a/libegg/spreadtable/eggspreadtablednd.h
+++ b/libegg/spreadtable/eggspreadtablednd.h
@@ -57,9 +57,17 @@ struct _EggSpreadTableDndClass
 };
 
 GType                 egg_spread_table_dnd_get_type              (void) G_GNUC_CONST;
-GtkWidget            *egg_spread_table_dnd_new                   (GtkOrientation  orientation,
-								  guint           lines);
-
+GtkWidget            *egg_spread_table_dnd_new                   (GtkOrientation     orientation,
+								  guint              lines);
+
+void                  egg_spread_table_dnd_insert_child          (EggSpreadTableDnd *table,
+								  GtkWidget         *child,
+								  gint               index);
+void                  egg_spread_table_dnd_remove_child          (EggSpreadTableDnd *table,
+								  GtkWidget         *child);
+void                  egg_spread_table_dnd_set_steal_events      (EggSpreadTableDnd *table,
+								  gboolean           steal_events);
+gboolean              egg_spread_table_dnd_get_steal_events      (EggSpreadTableDnd *table);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]