Drag and drop with Perl/Tk

By: Slaven Rezic

A useful interface for communication between GUI programs is the drag and drop facility. This interface can also be used for actions inside an application, e.g. for moving items from one place to another. Perl/Tk supports drag and drop, and since the documentation in the distribution is a little sparse on this topic, I will provide some information in this article.

A user who wants to implement drag and drop in his Perl/Tk application has to deal with the following concepts:

In Perl/Tk, two types of drag and drops are implemented: local drops and drops from other applications. Local drops work on all supported platforms. Drops between applications are not standardized, so there is a number of drag and drop protocols. Perl/Tk supports these protocols: Sun, XDND, KDE and Win32. The Sun protocol is supported since the old 400.xxx days. Win32 support is added in the version 800.005, while the KDE and XDND support is fairly new: they appeared in the latest 800.015 release. KDE drag and drop is used by the KDE file manager kfm. XDND is a protocol developed by John Lindal and is used in GNOME. It is expected that KDE will also switch to XDND in the future.

The drag and drop interface is fairly low-level, as it works using X11 selections. There are some limitations (the interface only works for single files and does not support anything else than filenames, that is, drops using URLs in KDE will not work). There is also rumor that there will be changes in the Tcl/Tk core to support drag and drop, so it is likely that this changes will appear some day in Perl/Tk, too. Nevertheless, the current interface in Perl/Tk is working and useful.

Local drops

Here is a sample script for local drag and drop. In this example two listboxes are created. Items from the left listbox can be dragged to the right one.

For local drops we need either the source and the target side, so both drag and drop modules should be included in the script: Tk::DragDrop (for the source) and Tk::DropSite (for the target). The source side is defined by calling the DragDrop method on the source widget. The following options should be set:

The DragDrop method returns a "token", which is a Label widget for the text or image displayed during the drag action.

The target widget is called with the DropSite method. The -droptypes option is used to register the drop type this widget is accepting (here we use again Local) and the -dropcommand option is set to a callback which is executed when a drop occured.

In the example script, the StartDrag is called when a drag is started. This subroutine is responsible for setting the content of the drag token with the configure method. In the sample script, the current item of the listbox is determined with the nearest method. After that the token is placed at the current cursor position and activated with raise and deiconify. Further processing is done in the FindSite method, which is responsible for highlighting the token over a dropsite and to handle a drop action.

The Drop function of the example script accepts a drop and performs an action. Here it uses the text value of the token to add another item to the right listbox.

Remote drops

A sample script for accepting drops from an external application, e.g. the KDE file manager or the Windows explorer.

For accepting drops from an external application, for example a file manager, only a drop site has to be defined. So it's sufficient to include only Tk::DropSite. The command to accept drops is the same as in the example above for local drops. The list of drop types should list all dnd protocols which should be accepted, that is Win32 if running on Windows, otherwise KDE, XDND and Sun.

The accepting callback receives an additional argument which is the name of a selection. This selection is used to get the filename of the drop. For Win32 dnd, we have to look at the "STRING" property, while for all other protocols, the property name is "FILE_NAME". In case of an error (because the property is not defined), the selection get is done in an eval block.

Additional methods

For the source side, the following callbacks can be set:

Short summary

Here is a short summary for using the drag and drop methods:
# load code for using the source side
use Tk::DragDrop;

# load code for using the target side
use Tk::DropSite;

# define the source side
$target->DropSite(-dropcommand => \&drop_handler,
                  -droptypes   => ['KDE','XDND','Sun','Win32','Local'],
		 );

# define the target side
$source->DragDrop(-event => '<B1-Motion>',
                  -sitetypes   => ['KDE','XDND','Sun','Win32','Local'],
		  -startcommand => \&drag_handler,
		  -entercommand => \&enter_handler,
		  -motioncommand => \&motion_handler,
                 );

# used in the drag_handler
$token->FindSite($xpos, $ypos, $event);

More information can be found in the DragDrop directory in the source distribution of Perl/Tk. There are some sample scripts: local_test, motion_test and site_test. And if there are still open questions: use the force, read the source!