[libegg/spread-table-dnd] Enhanced EggSpreadTableDnd
- From: Tristan Van Berkom <tvb src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libegg/spread-table-dnd] Enhanced EggSpreadTableDnd
- Date: Wed, 16 Mar 2011 18:25:04 +0000 (UTC)
commit f9847346f2fa27bc0db2f86209977118921bd816
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date: Sat Mar 12 19:28:36 2011 +0900
Enhanced EggSpreadTableDnd
o Added possibility to drag children that have no window by
setting the spread table itself to be a drag source and
starting the drags manually.
o Highlight drop targets using gtk_drag_highlight()
o Support drag'n'drop from one spread table to another,
even if the spread table is embedded in another spread table.
Currently I left all the Drag'n'Drop vfunc there with debug prints as
that might be useful to anyone picking up this example.
libegg/spreadtable/eggspreadtablednd.c | 357 ++++++++++++++++++++++++++++----
1 files changed, 313 insertions(+), 44 deletions(-)
---
diff --git a/libegg/spreadtable/eggspreadtablednd.c b/libegg/spreadtable/eggspreadtablednd.c
index 97c6f8d..edacb44 100644
--- a/libegg/spreadtable/eggspreadtablednd.c
+++ b/libegg/spreadtable/eggspreadtablednd.c
@@ -30,6 +30,41 @@
/* GtkWidgetClass */
static void egg_spread_table_dnd_realize (GtkWidget *widget);
+static gboolean egg_spread_table_dnd_motion (GtkWidget *widget,
+ GdkEventMotion *event);
+static gboolean egg_spread_table_dnd_button_press (GtkWidget *widget,
+ GdkEventButton *event);
+static gboolean egg_spread_table_dnd_button_release (GtkWidget *widget,
+ GdkEventButton *event);
+
+/* GtkWidgetClass drag-source */
+static void egg_spread_table_dnd_drag_begin (GtkWidget *widget,
+ GdkDragContext *context);
+static void egg_spread_table_dnd_drag_end (GtkWidget *widget,
+ GdkDragContext *context);
+static void egg_spread_table_dnd_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection_data,
+ guint info,
+ guint time_);
+static void egg_spread_table_dnd_drag_data_delete (GtkWidget *widget,
+ GdkDragContext *context);
+
+
+/* GtkWidgetClass drag-dest */
+static void egg_spread_table_dnd_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time_);
+static gboolean egg_spread_table_dnd_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_);
+static gboolean egg_spread_table_dnd_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_);
static void egg_spread_table_dnd_drag_data_received (GtkWidget *widget,
GdkDragContext *drag_context,
gint x,
@@ -37,6 +72,9 @@ static void egg_spread_table_dnd_drag_data_received (GtkWidget
GtkSelectionData *data,
guint info,
guint time);
+static gboolean egg_spread_table_dnd_drag_failed (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkDragResult result);
/* GtkContainerClass */
static void egg_spread_table_dnd_remove (GtkContainer *container,
@@ -56,13 +94,18 @@ static void drag_data_get (GtkWidget *widge
EggSpreadTableDnd *spread_table);
static GtkWidget *get_drag_child (EggSpreadTableDnd *spread_table,
const GtkSelectionData *selection);
-static gint get_drop_target_index (EggSpreadTableDnd *spread_table,
- GtkWidget *drag_child,
+static GtkWidget *get_child_at_position (EggSpreadTableDnd *spread_table,
gint x,
gint y);
struct _EggSpreadTableDndPrivate {
- GtkWidget *drag_child;
+ GtkWidget *drop_target; /* Remember which child widget has the drop highlight */
+
+ GtkWidget *drag_child; /* The widget that is currently being dragged, if the widget has
+ * no window and the SpreadTable is the active drag source */
+ gint pressed_button;
+ gint press_start_x;
+ gint press_start_y;
};
typedef struct {
@@ -75,6 +118,7 @@ static const GtkTargetEntry dnd_targets[] = {
{ "application/x-egg-spread-table-dnd-child", GTK_TARGET_SAME_APP, 0 }
};
+
G_DEFINE_TYPE (EggSpreadTableDnd, egg_spread_table_dnd, EGG_TYPE_SPREAD_TABLE)
static void
@@ -84,10 +128,25 @@ egg_spread_table_dnd_class_init (EggSpreadTableDndClass *class)
GtkContainerClass *container_class = GTK_CONTAINER_CLASS (class);
EggSpreadTableClass *spread_class = EGG_SPREAD_TABLE_CLASS (class);
- widget_class->realize = egg_spread_table_dnd_realize;
+ 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;
+ widget_class->motion_notify_event = egg_spread_table_dnd_motion;
+
+ /* Drag source */
+ widget_class->drag_begin = egg_spread_table_dnd_drag_begin;
+ widget_class->drag_end = egg_spread_table_dnd_drag_end;
+ widget_class->drag_data_get = egg_spread_table_dnd_drag_data_get;
+ widget_class->drag_data_delete = egg_spread_table_dnd_drag_data_delete;
+
+ /* Drag dest */
+ widget_class->drag_leave = egg_spread_table_dnd_drag_leave;
+ widget_class->drag_motion = egg_spread_table_dnd_drag_motion;
+ widget_class->drag_drop = egg_spread_table_dnd_drag_drop;
widget_class->drag_data_received = egg_spread_table_dnd_drag_data_received;
+ widget_class->drag_failed = egg_spread_table_dnd_drag_failed;
- container_class->remove = egg_spread_table_dnd_remove;
+ container_class->remove = egg_spread_table_dnd_remove;
spread_class->insert_child = egg_spread_table_dnd_insert_child;
dnd_target_atom_child = gdk_atom_intern_static_string (dnd_targets[0].target);
@@ -103,16 +162,28 @@ egg_spread_table_dnd_init (EggSpreadTableDnd *spread_table)
spread_table->priv = priv =
G_TYPE_INSTANCE_GET_PRIVATE (spread_table, EGG_TYPE_SPREAD_TABLE_DND, EggSpreadTableDndPrivate);
+ priv->pressed_button = -1;
+
/* Setup the spread table as a drag target for our target type */
- gtk_drag_dest_set (GTK_WIDGET (spread_table),
- GTK_DEST_DEFAULT_ALL,
- dnd_targets, G_N_ELEMENTS (dnd_targets),
+ gtk_drag_dest_set (GTK_WIDGET (spread_table),
+ GTK_DEST_DEFAULT_ALL,
+ dnd_targets, G_N_ELEMENTS (dnd_targets),
GDK_ACTION_MOVE);
+ /* Setup the spread table as a drag source for our target type also
+ * (to handle no-window widget children) */
+ gtk_drag_source_set (GTK_WIDGET (spread_table),
+ 0, dnd_targets, G_N_ELEMENTS (dnd_targets),
+ GDK_ACTION_MOVE);
+
gtk_widget_set_has_window (GTK_WIDGET (spread_table), TRUE);
}
/*****************************************************
+ * GObectClass *
+ *****************************************************/
+
+/*****************************************************
* GtkWidgetClass *
*****************************************************/
static void
@@ -137,7 +208,7 @@ egg_spread_table_dnd_realize (GtkWidget *widget)
attributes.event_mask = gtk_widget_get_events (widget)
| GDK_VISIBILITY_NOTIFY_MASK | GDK_EXPOSURE_MASK
| GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
- | GDK_BUTTON_MOTION_MASK;
+ | GDK_POINTER_MOTION_MASK;
attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
window = gdk_window_new (gtk_widget_get_parent_window (widget),
@@ -148,6 +219,177 @@ egg_spread_table_dnd_realize (GtkWidget *widget)
gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
}
+static gboolean
+egg_spread_table_dnd_motion (GtkWidget *widget,
+ GdkEventMotion *event)
+{
+ EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+
+ if (spread_table->priv->pressed_button >= 0 &&
+ gtk_drag_check_threshold (widget,
+ spread_table->priv->press_start_x,
+ spread_table->priv->press_start_y,
+ event->x, event->y))
+ {
+ spread_table->priv->drag_child =
+ get_child_at_position (spread_table,
+ spread_table->priv->press_start_x,
+ spread_table->priv->press_start_y);
+
+ g_print ("[widget] Pointer motion threshold reached, maybe beginning drag on child %p\n",
+ spread_table->priv->drag_child);
+
+
+ if (spread_table->priv->drag_child)
+ {
+ gtk_drag_begin (widget,
+ gtk_drag_source_get_target_list (widget),
+ GDK_ACTION_MOVE,
+ spread_table->priv->pressed_button,
+ (GdkEvent*)event);
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+static gboolean
+egg_spread_table_dnd_button_press (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+ gboolean handled = FALSE;
+
+ if (event->button == 1 && event->type == GDK_BUTTON_PRESS)
+ {
+ /* Save press to possibly begin a drag */
+ if (get_child_at_position (spread_table, event->x, event->y) &&
+ spread_table->priv->pressed_button < 0)
+ {
+ spread_table->priv->pressed_button = event->button;
+ spread_table->priv->press_start_x = event->x;
+ spread_table->priv->press_start_y = event->y;
+ }
+ }
+
+ return handled;
+}
+
+static gboolean
+egg_spread_table_dnd_button_release (GtkWidget *widget,
+ GdkEventButton *event)
+{
+ EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+
+ if (spread_table->priv->pressed_button == event->button)
+ spread_table->priv->pressed_button = -1;
+
+ return TRUE;
+}
+
+/*****************************************************
+ * GtkWidgetClass drag source *
+ *****************************************************/
+static void
+egg_spread_table_dnd_drag_begin (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ g_print ("[source] Drag begin\n");
+
+}
+
+static void
+egg_spread_table_dnd_drag_end (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ g_print ("[source] Drag end\n");
+
+}
+
+static void
+egg_spread_table_dnd_drag_data_get (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkSelectionData *selection,
+ guint info,
+ guint time_)
+{
+ EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+ EggSpreadTableDndDragData drag_data = { spread_table, NULL };
+ GdkAtom target;
+
+ g_print ("[source] Drag data get\n");
+
+ target = gtk_selection_data_get_target (selection);
+
+ if (spread_table->priv->drag_child &&
+ target == dnd_target_atom_child)
+ {
+ drag_data.child = spread_table->priv->drag_child;
+
+ gtk_selection_data_set (selection, target, 8,
+ (guchar*) &drag_data, sizeof (drag_data));
+ }
+
+}
+
+static void
+egg_spread_table_dnd_drag_data_delete (GtkWidget *widget,
+ GdkDragContext *context)
+{
+ g_print ("[source] Drag data delete\n");
+}
+
+/*****************************************************
+ * GtkWidgetClass drag dest *
+ *****************************************************/
+static void
+egg_spread_table_dnd_drag_leave (GtkWidget *widget,
+ GdkDragContext *context,
+ guint time_)
+{
+ g_print ("[dest] Drag leave\n");
+}
+
+static gboolean
+egg_spread_table_dnd_drag_motion (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_)
+{
+ EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+ GtkWidget *drop_target;
+
+ drop_target = get_child_at_position (spread_table, x, y);
+
+ if (drop_target != spread_table->priv->drop_target)
+ {
+ if (spread_table->priv->drop_target)
+ gtk_drag_unhighlight (spread_table->priv->drop_target);
+
+ spread_table->priv->drop_target = drop_target;
+
+ if (spread_table->priv->drop_target)
+ gtk_drag_highlight (spread_table->priv->drop_target);
+ }
+
+ g_print ("[dest] Drag motion at %d/%d\n", x, y);
+
+ return FALSE;
+}
+
+static gboolean
+egg_spread_table_dnd_drag_drop (GtkWidget *widget,
+ GdkDragContext *context,
+ gint x,
+ gint y,
+ guint time_)
+{
+ g_print ("[dest] Drag drop at %d/%d\n", x, y);
+
+ return FALSE;
+}
+
static void
egg_spread_table_dnd_drag_data_received (GtkWidget *widget,
GdkDragContext *drag_context,
@@ -158,22 +400,57 @@ egg_spread_table_dnd_drag_data_received (GtkWidget *widget,
guint time)
{
EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
- GtkWidget *child;
- gint index;
+ GtkWidget *drag_child;
/* Action time ! now reorder the child to the new position */
- child = get_drag_child (spread_table, data);
+ drag_child = get_drag_child (spread_table, data);
- if (child)
+ if (drag_child)
{
- /* This will return -1 if the drop was not on a widget */
- index = get_drop_target_index (spread_table, child, x, y);
+ GtkWidget *drop_widget;
+ gint index = -1, orig_index;
+ GList *children;
+
+ drop_widget = get_child_at_position (spread_table, x, y);
+
+ if (drop_widget)
+ {
+ children = gtk_container_get_children (GTK_CONTAINER (spread_table));
+ index = g_list_index (children, drop_widget);
+ orig_index = g_list_index (children, drag_child);
+ g_list_free (children);
+
+ if (index >= 0 && orig_index >= 0 && orig_index < index)
+ index--;
+ }
+
+ /* It might have been inside another spread table in the same application */
+ g_object_ref (drag_child);
+ gtk_container_remove (GTK_CONTAINER (gtk_widget_get_parent (drag_child)), drag_child);
+ egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), drag_child, index);
+ g_object_unref (drag_child);
+ }
- g_object_ref (child);
- gtk_container_remove (GTK_CONTAINER (spread_table), child);
- egg_spread_table_insert_child (EGG_SPREAD_TABLE (spread_table), child, index);
- g_object_unref (child);
+ if (spread_table->priv->drop_target)
+ {
+ gtk_drag_unhighlight (spread_table->priv->drop_target);
+ spread_table->priv->drop_target = NULL;
}
+
+ spread_table->priv->pressed_button = -1;
+
+ g_print ("[dest] Drag data received at %d/%d\n", x, y);
+
+}
+
+static gboolean
+egg_spread_table_dnd_drag_failed (GtkWidget *widget,
+ GdkDragContext *context,
+ GtkDragResult result)
+{
+ g_print ("[dest] Drag data failed\n");
+
+ return FALSE;
}
/*****************************************************
@@ -224,12 +501,16 @@ drag_data_get (GtkWidget *widget,
EggSpreadTableDndDragData drag_data = { spread_table, NULL };
GdkAtom target;
+ g_print ("drag_data_get()\n");
+
target = gtk_selection_data_get_target (selection);
if (target == dnd_target_atom_child)
{
drag_data.child = widget;
+ g_print ("drag_data_get() setting selection data\n");
+
gtk_selection_data_set (selection, target, 8,
(guchar*) &drag_data, sizeof (drag_data));
}
@@ -249,26 +530,29 @@ get_drag_child (EggSpreadTableDnd *spread_table,
data = (EggSpreadTableDndDragData*) gtk_selection_data_get_data (selection);
- g_return_val_if_fail (data->table == spread_table, NULL);
+ /* Unhighlight it here since we can drag and drop from one spread table
+ * to another */
+ if (data->table->priv->drop_target)
+ {
+ gtk_drag_unhighlight (data->table->priv->drop_target);
+ data->table->priv->drop_target = NULL;
+ }
return data->child;
}
-static gint
-get_drop_target_index (EggSpreadTableDnd *spread_table,
- GtkWidget *drag_child,
+static GtkWidget *
+get_child_at_position (EggSpreadTableDnd *spread_table,
gint x,
gint y)
{
- GtkWidget *child;
+ GtkWidget *child, *ret_child = NULL;
GList *children, *l;
GtkAllocation allocation;
- gboolean found = FALSE;
- gint i = 0, drag_child_index = -1;
children = gtk_container_get_children (GTK_CONTAINER (spread_table));
- for (i = 0, l = children; l; l = l->next, i++)
+ for (l = children; ret_child == NULL && l != NULL; l = l->next)
{
child = l->data;
@@ -277,31 +561,16 @@ get_drop_target_index (EggSpreadTableDnd *spread_table,
gtk_widget_get_allocation (child, &allocation);
- if (child == drag_child)
- drag_child_index = i;
-
if (x >= allocation.x && x <= allocation.x + allocation.width &&
y >= allocation.y && y <= allocation.y + allocation.height)
- {
- found = TRUE;
- break;
- }
+ ret_child = child;
}
g_list_free (children);
- if (found)
- {
- if (drag_child_index >= 0 && drag_child_index < i)
- return i - 1;
- else
- return i;
- }
-
- return -1;
+ return ret_child;
}
-
/*****************************************************
* API *
*****************************************************/
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]