14.4 Dropping Multiple Datatypes
A limitation of the Swing data transfer
mechanism is that, by default, it supports only a single type of
data. It is useful, sometimes, to allow a component to accept pastes
and drops of more than one type, handling each type in a different
way. A JEditorPane component might handle dropped
text in one way, handle dropped colors another way (by changing the
foreground color of the selected text, for example), and handle
dropped files yet another way (for example, by inserting the contents
of the file). In the previous examples, we've
created our own TransferHandler instances for
simple data transfer customization. For more complex
customizations, such as supporting multiple drop types, we need to
subclass TransferHandler.
Example 14-3 lists the
FileTransferHandler class. It is a
TransferHandler subclass that wraps and delegates
to some other specified TransferHandler, adding
the ability to accept pastes and drops of files using the predefined
DataFlavor.javaFileListFlavor
flavor. The example includes an inner class named
Test that demonstrates its use with a
JTextArea and a JFileChooser.
Depending on how well integrated your Java implementation is with
your operating system's desktop, you may also be
able to drag files from your desktop into the
JTextArea.
Example 14-3. FileTransferHandler.java
package je3.datatransfer;
import javax.swing.*;
import java.awt.datatransfer.*;
import java.io.*;
import java.awt.event.InputEvent;
import java.util.List;
/**
* This TransferHandler subclass wraps another TransferHandler and delegates
* most of its operations to the wrapped handler. It adds the ability to
* to drop or paste files using the predefined DataFlavor.javaFileListFlavor.
* When a file list is pasted or dropped, it assumes the files are text, reads
* them in order, concatenates their contents, and then passes the resulting
* string to the wrapped handler for insertion.
*/
public class FileTransferHandler extends TransferHandler {
TransferHandler wrappedHandler; // The handler that we wrap
// We use this array to test the wrapped handler
static DataFlavor[ ] stringFlavorArray =
new DataFlavor[ ] { DataFlavor.stringFlavor };
/** Pass an existing TransferHandler to this constructor */
public FileTransferHandler(TransferHandler wrappedHandler) {
if (wrappedHandler == null) // Fail immediately on null
throw new NullPointerException( );
this.wrappedHandler = wrappedHandler; // Remember wrapped handler
}
/**
* This method returns true if the TransferHandler knows how to work
* with one of the specified flavors. This implementation first checks
* the superclass, then checks for fileListFlavor support
*/
public boolean canImport(JComponent c, DataFlavor[ ] flavors) {
// If the wrapped handler can import it, we're done
if (wrappedHandler.canImport(c, flavors)) return true;
// Otherwise, if the wrapped handler can handle string imports, then
// see if we are being offered a list of files that we can convert
// to a string.
if (wrappedHandler.canImport(c, stringFlavorArray)) {
for(int i = 0; i < flavors.length; i++)
if (flavors[i].equals(DataFlavor.javaFileListFlavor))
return true;
}
// Otherwise, we can't import any of the flavors.
return false;
}
/**
* If the wrapped handler can import strings and the specified Transferable
* can provide its data as a List of File objects, then we read the
* files, and pass their contents as a string to the wrapped handler.
* Otherwise, we offer the Transferable to the wrapped handler to handle
* on its own.
*/
public boolean importData(JComponent c, Transferable t) {
// See if we're offered a java.util.List of java.io.File objects.
// We handle this case first because the Transferable is likely to
// also offer the filenames as strings, and we want to import the
// file contents, not their names!
if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor) &&
wrappedHandler.canImport(c, stringFlavorArray)) {
try {
List filelist =
(List)t.getTransferData(DataFlavor.javaFileListFlavor);
// Loop through the files to determine total size
int numfiles = filelist.size( );
int numbytes = 0;
for(int i = 0; i < numfiles; i++) {
File f = (File)filelist.get(i);
numbytes += (int)f.length( );
}
// There will never be more characters than bytes in the files
char[ ] text = new char[numbytes]; // to hold file contents
int p = 0; // current position in the text[ ] array
// Loop through the files again, reading their content as text
for(int i = 0; i < numfiles; i++) {
File f = (File)filelist.get(i);
Reader r = new BufferedReader(new FileReader(f));
p += r.read(text, p, (int)f.length( ));
}
// Convert the character array to a string and wrap it
// in a pre-defined Transferable class for transferring strings
StringSelection selection =
new StringSelection(new String(text, 0, p));
// Ask the wrapped handler to import the string
return wrappedHandler.importData(c, selection);
}
// If anything goes wrong, just beep to tell the user
catch(UnsupportedFlavorException e) {
c.getToolkit( ).beep( ); // audible error
return false; // return failure code
}
catch(IOException e) {
c.getToolkit( ).beep( ); // audible error
return false; // return failure code
}
}
// Otherwise let the wrapped class handle this Transferable itself
return wrappedHandler.importData(c, t);
}
/*
* The following methods just delegate to the wrapped TransferHandler
*/
public void exportAsDrag(JComponent c, InputEvent e, int action) {
wrappedHandler.exportAsDrag(c, e, action);
}
public void exportToClipboard(JComponent c, Clipboard clip, int action) {
wrappedHandler.exportToClipboard(c, clip, action);
}
public int getSourceActions(JComponent c) {
return wrappedHandler.getSourceActions(c);
}
public Icon getVisualRepresentation(Transferable t) {
// This method is not currently (Java 1.4) used by Swing
return wrappedHandler.getVisualRepresentation(t);
}
/**
* This class demonstrates the FileTransferHandler by installing it on a
* JTextArea component and providing a JFileChooser to drag and cut files.
*/
public static class Test {
public static void main(String[ ] args) {
// Here's the text area. Note how we wrap our TransferHandler
// around the default handler returned by getTransferHandler( )
JTextArea textarea = new JTextArea( );
TransferHandler defaultHandler = textarea.getTransferHandler( );
textarea.setTransferHandler(new FileTransferHandler(defaultHandler));
// Here's a JFileChooser, with dragging explicitly enabled.
JFileChooser filechooser = new JFileChooser( );
filechooser.setDragEnabled(true);
// Display them both in a window
JFrame f = new JFrame("File Transfer Handler Test");
f.getContentPane( ).add(new JScrollPane(textarea), "Center");
f.getContentPane( ).add(filechooser, "South");
f.setSize(400, 600);
f.setVisible(true);
}
}
}
|