Drag and Drop

While drag and drop belongs into GTK+ itself, I thought it would be better to cover it after some parts of GNOME were discussed.

Accepting Drops

You have already seen one drop handler back when we were discussing the mime types. Basically, to accept drops, you have to decide which mime type of data you want to be able to receive. You have already seen one for "text/uri-list". Basically your handler will only receive data of those mime types that you specify, so you only need to know how to decode those.

To specify the mime types you want to receive, you create an array of GtkTargetEntry structures, where the first element is a string of mime type, the second is an integer flag and the third is an integer info. You can leave the flags at 0. The info field can be used if you have several entries you are accepting, as the info integer will be passed to your drop handler, so you can create a switch statement to handle the different types of data. If you have only one type, just leave this at 0.

After this you need to set up the widget for dragging. You do this by calling the gtk_drag_dest_set function. The first argument is the widget you want to set up, the second is a flags argument for setting up which types of default drag behavior to use, you can leave this at GTK_DEST_DEFAULT_ALL. The next argument is the array of GtkTargetEntry structures, the next argument is the number of items in that array. The last argument is the type of action that you accept. The types can be any of the following ORed together: GDK_ACTION_DEFAULT, GDK_ACTION_COPY, GDK_ACTION_MOVE, GDK_ACTION_LINK, GDK_ACTION_PRIVATE and GDK_ACTION_ASK. The most useful are GDK_ACTION_COPY and GDK_ACTION_MOVE. If you are for example passing around strings or other data, you will most likely use GDK_ACTION_COPY only.

Then you need to set up and bind the drop handler. The drop handler should have the following prototype:

void  
target_drag_data_received  (GtkWidget          *widget,
			    GdkDragContext     *context,
			    gint                x,
			    gint                y,
			    GtkSelectionData   *data,
			    guint               info,
			    guint               time);

The data you have is in the structure GtkSelectionData, in the data field. That's all you need to do for normal DND. Here's and example:

static void  
target_drag_data_received  (GtkWidget          *widget,
			    GdkDragContext     *context,
			    gint                x,
			    gint                y,
			    GtkSelectionData   *data,
			    guint               info,
			    guint               time)
{
	g_print("Got: %s\\n",data->data);
}
...
static GtkTargetEntry target_table[] = {
	{ "text/plain", 0, 0 }
}
...
gtk_drag_dest_set (widget,
		   GTK_DEST_DEFAULT_ALL,
		   target_table, 1,
		   GDK_ACTION_COPY);
gtk_signal_connect (GTK_OBJECT (widget), "drag_data_received",
		    GTK_SIGNAL_FUNC (target_drag_data_received),
		    NULL);

For more information about drag and drop, you should see GTK+ documentation at www.gtk.org.

Allowing Drags

Now let's look at the source side of DND. You set up the GtkTargetEntry array, in the same manner as above. Then instead of the flags argument you substitute a mask for the start mouse button of the drag. This could be GDK_BUTTON1_MASK | GDK_BUTTON3_MASK for 1st and 3rd mouse buttons. Then you need to bind the drag_data_get signal that will send the data for the drag on it's way, and drag_data_delete if the action is GDK_ACTION_MOVE, to delete the data since the move was successful. Here's a simple example that will work with the above code snippet for drop:

static void  
source_drag_data_get  (GtkWidget          *widget,
		       GdkDragContext     *context,
		       GtkSelectionData   *selection_data,
		       guint               info,
		       guint               time,
		       gpointer            data)
{
	char string[] = "Some String!"
	gtk_selection_data_set (selection_data,
				selection_data->target,
				8, string, sizeof(string));
}
...
static GtkTargetEntry target_table[] = {
	{ "text/plain", 0, 0 }
}
...
gtk_drag_source_set (widget,
		     GDK_BUTTON1_MASK|GDK_BUTTON3_MASK,
		     target_table, 1,
		     GDK_ACTION_COPY);
gtk_signal_connect (GTK_OBJECT (widget), "drag_data_get",
		    GTK_SIGNAL_FUNC (source_drag_data_get),
		    NULL);

The gtk_selection_data_set function copies the data into the selection data, which is used for the transfer.