[libegg/spread-table-dnd: 4/78] Added EggSpreadTableDnD subclass



commit 154a1638988f2fc05669edbffee94cf45dc6ed6a
Author: Tristan Van Berkom <tristan van berkom gmail com>
Date:   Fri Mar 11 00:11:08 2011 +0900

    Added EggSpreadTableDnD subclass
    
    This subclass extends EggSpreadTable to allow items to be reordered
    in the simplest way I could find.

 libegg/spreadtable/Makefile.am         |    6 +-
 libegg/spreadtable/eggspreadtablednd.c |  330 ++++++++++++++++++++++++++++++++
 libegg/spreadtable/eggspreadtablednd.h |   65 +++++++
 3 files changed, 399 insertions(+), 2 deletions(-)
---
diff --git a/libegg/spreadtable/Makefile.am b/libegg/spreadtable/Makefile.am
index 5670e9d..4d228c9 100644
--- a/libegg/spreadtable/Makefile.am
+++ b/libegg/spreadtable/Makefile.am
@@ -11,10 +11,12 @@ noinst_LTLIBRARIES = libegg-spreadtable.la
 libegg_spreadtable_la_LIBADD = $(EGG_30_LIBS)
 
 EGGSOURCES = \
-	eggspreadtable.c
+	eggspreadtable.c \
+	eggspreadtablednd.c
 
 EGGHEADERS = \
-	eggspreadtable.h
+	eggspreadtable.h \
+	eggspreadtablednd.h
 
 libegg_spreadtable_la_SOURCES = $(EGGSOURCES)
 
diff --git a/libegg/spreadtable/eggspreadtablednd.c b/libegg/spreadtable/eggspreadtablednd.c
new file mode 100644
index 0000000..f7260cd
--- /dev/null
+++ b/libegg/spreadtable/eggspreadtablednd.c
@@ -0,0 +1,330 @@
+/* gtkspreadtablednd.c
+ * Copyright (C) 2011 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include "config.h"
+#include <gtk/gtk.h>
+#include <string.h>
+#include "eggspreadtablednd.h"
+
+#define DEFAULT_LINES 2
+#define P_(msgid) (msgid)
+
+/* GtkWidgetClass */
+static void          egg_spread_table_dnd_realize            (GtkWidget         *widget);
+static void          egg_spread_table_dnd_drag_data_received (GtkWidget         *widget,
+							      GdkDragContext    *drag_context,
+							      gint               x,
+							      gint               y,
+							      GtkSelectionData  *data,
+							      guint              info,
+							      guint              time);
+
+/* GtkContainerClass */
+static void          egg_spread_table_dnd_remove       (GtkContainer      *container,
+							GtkWidget         *child);
+
+/* EggSpreadTableClass */
+static void          egg_spread_table_dnd_insert_child (EggSpreadTable    *spread_table,
+							GtkWidget         *child,
+							gint               index);
+
+/* Drag and Drop callbacks & other utilities */
+static void          drag_data_get                     (GtkWidget         *widget,
+							GdkDragContext    *context,
+							GtkSelectionData  *selection,
+							guint              info,
+							guint              time,
+							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,
+							gint               x,
+							gint               y);
+
+struct _EggSpreadTableDndPrivate {
+  GtkWidget *drag_child;
+};
+
+typedef struct {
+  EggSpreadTableDnd *table;
+  GtkWidget         *child;
+} EggSpreadTableDndDragData;
+
+static GdkAtom              dnd_target_atom_child = GDK_NONE;
+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
+egg_spread_table_dnd_class_init (EggSpreadTableDndClass *class)
+{
+  GtkWidgetClass      *widget_class    = GTK_WIDGET_CLASS (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->drag_data_received = egg_spread_table_dnd_drag_data_received;
+
+  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);
+
+  g_type_class_add_private (class, sizeof (EggSpreadTableDndPrivate));
+}
+
+static void
+egg_spread_table_dnd_init (EggSpreadTableDnd *spread_table)
+{
+  EggSpreadTableDndPrivate *priv;
+
+  spread_table->priv = priv =
+    G_TYPE_INSTANCE_GET_PRIVATE (spread_table, EGG_TYPE_SPREAD_TABLE_DND, EggSpreadTableDndPrivate);
+
+  /* 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), 
+		     GDK_ACTION_MOVE);
+
+  gtk_widget_set_has_window (GTK_WIDGET (spread_table), TRUE);
+}
+
+/*****************************************************
+ *                 GtkWidgetClass                    *
+ *****************************************************/
+static void
+egg_spread_table_dnd_realize (GtkWidget *widget)
+{
+  GtkAllocation allocation;
+  GdkWindow *window;
+  GdkWindowAttr attributes;
+  gint attributes_mask;
+
+  gtk_widget_set_realized (widget, TRUE);
+
+  gtk_widget_get_allocation (widget, &allocation);
+
+  attributes.window_type = GDK_WINDOW_CHILD;
+  attributes.x = allocation.x;
+  attributes.y = allocation.y;
+  attributes.width = allocation.width;
+  attributes.height = allocation.height;
+  attributes.wclass = GDK_INPUT_OUTPUT;
+  attributes.visual = gtk_widget_get_visual (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;
+  attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL;
+
+  window = gdk_window_new (gtk_widget_get_parent_window (widget),
+                           &attributes, attributes_mask);
+  gtk_widget_set_window (widget, window);
+  gdk_window_set_user_data (window, widget);
+
+  gtk_style_context_set_background (gtk_widget_get_style_context (widget), window);
+}
+
+static void
+egg_spread_table_dnd_drag_data_received (GtkWidget        *widget,
+					 GdkDragContext   *drag_context,
+					 gint              x,
+					 gint              y,
+					 GtkSelectionData *data,
+					 guint             info,
+					 guint             time)
+{
+  EggSpreadTableDnd *spread_table = EGG_SPREAD_TABLE_DND (widget);
+  GtkWidget         *child;
+  gint               index;
+
+  /* Action time ! now reorder the child to the new position */
+  child = get_drag_child (spread_table, data);
+
+  if (child)
+    {
+      /* This will return -1 if the drop was not on a widget */
+      index = get_drop_target_index (spread_table, child, x, y);
+
+      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);
+    }
+}
+
+/*****************************************************
+ *                GtkContainerClass                  *
+ *****************************************************/
+static void
+egg_spread_table_dnd_remove (GtkContainer *container,
+			     GtkWidget    *child)
+{
+  /* Disconnect dnd */
+  g_signal_handlers_disconnect_by_func (child, G_CALLBACK (drag_data_get), container);
+  gtk_drag_source_unset (child);
+  
+  GTK_CONTAINER_CLASS (egg_spread_table_dnd_parent_class)->remove (container, child);
+}
+
+
+/*****************************************************
+ *               EggSpreadTableClass                 *
+ *****************************************************/
+static void
+egg_spread_table_dnd_insert_child (EggSpreadTable *spread_table,
+				   GtkWidget      *child,
+				   gint            index)
+{
+  EGG_SPREAD_TABLE_CLASS (egg_spread_table_dnd_parent_class)->insert_child (spread_table, child, index);
+
+  /* Connect dnd */
+  gtk_drag_source_set (child, GDK_BUTTON1_MASK | GDK_BUTTON3_MASK,
+		       dnd_targets, G_N_ELEMENTS (dnd_targets), 
+		       GDK_ACTION_MOVE);
+
+  g_signal_connect (child, "drag-data-get", 
+		    G_CALLBACK (drag_data_get), spread_table);
+}
+
+/*****************************************************
+ *       Drag'n'Drop signals & other functions       *
+ *****************************************************/
+static void
+drag_data_get (GtkWidget         *widget,
+	       GdkDragContext    *context,
+	       GtkSelectionData  *selection,
+	       guint              info,
+	       guint              time,
+	       EggSpreadTableDnd *spread_table)
+{
+  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));
+    }
+}
+
+static GtkWidget *
+get_drag_child (EggSpreadTableDnd      *spread_table,
+		const GtkSelectionData *selection)
+{
+  EggSpreadTableDndDragData *data;
+  GdkAtom target;
+
+  g_return_val_if_fail (gtk_selection_data_get_format (selection) == 8, NULL);
+  g_return_val_if_fail (gtk_selection_data_get_length (selection) == sizeof (EggSpreadTableDndDragData), NULL);
+  target = gtk_selection_data_get_target (selection);
+  g_return_val_if_fail (target == dnd_target_atom_child, NULL);
+
+  data = (EggSpreadTableDndDragData*) gtk_selection_data_get_data (selection);
+
+  g_return_val_if_fail (data->table == spread_table, NULL);
+
+  return data->child;
+}
+
+static gint
+get_drop_target_index (EggSpreadTableDnd *spread_table,
+		       GtkWidget         *drag_child,
+		       gint               x,
+		       gint               y)
+{
+  GtkWidget    *child;
+  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++)
+    {
+      child = l->data;
+
+      if (!gtk_widget_get_visible (child))
+	continue;
+
+      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;
+	}
+    }
+
+  g_list_free (children);
+
+  if (found)
+    {
+      if (drag_child_index >= 0 && drag_child_index < i)
+	return i - 1;
+      else
+	return i;
+    }
+
+  return -1;
+}
+
+
+/*****************************************************
+ *                       API                         *
+ *****************************************************/
+
+/**
+ * egg_spread_table_dnd_new:
+ * @orientation: The #GtkOrientation for the #EggSpreadTableDnd
+ * @lines: The fixed amount of lines to distribute children to.
+ *
+ * Creates a #EggSpreadTableDnd.
+ *
+ * Returns: A new #EggSpreadTableDnd container
+ */
+GtkWidget *
+egg_spread_table_dnd_new (GtkOrientation orientation,
+			  guint          lines)
+{
+  return (GtkWidget *)g_object_new (EGG_TYPE_SPREAD_TABLE_DND,
+				    "orientation", orientation,
+				    "lines", lines,
+				    NULL);
+}
diff --git a/libegg/spreadtable/eggspreadtablednd.h b/libegg/spreadtable/eggspreadtablednd.h
new file mode 100644
index 0000000..24528f9
--- /dev/null
+++ b/libegg/spreadtable/eggspreadtablednd.h
@@ -0,0 +1,65 @@
+/* 
+ * Copyright (C) 2011 Openismus GmbH
+ *
+ * Authors:
+ *      Tristan Van Berkom <tristanvb openismus com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __EGG_SPREAD_TABLE_DND_H__
+#define __EGG_SPREAD_TABLE_DND_H__
+
+#include <gtk/gtk.h>
+#include "eggspreadtable.h"
+
+G_BEGIN_DECLS
+
+
+#define EGG_TYPE_SPREAD_TABLE_DND            (egg_spread_table_dnd_get_type ())
+#define EGG_SPREAD_TABLE_DND(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), EGG_TYPE_SPREAD_TABLE_DND, EggSpreadTableDnd))
+#define EGG_SPREAD_TABLE_DND_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), EGG_TYPE_SPREAD_TABLE_DND, EggSpreadTableDndClass))
+#define EGG_IS_SPREAD_TABLE_DND(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), EGG_TYPE_SPREAD_TABLE_DND))
+#define EGG_IS_SPREAD_TABLE_DND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), EGG_TYPE_SPREAD_TABLE_DND))
+#define EGG_SPREAD_TABLE_DND_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), EGG_TYPE_SPREAD_TABLE_DND, EggSpreadTableDndClass))
+
+typedef struct _EggSpreadTableDnd            EggSpreadTableDnd;
+typedef struct _EggSpreadTableDndPrivate     EggSpreadTableDndPrivate;
+typedef struct _EggSpreadTableDndClass       EggSpreadTableDndClass;
+
+
+struct _EggSpreadTableDnd
+{
+  EggSpreadTable parent_instance;
+
+  /*< private >*/
+  EggSpreadTableDndPrivate *priv;
+};
+
+struct _EggSpreadTableDndClass
+{
+  EggSpreadTableClass parent_class;
+};
+
+GType                 egg_spread_table_get_type                  (void) G_GNUC_CONST;
+GtkWidget            *egg_spread_table_dnd_new                   (GtkOrientation  orientation,
+								  guint           lines);
+
+
+G_END_DECLS
+
+
+#endif /* __EGG_SPREAD_TABLE_H__ */



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