跳转到内容

GTK+ 示例/树视图/DnD

来自维基教科书,开放世界中的开放书籍

拖放 (DnD) **** 需要修改 ***

[编辑 | 编辑源代码]

本节比其他任何部分都需要修改。如果您了解有关树视图拖放的任何信息,您可能比本文作者更了解。在这种情况下,请提供一些反馈。

如果您想深入了解树视图拖放,您可能想查看 Owen Taylor 关于此主题的邮件。它可能与实际实现的内容不完全相同,但它提供了很好的概述,并提供了比文档更多信息。

除了与任何窗口小部件一起使用的标准 Gtk+ 拖放机制外,还有专门用于树视图窗口小部件的特殊拖放机制。您通常希望使用特定于树视图的拖放框架。

从其他窗口或部件拖放与行无关的数据到树视图或从树视图中

[编辑 | 编辑源代码]

从树视图窗口小部件中拖放一般信息或拖放到树视图窗口小部件中,与其他任何窗口小部件的工作方式相同,并且涉及标准的 Gtk+ 拖放机制。如果您使用此功能,您可以接收从树视图中的任何位置(包括空部分)拖放到该位置,或从该位置启动拖放操作。这不是特定于行或列的,很可能不是您想要的。然而,这是一个树视图的小示例,您可以在其中从其他应用程序(例如浏览器)拖放 URI,并将拖放的 URI 附加到列表中(请注意,通常您可能更愿意将整个窗口设置为目标,而不仅仅是树视图窗口小部件)。

#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;
}

如果您接收拖放到树视图中,您可以连接到视图的“drag-motion”信号以跟踪鼠标指针,同时它在树视图上的拖放操作中。这对于例如当鼠标在拖放操作期间在节点上悬停一段时间时,您希望展开树中折叠的节点时非常有用。这是一个实现此目的的示例。

/***************************************************************************
 *
 *   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;
}

连接到视图的“drag-drop”信号,以便在发生拖放时调用它。您可以使用 gtk_tree_view_get_path_at_pos 将提供的坐标转换为树路径。

在树中拖动行

[编辑 | 编辑源代码]

GtkListStore 和 GtkTreeStore 都实现了 GtkTreeDragDest 和 GtkTreeDragSource 接口,这意味着它们内置支持行重新排序。您需要调用 gtk_tree_view_set_reorderable 来激活此功能,然后连接到树模型的信号以捕获发生的重新排序操作。

以下示例所示

#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;
}

编译

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

从一棵树拖动行到另一棵树

[编辑 | 编辑源代码]

这实际上比设置来自任意来源的拖放功能要容易得多。数据通过 GtkSelectionData 对象从一个树视图传递到另一个树视图。把它想象成一个邮箱。当发出“drag-data-get”时,您将一些东西放入其中,当发出“drag-data-received”时,您取出您的东西。

以下示例是使用许多不同的来源放在一起的,但基本设计来自 php-gtk2 食谱文章

#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;
}

编译

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

GTK 通过 GtkSelectionData 支持大量数据类型。但在本例中,我们只发送一个指针的值。一个指向行数据结构的指针,该结构包含填充目标树视图所需的所有数据。

华夏公益教科书