Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

JVM Languages

Java Q&A


December 1996: Java Q&A

How Do I Implement a Drag-and-Drop Interface?

Cliff Berg

Cliff, vice president of technology of Digital Focus, can be contacted at [email protected]. To submit questions, check out the Java Developer FAQ Web site at http://www.digitalfocus.com/faq/.


Drag-and-drop interfaces let users move, transfer, and even convert objects by simply selecting them with the mouse and dragging their iconic representation from one part of the screen to another. Dragging an object from one window to another is typically interpreted as a request to move the object. Dragging an object's icon and "dropping" it onto another is interpreted as a request to join the two objects in some way, perhaps by supplying the dropped object as an input parameter to the target object, which may represent a program.

Unfortunately, at this writing, the Java AWT API does not provide any built-in facilities for building drag-and-drop interfaces. You have to construct these from the component and container primitive objects supplied with the AWT. Drag-and-drop features may be included in a future version of Java; if and when they are, I'll revisit the subject.

This month, I present a technique for building a drag-and-drop interface using the standard AWT components. To make things more interesting, I'll build a simple application that lets end users perform a host-to-host file transfer, by simply dragging a file icon from one part of the screen to another.

Containers and Components

The main difficulty in designing a drag-and-drop interface is achieving the required interaction between the components that are dragged, the desktop across which they are dragged, and the visual containers (usually windows) or components they are dropped into or onto. A precise specification of the methods required to implement this interaction would clarify a drag-and-drop design. For simplicity, I'll ignore the need to drop one component onto another, and focus on dropping components into containers (directory windows, for example).

In the design presented here, there are two Java interfaces that specify this interaction: Draggable and DesktopContainer. All components that will be dragged by users must implement the Draggable interface; all containers (Windows, Frames, Panels, Applets, and the like) that will receive components must implement the DesktopContainer interface.

Draggable specifies the methods in Example 1(a). The drawOutline() method is called whenever a container wishes the dragged component to draw an image of itself as it is dragged. The method takes a Graphics object as a parameter; this is the context in which the component is drawn, and it will generally be the graphics context of the calling object. A Draggable may choose to draw a complete image, or it may draw an outline or ghost image, for speed, since drawOutline() will probably be called in quick succession as the object is dragged. Also note that drawOutline() will probably also have to make use of the setXORMode() method, so that it can undo its own prior drawing operations each time the component is redrawn.

The select() method will be called whenever the enclosing container or desktop chooses to "select" (or unselect) the object; for example, when users click on it. The selection state is intended to be maintained by the Draggable, so that it can draw itself differently when it is selected. Finally, the getName() method is used to return the textual name of the object.

The DesktopContainer interface specifies the methods in Example 1(b). The give() method is called whenever an object is to be transferred out of the immediately enclosing container (which is a DesktopContainer). Conversely, the receive() method is called by a container when a Draggable is dragged onto it. In this paradigm, one container gives up the object by calling give(), and another container receives it by calling receive(). The receive() method returns a boolean indicating whether the component was accepted; otherwise, the giving container may want to take the component back, by calling receive().

All you have to do is implement these methods in a suitable container class and component class, and instantiate the container in an applet or application.

Since the application involves file transfer, we are going to drag file icons. I define a class FileIcon, which extends Canvas (so we can draw text on it) and implements Draggable.

FileIcon and DirPanel

FileIcon contains the methods required by Draggable, including drawOutline(), select(), and getName(), as well as the methods paint() and handleEvent(). The purpose of providing a handleEvent() method for this class is to intercept mouse-down events within the component. Whenever a mouse-down event occurs, handleEvent() calls select(true), causing the component to become selected. It then returns super.handleEvent() instead of True, indicating that the event is to be passed to the superclass event handler, and then most likely to the enclosing container, which can perform some additional action in response to the mouse click.

The FileIcon's paint() method simply draws a text string containing the name of the object. A more elaborate version of this application would probably draw pictorial representations of files, or file folders for directories. Also, the background color could be set to match the background color of the enclosing parent container, to create a borderless look for the FileIcon objects.

FileIcon has a copy constructor: a constructor that takes the same class type as its only parameter. This will be used by our application for cloning a FileIcon object, when required. An example of this might be when users drag a component from one container into another while holding down the Shift key; you use the Shift key to specify a copy operation, as opposed to a move operation. Many existing applications use the Shift (and sometimes the Ctrl) key in a similar way, to distinguish between two modes of a similar operation.

DirPanel is the class for implementing a container that can give and receive draggable components. DirPanel extends Panel, and implements the DesktopContainer required methods give() and receive(). It also overrides Panel's methods for preferredSize(), minimumSize(), reshape(), and resize(). This is so you have precise control over the panel's shape and size, without interference from a layout manager. (The preferredSize() method for a Panel normally collapses the panel to the minimum space needed to contain its components.) In fact, you set the layout manager for the panel to null so you can permanently position the components that are to be contained within the DirPanel, including those that are dragged into it. You could define a special layout manager to implement the policies required for positioning the components; but for this simplified example, direct positioning sufficed.

The constructor for DirPanel performs some tasks relevant to our application. To do this, it requires these parameters: width, height, host, user, pswd, and dir. width and height are the desired size of the panel. The host is the name of the Internet host whose files are to be depicted in this DirPanel. The user is the user ID of an FTP account on that host, and pswd is the associated password. dir is the subdirectory of the FTP account from which to display a list of files.

To perform the functions required to connect to one or more FTP servers, establish connections, transfer files between them, and get directory listings, you use a class called FTPClient. (This is not to be confused with the Sun class, written by Jonathan Payne, that bears the similar name FtpClient. The latter was not used because it implements a different subset of the FTP protocol than what was needed for this column.) FTPClient has these methods: openConnection(), sendCommand(), pwd(), cwd(), list(), delete(), send(), and receive(), as well as many others. It also contains a thread for performing the actual data transfer when a directory listing is obtained (this is the only kind of remote-to-local data transfer implemented by FTPClient). The source code for FTPClient.java and FTPClientImpl.java is available electronically; see "Availability," page 3. DirPanel's constructor has the job of creating a connection to the specified Internet host, changing to the specified directory, getting a directory listing, then creating and displaying FileIcon components to represent the files and directories found. By the time a DirPanel is constructed, all this has been done.

Anyone who has used an FTP tool is probably familiar with the two modes of usage: local-to-remote and remote-to-remote. Most FTP tools start out in a local-to-remote mode and file-transfer operations invoked by the user are performed between the user's local file system and the remote system. In remote-to-remote mode, a connection from the remote host to a third host is established and file-transfer operations are performed between those two remote hosts. In this mode, data is not actually transferred to the user's local machine. You can, for example, use an FTP program that supports this mode of operation to transfer a large number of files between two remote machines, connected by high-speed data lines, without any of the data ever having to come across the user's local connection.

Our application instantiates two DirPanel objects, and thus creates a control connection to each of two remote machines. The objective here is to be able to drag a file icon from one panel to another, and in so doing, cause the associated file to be transferred from one remote machine to the other directly, without passing through the user's local machine (applet!).

The Main Applet

The main applet (available electronically), which I have called "DragAndDrop," instantiates two DirPanels. Each panel automatically opens a connection to a host when it is created. The host is specified as an HTML parameter, as is the directory to display. In a real application, there would be a means for the user to specify the host and directory interactively. For example, the widely used WS_FTP program written by John Junod uses two side-by-side windows: The left window displays the local file system, and a pop-up dialog prompts users for a host to connect to.

You might wonder why I used an applet. In particular, this application requires a TCP/IP socket connection (actually, two are required) to each host that it sets up a control connection to. Since we are instantiating two DirPanels, connections to more than one host will be required, and they may not necessarily be the host that the applet was downloaded from. As of today, Netscape's Navigator and other browsers prohibit socket connections to hosts other than the host the applet originated from, so the applet will receive a security violation. However, the upcoming Java 1.1 will provide support for signed applets, which will allow this restriction to be relaxed, and it is anticipated that in the very near future, one will be able to provide applets on a web page that are permitted to connect to multiple hosts as long as they are digitally "signed" by a certifying authority. Further, an applet can always be instantiated into an application anyway, or run from appletviewer.

The actual mechanics of managing the display of a FileIcon's outline (as it is dragged within a DirPanel, across the DirPanel's boundary, onto the applet area, and back into another DirPanel) is handled within our applet's handleEvent() method. The applet has a method called drawOutline() that takes a Draggable and x- and y-coordinates as parameters. This method loops through all the components owned by the applet, gets their graphics contexts, and draws the Draggable in each of those graphics contexts (including the context of the applet itself). It does so by calling the Draggable's drawOutline() method. This method draws the component's outline, in XOR mode, so you can erase the outline drawn simply by redrawing it in XOR mode.

Thus, you are simply drawing the Draggable component onto every other component, and relying on the built-in clipping of the AWT to prevent drawing over the Draggable multiple times. (Drawing over a component more than once would be a problem, because we are drawing the outlines in XOR mode and it would erase parts of the component.)

When users press the mouse button on a Draggable component, the Draggable's handleEvent() method changes the component to the "selected" state, then forwards the event to the enclosing DirPanel (a DesktopContainer), which merely forwards the event to the applet's handleEvent() method. This method then calls drawOutline() for the Draggable. Successive mouse-drag events result in the applet's handleEvent() method being called again, but this time, the applet calls drawOutline() twice: once to erase the previous image and once to display it at the new location. Finally, a mouse-up event causes drawOutline() to be called only once, erasing the last image drawn.

The applet implements DesktopContainer so users can drag components on and off the applet "desktop." In these cases, the action of dropping a file icon onto the desktop is probably best interpreted as a request to transfer the file to the local system; conversely, dragging from the applet onto a DirPanel should probably transfer the file from the local system to the associated remote host. These functions are left for you to implement--the code presented with this article only provides for file transfer between two remote hosts. Most of the mechanics required to implement file transfer to a local machine are already incorporated: The FTPClient class' list() method has to set up a data transfer to the local system, and the technique used to do so can be modified to retrieve a remote file and write the data received to a local file. A similar technique can be used to transfer in the other direction.

FileIcon displays directory names in bold, ordinary files in regular text, and file links in italic. FTPClient's list() method retrieves a listing of the current host directory, consisting of a Java Vector of arrays of type Object[], where the first element of each array is the String name of the file, and the second element is an Integer object that encodes the type of file: DIRECTORY, FILE, LINK, or UNKNOWN. These attributes are maintained in each FileIcon object. Double-clicking on a directory name should cause the program to call the FTPClient's list() method again and refresh the display with the new directory; you are invited to implement that, as well as a "next-directory-up" button, scrolling, selecting transfer mode (ASCII, binary), and so on. Also, users may want to filter out files of type UNKNOWN, to get rid of ".", "..", and other pseudofile and nonfile symbols returned by the list() method. Currently, the list() method assumes that the host system has a UNIX file system.

Figure 1 shows the final applet. Again, the source code described here is available electronically from DDJ and at the Digital Focus web site (http://www.digitalfocus.com/ddj/code).

Conclusion

It is often said that the Java AWT does not provide the functionality users are accustomed to--multimedia, elaborate and highly functional controls, cut-and-paste, and the like. Java 1.1 will provide a great deal of additional functionality, and bring the AWT up to the level of (and, in many areas, beyond) today's full-function APIs. In the meantime, many user-friendly features can be implemented using the basic components of the AWT, and real applications are certainly possible. If you are interested in seeing the gamut of advanced Java applications, check out the comprehensive listing at the Java Applet Rating Service (JARS) website (http://www.jars.com).

Java provides a platform-independent software medium, with a portable network-programming model, combined with the automatic deployment of a web applet. This makes the Internet more accessible to the end user than ever before, and powerful new kinds of applications are limited only by your imagination.

Figure 1: The DragAndDrop applet.

Example 1: (a) Methods specified by the Draggable interface; (b) methods specified by the DesktopContainer interface.

(a)
public void drawOutline(Graphics g, int x, int y);
public void select(boolean selected);
public String getName();

(b)
public void give(Draggable comp);
public boolean receive(Draggable comp, int x, int y);


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.