GTK+ By Example/Tree View/DnD

Drag'n'Drop (DnD) **** needs revision *** edit

This section needs revision more than any other section. If you know anything about tree view drag'n'drop, you probably know more than the author of this text. Please give some feedback in that case.

If you want to dive into treeview drag'n'drop, you might want to check out Owen Taylor's mail on that topic. It might not be completely identical to what has actually been implemented, but it gives a great overview, and provides more information than the docs do.

In addition to the standard Gtk+ Drag and Drop mechanisms that work with any widget, there are special Drag and Drop mechanisms just for the tree view widget. You usually want to use the tree-view specific Drag-and-Drop framework.

Drag'n'Dropping Row-Unrelated Data to and from a Tree View from other Windows or Widgets edit

Drag'n'Dropping general information from or to a tree view widget works just like it works with any other widget and involves the standard Gtk+ Drag and Drop mechanisms. If you use this, you can receive drops to or initiate drags from anywhere in your tree view (including empty sections). This is not row- or column-specific and is most likely not want you want. Nevertheless, here is a small example of a tree view in which you can drag'n'drop URIs from other applications (browsers, for example), with the dropped URIs just being appended to the list (note that usually you would probably rather want to set up your whole window as a target then and not just the tree view widget):

#include <gtk/gtk.h>

enum
{
  COL_URI = 0,
  NUM_COLS
} ;


void
view_onDragDataReceived(GtkWidget *wgt, GdkDragContext *context, int x, int y,
                        GtkSelectionData *seldata, guint info, guint time,
                        gpointer userdata)
{
  GtkTreeModel *model;
  GtkTreeIter   iter;

  model = GTK_TREE_MODEL(userdata);

  gtk_list_store_append(GTK_LIST_STORE(model), &iter);

  gtk_list_store_set(GTK_LIST_STORE(model), &iter, COL_URI, (gchar*)seldata->data, -1);
}

static GtkWidget *
create_view_and_model (void)
{
  GtkTreeViewColumn   *col;
  GtkCellRenderer     *renderer;
  GtkListStore        *liststore;
  GtkWidget           *view;

  liststore = gtk_list_store_new(NUM_COLS, G_TYPE_STRING);

  view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(liststore));

  g_object_unref(liststore); /* destroy model with view */

  col = gtk_tree_view_column_new();
  renderer = gtk_cell_renderer_text_new();

  gtk_tree_view_column_set_title(col, "URI");
  gtk_tree_view_append_column(GTK_TREE_VIEW(view), col);
  gtk_tree_view_column_pack_start(col, renderer, TRUE);
  gtk_tree_view_column_add_attribute(col, renderer, "text", COL_URI);

  gtk_tree_selection_set_mode(gtk_tree_view_get_selection(GTK_TREE_VIEW(view)),
                              GTK_SELECTION_SINGLE);

  /* Make tree view a destination for Drag'n'Drop */
  if (1)
  {
    enum
    {
      TARGET_STRING,
      TARGET_URL
    };

    static GtkTargetEntry targetentries[] =
    {
      { "STRING",        0, TARGET_STRING },
      { "text/plain",    0, TARGET_STRING },
      { "text/uri-list", 0, TARGET_URL },
    };

    gtk_drag_dest_set(view, GTK_DEST_DEFAULT_ALL, targetentries, 3,
                      GDK_ACTION_COPY|GDK_ACTION_MOVE|GDK_ACTION_LINK);

    g_signal_connect(view, "drag_data_received",
                     G_CALLBACK(view_onDragDataReceived), liststore);
  }

  return view;
}

int
main (int argc, char **argv)
{
  GtkWidget *window, *vbox, *view, *label;

  gtk_init(&argc, &argv);

  window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
  g_signal_connect(window, "delete_event", gtk_main_quit, NULL); /* dirty */
  gtk_window_set_default_size(GTK_WINDOW(window), 400, 200);

  vbox = gtk_vbox_new(FALSE, 0);
  gtk_container_add(GTK_CONTAINER(window), vbox);

  label = gtk_label_new("\nDrag and drop links from your browser into the tree view.\n");
  gtk_box_pack_start(GTK_BOX(vbox), label, FALSE, FALSE, 0);

  view = create_view_and_model();
  gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);

  gtk_widget_show_all(window);

  gtk_main();

  return 0;
}

If you are receiving drops into a tree view, you can connect to the view's "drag-motion" signal to track the mouse pointer while it is in a drag and drop operation over the tree view. This is useful for example if you want to expand a collapsed node in a tree when the mouse hovers above the node for a certain amount of time during a drag'n'drop operation. Here is an example of how to achieve this:

/***************************************************************************
 *
 *   onDragMotion_expand_timeout
 *
 *   Timeout used to make sure that we expand rows only
 *    after hovering about them for a certain amount
 *    of time while doing Drag'n'Drop
 *
 ***************************************************************************/

gboolean
onDragMotion_expand_timeout (GtkTreePath **path)
{
        g_return_val_if_fail (  path != NULL, FALSE );
        g_return_val_if_fail ( *path != NULL, FALSE );

        gtk_tree_view_expand_row(GTK_TREE_VIEW(view), *path, FALSE);

        return FALSE; /* only call once */
}


/***************************************************************************
 *
 *   view_onDragMotion: we don't want to expand unexpanded nodes
 *                      immediately when the mouse pointer passes across
 *                      them during DnD. Instead, we only want to expand
 *                      the node if the pointer has been hovering above the
 *                      node for at least 1.5 seconds or so. To achieve this,
 *                      we use a timeout that is removed whenever the row
 *                      in focus changes.
 *
 ***************************************************************************/

static gboolean
view_onDragMotion (GtkWidget *widget, GdkDragContext *context, gint x,
                   gint y, guint time, gpointer data)
{
  static GtkTreePath  *lastpath;  /* NULL */
  GtkTreePath         *path = NULL;

  if (gtk_tree_view_get_path_at_pos(GTK_TREE_VIEW(widget), x, y, &path, NULL, NULL, NULL))
  {
    if (!lastpath || ((lastpath) && gtk_tree_path_compare(lastpath, path) != 0))
    {
      (void) g_source_remove_by_user_data(&lastpath);

      if (!gtk_tree_view_row_expanded(GTK_TREE_VIEW(widget), path))
      {
        /* 1500 = 1.5 secs */
        g_timeout_add(1500, (GSourceFunc) onDragMotion_expand_timeout, &lastpath);
      }
    }
  }
  else
  {
    g_source_remove_by_user_data(&lastpath);
  }

  if (lastpath)
    gtk_tree_path_free(lastpath);

  lastpath = path;

  return TRUE;
}

Connect to the view's "drag-drop" signal to be called when the drop happens. You can translate the coordinates provided into a tree path with gtk_tree_view_get_path_at_pos.

Dragging Rows Around Within a Tree edit

Both GtkListStore and GtkTreeStore implement the GtkTreeDragDest and GtkTreeDragSource interfaces, which means that they have in-built support for row reordering. You need to call gtk_tree_view_set_reorderable to activate this, and then connect to the tree model's signals to catch the reorderings that take place.

Example shown below

#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
#define DESCRIPTION "Drag and Drop within a Treeview - by Pierre-Louis Malatray"
 
/* Row data structure */
struct DATA { 
        char *row;
        char *item;
        int qty;
        float price;
};
 
/* A convenience enumerator to tag data types */
enum {
        TARGET_STRING,
        TARGET_INTEGER,
        TARGET_FLOAT
};
 
/* A convenience enumerator to count the columns */
enum {
        ROW_COL=0,
        ITEM_COL,
        QTY_COL,
        PRICE_COL,
        NUM_COLS
};
 
/* Some sample data for treeview 1. A NULL row is added so we dont 
   need to pass around the size of the array */
static struct DATA row_data[] = {
        { "row0","item 12", 3, 4.3 },
        { "row1","item 23", 44,34.4},
        { "row2","item 33", 34,25.4},
        { "row3","item 43", 37,64.4},
        { "row4","item 53", 12,14.4},
        { "row5","item 68", 42,34.4},
        { "row6","item 75", 72,74.4},
        {NULL}
};

gboolean dndactive = FALSE;

/* Convenience function to deallocated memory used for DATA struct */
void free_DATA(struct DATA *data){
        if(data){
                free(data->row);
                free(data->item);
        }
        free(data);
}
 
/* Convenience function to print out the contents of a DATA struct onto stdout */
void print_DATA(struct DATA *data){
        printf("DATA @ %p\n",data);
        printf(" |->row = %s\n",data->row);
        printf(" |->item = %s\n",data->item);
        printf(" |->qty = %i\n",data->qty);
        printf(" +->price = %f\n",data->price);
}
 
void on_drag_data_deleted(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
{
	/* useless for DnD operations */
	printf("on_drag_data_deleted\n");
}

void on_drag_data_inserted(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
{
	printf("on_drag_data_inserted\n");
	/* activate DnD operation */
	dndactive = TRUE;
}

void on_drag_data_changed(GtkTreeModel *tree_model, GtkTreePath *path, GtkTreeIter *iter, gpointer user_data)
{
	/* Always initialise a GValue with 0 */
	GValue value={0,};
	char *cptr, *spath;
	gint prof;

	printf("on_drag_data_changed\n");
	if (!dndactive) {
		/* No DnD active here */
		printf("on_drag_data_changed : DND off\n");
		return;
	}

	/* Manage DnD operation */
	
	/* Get the GValue of a particular column from the row, the iterator currently points to*/
	gtk_tree_model_get_value(tree_model, iter, ROW_COL, &value);
	cptr = (char*) g_value_get_string(&value);
	spath = gtk_tree_path_to_string(path);
	prof = gtk_tree_path_get_depth(path);
	printf("on_drag_data_changed : cell=%s, path=%s, prof=%d\n", cptr, spath, prof);
	g_value_unset(&value);
}

/* Creates a scroll window, puts a treeview in it and populates it */
GtkWidget *add_treeview(GtkWidget *box, struct DATA array[])
{
	GtkWidget *swindow;
	GtkWidget *tree_view;
	GtkTreeStore *tree_store;
	GtkTreeModel *tree_model;
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	int i;

	char column_names[NUM_COLS][16] = {"Row #", "Description", "Qty", "Price"};

	swindow = gtk_scrolled_window_new(NULL, NULL);

	/* Both Vertical and Horizontal scroll set to Auto (NULL) */
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow),
								GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);

	/* Add this window to the box */
	gtk_box_pack_start(GTK_BOX(box),swindow,TRUE,TRUE,2);

	/* Create the treeview and its tree store */
	tree_store = gtk_tree_store_new(NUM_COLS,
							G_TYPE_STRING,G_TYPE_STRING,G_TYPE_INT,G_TYPE_FLOAT);

	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(tree_store));

	/* Add the treeview to the scrolled window */
	gtk_container_add(GTK_CONTAINER(swindow), tree_view);

	/* Add the columns */
	for(i = 0; i < 4; i++) {
		renderer = gtk_cell_renderer_text_new();
		column = gtk_tree_view_column_new_with_attributes (
										column_names[i],renderer,"text",i,NULL);
		gtk_tree_view_column_set_sort_column_id (column, i);
		gtk_tree_view_append_column (GTK_TREE_VIEW(tree_view), column);
	}

	/* Tell the theme engine we would like differentiated row colour */
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view),TRUE);

	/* Add the data */
	GtkTreeIter iter;
	i = 0;
	while(array[++i].row != NULL) {
		gtk_tree_store_append(tree_store, &iter, NULL);
		gtk_tree_store_set(tree_store, &iter,
							ROW_COL,array[i].row,
							ITEM_COL,array[i].item,
							QTY_COL,array[i].qty,
							PRICE_COL,array[i].price,-1);
	}

	/* Prepare treeview for DnD facility */

	/* Set treeview reorderable */
	gtk_tree_view_set_reorderable(GTK_TREE_VIEW(tree_view), TRUE);
	
	/* Get treeview model to connect events on it */
	tree_model = gtk_tree_view_get_model(GTK_TREE_VIEW(tree_view));

	// Attach a "drag-data-deleted" signal - useless here
	g_signal_connect(GTK_WIDGET(tree_model),"row-deleted", G_CALLBACK(on_drag_data_deleted), NULL);
	// Attach a "drag-data-inserted" signal to start DnD operation
	g_signal_connect(GTK_WIDGET(tree_model), "row-inserted", G_CALLBACK(on_drag_data_inserted), NULL);
	// Attach a "drag-data-changed" signal to manage data
	g_signal_connect(GTK_WIDGET(tree_model), "row-changed", G_CALLBACK(on_drag_data_changed), NULL);

	return tree_view;
}
 
int main(int argc, char **argv)
{
	GtkWidget *window;
	GtkTreeModel *model; 

	gtk_init(&argc,&argv);

	/* Create the top level window and setup the quit callback */
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_default_size(GTK_WINDOW(window),666,266);
	g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(gtk_exit),NULL);

	/* Build up the GUI with some boxes */
	GtkWidget *vbox = gtk_vbox_new(FALSE,10);
	gtk_container_add(GTK_CONTAINER(window),vbox);

	/* Add a title */
	GtkWidget *title = gtk_label_new(DESCRIPTION);
	gtk_box_pack_start(GTK_BOX(vbox),title,FALSE,FALSE,1);

	GtkWidget *hbox = gtk_hbox_new(TRUE,1);
	gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,1);

	/* Create treeview */
	GtkWidget *view1;
	view1 = add_treeview(hbox, row_data);

	/* Rock'n Roll */
	gtk_widget_show_all(window);
	gtk_main();
	return 0;
}

Compile:

gcc -g `pkg-config --cflags --libs gtk+-2.0`  -o DnD_Example example.c

Dragging Rows from One Tree to Another edit

This is actually much easier to do than setting up drag-and-drop functionality from arbitrary sources. Data is passed from one treeview to another by means of a GtkSelectionData object. Think of this like a post box. You put something in it when "drag-data-get" is fired and you take your stuff out when "drag-data-received" is emitted.

The example shown below was put together using many different sources but the fundamental design comes from a php-gtk2 cookbook article

#include <gtk/gtk.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define DESCRIPTION "Drag and Drop Between 2 Treeviews - by Vikram Ambrose"

/* Row data structure */
struct DATA { 
	char *row;
	char *item;
	int qty;
	float price;
};

/* A convenience enumerator to tag data types */
enum {
	TARGET_STRING,
	TARGET_INTEGER,
	TARGET_FLOAT
};
	
/* A convenience enumerator to count the columns */
enum {
	ROW_COL=0,
	ITEM_COL,
	QTY_COL,
	PRICE_COL,
	NUM_COLS
};

/* Some sample data for treeview 1. A NULL row is added so we dont 
   need to pass around the size of the array */
static struct DATA row_data[] = {
	{ "row0","item 12", 3, 4.3 },
	{ "row1","item 23", 44,34.4},
	{ "row2","item 33", 34,25.4},
	{ "row3","item 43", 37,64.4},
	{ "row4","item 53", 12,14.4},
	{ "row5","item 68", 42,34.4},
	{ "row6","item 75", 72,74.4},
	{NULL}
};

/* Sample data for treeview 2 */
static struct DATA row2_data[] = {
	{"row7", "item 127", 105, 115.5},
	{"row8","item 124", 117, 118.6},
	{"row9", "item 123", 120, 121.73},
	{NULL}
};
	
static const GtkTargetEntry drag_targets = { 
	"STRING", GTK_TARGET_SAME_APP,TARGET_STRING
};

static guint n_targets = 1;

/* Could be used instead, if GtkTargetEntry had more than one row */
//static guint n_targets = G_N_ELEMENTS (drag_targets);

/* Convenience function to deallocated memory used for DATA struct */
void free_DATA(struct DATA *data){
	if(data){
		free(data->row);
		free(data->item);
	}
	free(data);
}

/* Convenience function to print out the contents of a DATA struct onto stdout */
void print_DATA(struct DATA *data){
	printf("DATA @ %p\n",data);
	printf(" |->row = %s\n",data->row);
	printf(" |->item = %s\n",data->item);
	printf(" |->qty = %i\n",data->qty);
	printf(" +->price = %f\n",data->price);
}

/* User callback for "get"ing the data out of the row that was DnD'd */
void on_drag_data_get(	GtkWidget *widget, GdkDragContext *drag_context,
			GtkSelectionData *sdata, guint info, guint time,
			gpointer user_data){
	GtkTreeIter iter;
	GtkTreeModel *list_store;
	GtkTreeSelection *selector;
	gboolean rv;
	printf("on_drag_data_get: ");

	/* Get the selector widget from the treeview in question */
	selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(widget));

	/* Get the tree model (list_store) and initialise the iterator */
	rv = gtk_tree_selection_get_selected(selector,&list_store,&iter);

	/* This shouldn't really happen, but just in case */
	if(rv==FALSE){
		printf(" No row selected\n");
		return;
	}

	/* Always initialise a GValue with 0 */
	GValue value={0,};
	char *cptr;

	/* Allocate a new row to send off to the other side */
	struct DATA *temp = malloc(sizeof(struct DATA));

	/* Go through the columns */
	
	/* Get the GValue of a particular column from the row, the iterator currently points to*/
	gtk_tree_model_get_value(list_store,&iter,ROW_COL,&value);
	cptr = (char*) g_value_get_string(&value);
	temp->row = malloc(strlen(cptr)*sizeof(char)+1);
	strcpy(temp->row,cptr);
	g_value_unset(&value);
	
	gtk_tree_model_get_value(list_store,&iter,ITEM_COL,&value);
	cptr = (char*)g_value_get_string(&value);
	temp->item = malloc(strlen(cptr)*sizeof(char)+1);
	strcpy(temp->item,cptr);
	g_value_unset(&value);

	gtk_tree_model_get_value(list_store,&iter,QTY_COL,&value);
	temp->qty = g_value_get_int(&value);
	g_value_unset(&value);
	
	gtk_tree_model_get_value(list_store,&iter,PRICE_COL,&value);
	temp->price = g_value_get_float(&value);
	g_value_unset(&value);
	
	/* Send the data off into the GtkSelectionData object */
	gtk_selection_data_set(sdata,
		gdk_atom_intern ("struct DATA pointer", FALSE),
		8,		/* Tell GTK how to pack the data (bytes) */
		(void *)&temp,  /* The actual pointer that we just made */
		sizeof (temp)); /* The size of the pointer */
			
	/* Just print out what we sent for debugging purposes */
	print_DATA(temp);
}

/* User callback for putting the data into the other treeview */
void on_drag_data_received(GtkWidget *widget, GdkDragContext *drag_context,
			gint x, gint y, GtkSelectionData *sdata, guint info,
			guint time, gpointer user_data){

	GtkTreeModel *list_store;	
	GtkTreeIter iter;

	printf("on_drag_data_received:\n");

	/* Remove row from the source treeview */
	GtkTreeSelection *selector;
	selector = gtk_tree_view_get_selection(GTK_TREE_VIEW(user_data));
	gtk_tree_selection_get_selected(selector,&list_store,&iter);
	gtk_list_store_remove(GTK_LIST_STORE(list_store),&iter);

	/* Now add to the other treeview */
	GtkTreeModel *list_store2;
	GtkTreeIter iter2;
	list_store2 = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
	gtk_list_store_append(GTK_LIST_STORE(list_store2),&iter2);

	/* Copy the pointer we received into a new struct */
	struct DATA *temp = NULL;
	const guchar *my_data = gtk_selection_data_get_data (sdata);
	memcpy (&temp, my_data, sizeof (temp));

	/* Add the received data to the treeview model */
	gtk_list_store_set(GTK_LIST_STORE(list_store2),&iter2,
		ROW_COL,temp->row,
		ITEM_COL,temp->item,
		QTY_COL,temp->qty,
		PRICE_COL,temp->price,-1);

	/* We dont need this anymore */
	free_DATA(temp);
}

/* User callback just to see which row was selected, doesnt affect DnD. 
   However it might be important to note that this signal and drag-data-received may occur at the same time. If you drag a row out of one view, your selection changes too */
void on_selection_changed (GtkTreeSelection *treeselection,gpointer user_data){
	GtkTreeIter iter;
	GtkTreeModel *list_store;
	gboolean rv;
	printf("on_selection_changed: ");

	rv = gtk_tree_selection_get_selected(treeselection,
		&list_store,&iter);
	/* "changed" signal sometimes fires blanks, so make sure we actually 
	 have a selection/
http://library.gnome.org/devel/gtk/stable/GtkTreeSelection.html#GtkTreeSelection-changed */
	if (rv==FALSE){
		printf("No row selected\n");
		return;
	}
	
	GValue value={0,};
	char *cptr;
	int i;

	/* Walk throw the columns to see the row data */
	for(i=0;i<NUM_COLS;i++){
		gtk_tree_model_get_value(list_store,&iter,i,&value);
		cptr = (gchar *) g_strdup_value_contents (&value);
		g_value_unset(&value);
		if(cptr)printf("%s|",cptr);
		free(cptr);
	}
	printf("\n");

}

/* Creates a scroll windows,  puts a treeview in it and populates it */
GtkWidget *add_treeview(GtkWidget *box, struct DATA array[]){
	GtkWidget *swindow;

	swindow = gtk_scrolled_window_new(NULL,NULL);

	/* Both Vertical and Horizontal scroll set to Auto (NULL) */
	gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(swindow),
		 GTK_POLICY_AUTOMATIC,GTK_POLICY_AUTOMATIC);
	
	/* Add this window to the box */
	gtk_box_pack_start(GTK_BOX(box),swindow,TRUE,TRUE,2);

	/* Create the treeview and its list store */
	GtkListStore *list_store;
	list_store = gtk_list_store_new(NUM_COLS,
		G_TYPE_STRING,G_TYPE_STRING,G_TYPE_INT,G_TYPE_FLOAT);

	GtkWidget *tree_view;
	tree_view = gtk_tree_view_new_with_model(GTK_TREE_MODEL(list_store));

	/* Add the treeview to the scrolled window */
	gtk_container_add(GTK_CONTAINER(swindow),tree_view);
		
	/* Add the columns */
	GtkCellRenderer *renderer;
	GtkTreeViewColumn *column;
	
	char column_names[NUM_COLS][16] = {
		"Row #", "Description", "Qty", "Price"};
	int i;
	for(i=0;i<4;i++){
		renderer = gtk_cell_renderer_text_new();
		column = gtk_tree_view_column_new_with_attributes (
			column_names[i],renderer,"text",i,NULL);
		gtk_tree_view_column_set_sort_column_id (column, i);
		gtk_tree_view_append_column (GTK_TREE_VIEW(tree_view), column);
	}

	/* Tell the theme engine we would like differentiated row colour */
	gtk_tree_view_set_rules_hint(GTK_TREE_VIEW(tree_view),TRUE);

	/* Add the data */
	GtkTreeIter iter;
	i=0;
	while(array[++i].row!=NULL){
		gtk_list_store_append(list_store,&iter);
		gtk_list_store_set(list_store,&iter,
			ROW_COL,array[i].row,
			ITEM_COL,array[i].item,
			QTY_COL,array[i].qty,
			PRICE_COL,array[i].price,-1);
	}
	
	/* Attach the "changed" callback onto the tree's selector */
	g_signal_connect(
		gtk_tree_view_get_selection (GTK_TREE_VIEW(tree_view)),
		"changed",G_CALLBACK(on_selection_changed),NULL);

	return tree_view;
}

int main(int argc, char **argv){
	GtkWidget *window;

        gtk_init(&argc,&argv);

	/* Create the top level window and setup the quit callback */
	window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
	gtk_window_set_default_size(GTK_WINDOW(window),666,266);
	g_signal_connect(G_OBJECT(window),"destroy",G_CALLBACK(exit),NULL);

	/* Build up the GUI with some boxes */
	GtkWidget *vbox = gtk_vbox_new(FALSE,10);
	gtk_container_add(GTK_CONTAINER(window),vbox);

	/* Add a title */
	GtkWidget *title = gtk_label_new(DESCRIPTION);
	gtk_box_pack_start(GTK_BOX(vbox),title,FALSE,FALSE,1);

	GtkWidget *hbox = gtk_hbox_new(TRUE,1);
	gtk_box_pack_start(GTK_BOX(vbox),hbox,TRUE,TRUE,1);

	/* Create treeview 1 */
	GtkWidget *view1;
	view1 = add_treeview(hbox,row_data);

	/* Set treeview 1 as the source of the Drag-N-Drop operation */
	gtk_drag_source_set(view1,GDK_BUTTON1_MASK, &drag_targets,n_targets,
		GDK_ACTION_COPY|GDK_ACTION_MOVE);
	/* Attach a "drag-data-get" signal to send out the dragged data */
	g_signal_connect(view1,"drag-data-get",
		G_CALLBACK(on_drag_data_get),NULL);

	/* Create treeview 2 */
	GtkWidget *view2;
	view2 = add_treeview(hbox,row2_data);

	/* Set treeview 2 as the destination of the Drag-N-Drop operation */
	gtk_drag_dest_set(view2,GTK_DEST_DEFAULT_ALL,&drag_targets,n_targets,
		GDK_ACTION_COPY|GDK_ACTION_MOVE); 
	/* Attach a "drag-data-received" signal to pull in the dragged data */
	g_signal_connect(view2,"drag-data-received",
		G_CALLBACK(on_drag_data_received),view1);

	/* Rock'n Roll */
	gtk_widget_show_all(window);
        gtk_main();
	return 0;
}

Compile:

gcc -g example.c  `pkg-config --cflags --libs gtk+-2.0`  -o DnD_Example

GTK support numerous data types through GtkSelectionData. But in this example we only send across the value of a pointer. A pointer to a row data structure containing all the data required to populate the destination treeview.