Drag and Drop
in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
Object
that implements the
Transferable
interface
DataFlavor
that
describes the Transferable
Component
that has an associated
DragSource
DragGestureListener
that is
informed when a drag gesture is recognized by a
DragSource
Component
that has an associated
DropTarget
TransferHandler
that handles
the transfer of the Transferable
to/from the
Component
objects.getTransferHandler()
method is invoked
TransferHandler
object's
createTransferable()
method is invoked
which results in a call to the source object's
get____()
method (where ____
denotes the name of the attribute)
TransferHandler
object's
getSourceActions()
and
canImport()
methods are invokedComponent
object's
set____()
method is invoked and the
source object's exportDone()
method
is invokedsetDragEnabled(true)
the following components recognize the drag gesture:
JColorChooser
, JEditorPane
,
JFileChooser
, JFormattedTextField
,
JList
, JTable
,
JTextArea
, JTextField
,
JTextPane
, and JTree
JEditorPane
,
JFormattedTextField
,
JPasswordField
,
JTextArea
, JTextField
,
and JTextPane
JList
, JTable
,
JTree
Transferable
:
java.awt.datatransfer.StringSelection
DataFlavor
:
DataFlavor.stringFlavor
JList
(with a
java.awt.dnd.DragSource
)
and a JTextArea
(with a
javax.swing.TransferHandler.SwingDropTarget
)TransferHandler
:
javax.swing.plaf.basic.BasicTextUI.TextTransferHandler
import java.awt.*; import javax.swing.*; /** * A demonstration of drag and drop with text components that * support both "out of the box". * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class OutOfTheBoxDemo implements Runnable { /** * Create and show the GUI. * * @param args The command line arguments (which are ignored) * @throws Exception if something goes wrong */ public static void main(String[] args) throws Exception { SwingUtilities.invokeAndWait(new OutOfTheBoxDemo()); } /** * The code to execute in the event dispatch thread. */ public void run() { JFrame f = new JFrame("DnD Out of the Box"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(640, 480); JPanel cp = (JPanel)f.getContentPane(); cp.setLayout(new BorderLayout()); cp.add(new JLabel("Drag from the JList; Drop into the JTextArea", JLabel.CENTER), BorderLayout.NORTH); // The source String[] data = {"CS149", "CS159", "CS240", "CS261", "CS345", "CS361"}; JList<String> list = new JList<String>(data); list.setBorder(BorderFactory.createTitledBorder("Offered")); list.setFixedCellWidth(100); list.setDragEnabled(true); // Drag support must be enabled manually cp.add(list, BorderLayout.WEST); // The target JTextArea textArea = new JTextArea(); // Drop support is enabled by default textArea.setBorder(BorderFactory.createTitledBorder("Completed")); cp.add(textArea, BorderLayout.CENTER); f.setVisible(true); } }
TransferHandler
can be used to transfer String
objects
to/from components that have the "text" property
(i.e., getText()
and setText()
methods) using DataFlavor.stringFlavor
JLabel
has getText()
and
setText()
methodssetTransferHandler()
TransferHandler
label.addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } } );
TransferHandler
Component
that can display words
at random locations (like a collage)String
and a locationJPanel
TransferHandler
TransferHandler
(cont.)import java.awt.*; /** * An encapsulation of a String with a location. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class StringContent { private Point location; private String text; /** * Explicit Value Constructor. * * @param s The String * @param x The x-coordinate * @param y The y-coordinate */ public StringContent(String s, int x, int y) { this.text = s; this.location = new Point(x, y); } /** * Get the location. * * @return The location. */ public Point getLocation() { return location; } /** * Get the text. * * @return The text */ public String getText() { return text; } }
TransferHandler
(cont.)import java.awt.Graphics; import java.util.*; import javax.swing.*; /** * An example of a Component that supports text drag-and-drop. * * @author Prof. David Bernstein, James Madison University * @version Drop Only; Standard Handler */ public class StringCollage extends JPanel { private static final long serialVersionUID = 1L; private List<StringContent> elements; /** * Default Constructor. */ public StringCollage() { super(); elements = new ArrayList<StringContent>(); // Use a bean-oriented handler for objects with a text property // (i.e., with getText() and setText() methods) setTransferHandler(new TransferHandler("text")); } // Methods required to be a "text" bean /** * Get the text on this Component. * * This method is required so that this is a bean with the text property. * * @return The text of the last element */ public String getText() { return elements.get(elements.size()-1).getText(); } /** * Add a new piece of text to this Component at a random location. * * @param The text (tab delimited) */ public void setText(String s) { setText(s, (int)(Math.random()*getWidth()), (int)(Math.random()*getHeight())); repaint(); } // Methods that provide the desired GUI functionality /** * Render this Component. * * @param g The rendering engine to use. */ public void paint(Graphics g) { super.paint(g); for (StringContent e: elements) { g.drawString(e.getText(), e.getLocation().x, e.getLocation().y); } } /** * Set text on this element at the given location. * * @param text The text to add * @param x The x-coordinate * @param y The y-coordinate */ public void setText(String text, int x, int y) { elements.add(new StringContent(text, x, y)); } }
TransferHandler
(cont.)import java.awt.*; import javax.swing.*; /** * A demonstration of a custom Component that supports * text drop. * * @author Prof. David Bernstein, James Madison University */ public class StringCollageDemo implements Runnable { /** * Create and show the GUI. * * @param args The command line arguments (which are ignored) * @throws Exception if something goes wrong */ public static void main(String[] args) throws Exception { SwingUtilities.invokeAndWait(new StringCollageDemo()); } /** * The code to execute in the event dispatch thread. */ public void run() { JFrame f = new JFrame("TextCollageDemo"); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(640, 480); JPanel cp = (JPanel)f.getContentPane(); cp.setLayout(new BorderLayout()); JTextField field = new JTextField(); field.setBorder(BorderFactory.createTitledBorder("Type text here and then drag it onto the collage")); field.setDragEnabled(true); cp.add(field, BorderLayout.NORTH); StringCollage collage = new StringCollage(); cp.add(collage, BorderLayout.CENTER); f.setVisible(true); } }
TransferHandler
DropTargetListener
and a DropTarget
to register
it withTransferHandler
for other purposes later,
let's use the second approach nowStringCollage
implement
DropTargetListener
import java.awt.Graphics; import java.awt.Point; import java.awt.dnd.*; import java.util.*; import javax.swing.*; /** * An example of a Component that supports text drop at a particular * location. * * @author Prof. David Bernstein, James Madison University * @version Drop Only; Specific Location */ public class StringCollage extends JPanel implements DropTargetListener { private static final long serialVersionUID = 1L; private List<StringContent> elements; private Point dropLocation; /** * Default Constructor. */ public StringCollage() { super(); elements = new ArrayList<StringContent>(); dropLocation = null; // Create a DropTarget for this Component (and make the Component // the DropTargetListener) new DropTarget(this, this); // Use a bean-oriented handler for objects with a text property // (i.e., with getText() and setText() methods) setTransferHandler(new TransferHandler("text")); } // Methods required to be a "text" bean /** * Get the text on this Component. * * This method is required so that this is a bean with the text property. * * @return The text of the last element */ public String getText() { return elements.get(elements.size()-1).getText(); } /** * Add a new piece of text to this Component. * * If the text was dragged onto this Component it will be added at the drop * location. Otherwise, it won't be added. To add a new piece of text manually, * use the three-parameter version of this method. * * This method is required so that this is a bean with the text property. * * @param The text (tab delimited) */ public void setText(String s) { if (dropLocation != null) setText(s, dropLocation.x, dropLocation.y); dropLocation = null; repaint(); } // Methods that provide the desired GUI functionality /** * Render this Component. * * @param g The rendering engine to use. */ public void paint(Graphics g) { super.paint(g); for (StringContent e: elements) { g.drawString(e.getText(), e.getLocation().x, e.getLocation().y); } } /** * Set text on this element at the given location. * * @param text The text to add * @param x The x-coordinate * @param y The y-coordinate */ public void setText(String text, int x, int y) { elements.add(new StringContent(text, x, y)); } // Methods required by DropTargetListener /** * Handle dragEnter messages. * * @param dtde The event that generated the message */ public void dragEnter(DropTargetDragEvent dtde) { dropLocation = dtde.getLocation(); } /** * Handle dragOver messages. * * @param dtde The event that generated the message */ public void dragOver(DropTargetDragEvent dtde) { dropLocation = dtde.getLocation(); } /** * Handle dropActionChanged messages. * * @param dtde The event that generated the message */ public void dropActionChanged(DropTargetDragEvent dtde) { } /** * Handle dragEnter messages. * * @param dtde The event that generated the message */ public void dragExit(DropTargetEvent dte) { dropLocation = null; } /** * Handle drop messages. * * @param dtde The event that generated the message */ public void drop(DropTargetDropEvent dtde) { // Invoke the TransferHandler object's importData() method. // The TransferHandler will, in turn, invoke this Component's // setText() method. TransferHandler.TransferSupport support; support = new TransferHandler.TransferSupport(this, dtde.getTransferable()); getTransferHandler().importData(support); } }
TransferHandler
Component
that
displays words at different locations (like a collage)TransferHandler
that a drag gesture has startedDragGestureRecognizer
and a DragSource
to register
it withMouseListener
DragSource
later, we'll
use the second approach nowMouseListener
will only be used
locally and once, we'll use an anonymous classTransferHandler
(cont.)/** * Default Constructor. */ public StringCollage() { super(); elements = new ArrayList<StringContent>(); // Disable drag by default setDragEnabled(false); // Use a bean-oriented handler for objects with a text property // (i.e., with getText() and setText() methods) setTransferHandler(new TransferHandler("text")); // Add a MouseListener that responds to mousePressed messages // by exporting them to a TransferHandler addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } } ); }
TransferHandler
Component
that
displays words at different locations (like a collage)TransferHandler
DragGestureListener
and
DragSourceListener
and a DragSource
to register
them withTransferHandler
later
so will use the second approach nowStringCollage
implement
DragGestureListener
and
DragSourceListener
TransferHandler
(cont.)import java.awt.Cursor; import java.awt.Graphics; import java.awt.datatransfer.*; import java.awt.dnd.*; import java.util.*; import javax.swing.*; /** * An example of a Component that supports text drag and drop. * * @author Prof. David Bernstein, James Madison University * @version Drag Only (Copy or Move); Standard TransferHandler */ public class StringCollage extends JPanel implements DragGestureListener, DragSourceListener { private static final long serialVersionUID = 1L; private boolean dragEnabled; private int action; private List<StringContent> elements; private DragSource source; /** * Default Constructor. */ public StringCollage() { super(); elements = new ArrayList<StringContent>(); this.action = DnDConstants.ACTION_COPY; // Use a bean-oriented handler for objects with a text property // (i.e., with getText() and setText() methods) setTransferHandler(new TransferHandler("text")); // Construct a DragSource that uses this StringCollage as both its Component // and its DragGestureListener source = new DragSource(); source.createDefaultDragGestureRecognizer(this, this.action, this); // Disable drag by default setDragEnabled(false); } // Methods required to be a "text" bean /** * Get the text on this Component. * * This method is required so that this is a bean with the text property. * * @return The text (tab delimited) */ public String getText() { return elements.get(elements.size()-1).getText(); } /** * Add a new piece of text to this Component at location 0, 0. * * This method is required so that this is a bean with the text property. * * @param The text (tab delimited) */ public void setText(String s) { setText(s, 0, 0); } // Methods that provide the desired GUI functionality /** * Render this Component. * * @param g The rendering engine to use. */ public void paint(Graphics g) { super.paint(g); for (StringContent e: elements) { g.drawString(e.getText(), e.getLocation().x, e.getLocation().y); } } /** * Set text on this element at the given location. * * @param text The text to add * @param x The x-coordinate * @param y The y-coordinate */ public void setText(String text, int x, int y) { elements.add(new StringContent(text, x, y)); } // Useful additional functionality /** * Get the DnD action associated with this Component; * * @return The action */ public int getAction() { return action; } /** * Is drag enabled on this Component? * * @return true or false */ public boolean getDragEnabled() { return dragEnabled; } /** * Set the DnD action associated with this Component. * * @param The action (DnDConstants.ACTION_COPY or DnDConstants.ACTION_MOVE) */ public void setAction(int action) { if (action == DnDConstants.ACTION_MOVE) this.action = DnDConstants.ACTION_MOVE; else this.action = DnDConstants.ACTION_COPY; } /** * Determine whether or not drag is enabled on this Component. * * @param enabled true to enable; false to disable */ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } // Required by DragSourceListener /** * Handle dragEnter messages. * * @param dsde The event that generated the message */ public void dragEnter(DragSourceDragEvent dsde) { } /** * Handle dragOver messages. * * @param dsde The event that generated the message */ public void dragOver(DragSourceDragEvent dsde) { } /** * Handle dropActionChanged messages. * * @param dsde The event that generated the message */ public void dropActionChanged(DragSourceDragEvent dsde) { } /** * Handle dragExit messages. * * @param dsde The event that generated the message */ public void dragExit(DragSourceEvent dse) { } /** * Handle dragDropEnd messages. * * @param dsde The event that generated the message */ public void dragDropEnd(DragSourceDropEvent dsde) { if (action == DnDConstants.ACTION_MOVE) { elements.remove(elements.size()-1); repaint(); } } // Methods required by DragGestureListener /** * Handle dragGestureRecognized messages. * * @param dge The event that generated the message */ public void dragGestureRecognized(DragGestureEvent dge) { Cursor cursor = DragSource.DefaultMoveDrop; // StringSelection is a Transferable wrapper for a String source.startDrag(dge, cursor, new StringSelection(getText()), this); } }
TransferHandler
Component
that
displays words at different locations (like a collage)TransferHandler
that controls the processTransferHandler
(cont.)StringTransferHandler
import java.io.IOException; import java.awt.datatransfer.*; import javax.swing.*; /** * A TransferHandler that supports DnD (both Copy and Move) of String objects. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class StringTransferHandler extends TransferHandler { private static final long serialVersionUID = 1L; private boolean shouldCutWhenDone; private StringCollage source; /** * Import the Transferable in the TransferSupport into the * JComponent in the TransferSupport. * * @param support The TransferSupport * @return true if the data was imported; false otherwise */ public boolean importData(TransferHandler.TransferSupport support) { JComponent c = (JComponent)support.getComponent(); Transferable t = support.getTransferable(); shouldCutWhenDone = false; if ((source != c) && canImport(support)) { StringCollage component = (StringCollage)c; try { String text = (String)t.getTransferData(DataFlavor.stringFlavor); component.setText(text); shouldCutWhenDone = true; return true; } catch (IOException | UnsupportedFlavorException e) { return false; } } return false; } /** * Create a Transferable using the contents of the given JComponent. * */ protected Transferable createTransferable(JComponent c) { StringCollage component = (StringCollage)c; source = component; shouldCutWhenDone = true; // By default return new StringSelection(component.getText()); } /** * Get the source actions supported by this TransferHandler * for the given Component. * * @param c The Component of interest * @return The source actions supported (COPY_OR_MOVE) */ public int getSourceActions(JComponent c) { StringCollage component = (StringCollage)c; source = component; return component.getAction(); } /** * Complete the export process. * * @param c The Component * @param data The Transferable * @param action The action */ protected void exportDone(JComponent c, Transferable data, int action) { if ((action == MOVE) && shouldCutWhenDone) { StringCollage component = (StringCollage)c; component.setText(null); } source = null; } /** * Return true if the Component in the TransferSupport can support one of the * DataFlavor objects in the TransferSupport. * * @param support The TransferSupport */ public boolean canImport(TransferHandler.TransferSupport support) { DataFlavor[] flavors = support.getDataFlavors(); for (DataFlavor flavor: flavors) { if (flavor.equals(DataFlavor.stringFlavor)) return true; } return false; } }
TransferHandler
(cont.)/** * Default Constructor. */ public StringCollage() { super(); elements = new ArrayList<StringContent>(); this.action = DnDConstants.ACTION_MOVE; // Disable drag by default setDragEnabled(false); // Use a custom TransferHandler setTransferHandler(new StringTransferHandler()); // Add a MouseListener that responds to mousePressed messages // by exporting them to a TransferHandler addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, action); } } ); }
TransferHandler
and DataFlavor
TransferHndler
TransferHndler
TransferHandler
TransferHandler
import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A Component that supports drag-and-drop and cut/copy/paste * for Image objects. * * @author Prof. David Bernstein * @version DnD (Copy Only); Standard TransferHandler */ public class ImagePanel extends JPanel { private static final long serialVersionUID = 1L; private boolean dragEnabled; private Image image; /** * Default Constructor. */ public ImagePanel() { super(); image = null; setTransferHandler(new TransferHandler("image")); // Add a MouseListener that will export mousePressed messages // to the TransferHandler as drags. addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.COPY); } } ); } // Methods required by ImageTransferer /** * Get the Image associated with this ImagePanel. * * @return The Image */ public Image getImage() { return image; } /** * Get the source actions supported by this ImageTransferer. * * @return TransferHandler.COPY */ public int getSourceActions() { return TransferHandler.COPY; } /** * Set the Image on this Component. * * @param image The Image to use. */ public void setImage(Image image) { this.image = image; repaint(); } // Methods that provide the desired GUI functionality /** * Is drag enabled for this Component? * * @return true if enabled; false otherwise */ public boolean getDragEnabled() { return dragEnabled; } /** * Render this Component. * * @param g The rendering engine to use */ public void paint(Graphics g) { super.paint(g); if (image != null) { int width = getWidth(); int height = getHeight(); int iw = image.getWidth(null); int ih = image.getHeight(null); g.drawImage(image, width/2 - iw/2, height/2 - ih/2, null); } } /** * Determine whether drag is enabled on this Component. * * @param enabled true to enable; false to disable */ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } }
TransferHandler
import java.awt.*; import java.io.*; import javax.imageio.*; import javax.swing.*; /** * A demonstration of a Component that provides drag and drop * support for Image objects. * * @author Prof. David Bernstein, James Madison University */ public class ImagePanelDemo implements Runnable { /** * Construct and display the GUI. * * @param args The command line arguments (which are ignored) * @throws Exception If something goes wrong */ public static void main(String[] args) throws Exception { SwingUtilities.invokeAndWait(new ImagePanelDemo()); } /** * The code to run in the event dispatch thread. */ public void run() { JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(640, 480); JPanel cp = (JPanel)f.getContentPane(); cp.setLayout(new GridLayout(1,2)); ImagePanel left = new ImagePanel(); left.setBorder(BorderFactory.createTitledBorder("Left")); left.setDragEnabled(true); cp.add(left); ImagePanel right = new ImagePanel(); right.setBorder(BorderFactory.createTitledBorder("Right")); right.setDragEnabled(true); cp.add(right); Image image; try { image = ImageIO.read(new File("WilsonHall.png")); } catch (IOException ioe) { image = null; } left.setImage(image); f.setVisible(true); } }
Transferable
interfaceStringSelection
class) for this
purposeTransferable
version of the object of
interestTransferable
interfaceTransferable
Functionality:
Transferable
interfaceTransferable
ImageImage
is an abstract class, so we must
extend either BufferedImage
or
VolatileImage
which is limitingImage
isn't an interface so the
decorator must extend it rather than implement it
which can be confusingTransferable
Image (cont.)StringSelection
?
String
objects so nothing new is requiredTransferHandler
Transferable
(which must use a local
object reference)TransferHandler
(cont.)Transferable
import java.awt.*; import java.awt.datatransfer.*; /** * A wrapper for an Image that provides all of the functionality * require by the Transferable interface. * * @author Prof. David Bernstein, James Madison University * @version Works with a "standard" TransferHandler */ public class ImageSelection implements Transferable { private Image image; private DataFlavor JVM_LOCAL_OBJECT; /** * Construct a TransferableImage. * * @param image The Image to decorate */ public ImageSelection(Image image) { this.image = image; try { JVM_LOCAL_OBJECT = new DataFlavor("application/x-java-jvm-local-objectref;class=java.awt.image.BufferedImage"); } catch (ClassNotFoundException cnfe) { JVM_LOCAL_OBJECT = DataFlavor.imageFlavor; } } // Required by the Transferable interface /** * Get the DataFlavor objects supported by this Transferable. * * @return The DataFlavor objects */ public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { JVM_LOCAL_OBJECT }; } /** * Is the given DataFlavor supported by this Transferable? * * @param DataFlavor The DataFlavor of interest * @return true if it is supported; false otherwise */ public boolean isDataFlavorSupported(DataFlavor flavor) { for (DataFlavor supported: getTransferDataFlavors()) { if (flavor.equals(supported)) return true; } return false; } /** * Get the Transferable Object for a particular DataFlavor. * * @param flavor The DataFlavor of interest */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (!isDataFlavorSupported(flavor)) throw new UnsupportedFlavorException(flavor); return image; } }
TransferHandler
(cont.)import java.awt.*; import java.awt.dnd.*; import javax.swing.*; /** * A Component that supports drag and drop for Image objects. * * @author Prof. David Bernstein * @version DnD (Move Only); Standard TransferHandler */ public class ImagePanel extends JPanel implements DropTargetListener, DragGestureListener, DragSourceListener { private static final long serialVersionUID = 1L; private boolean dragEnabled; private DragSource source; private Image image; /** * Default Constructor. */ public ImagePanel() { image = null; // Create a DropTarget for this Component. // Note: This Component is also the DropTargetListener. new DropTarget(this, this); // Create a DragSource for this Component. // Note: This Component is also the DropTargetListener. source = new DragSource(); source.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this); // Use a bean-oriented handler for objects with an image property // (i.e., with getImage() and setImage() methods) TransferHandler handler = new TransferHandler("image"); setTransferHandler(handler); } // Methods required by image beans /** * Get the Image associated with this ImagePanel. * * This method is required so that this Component is a bean * with the image property. * * @return The Image */ public Image getImage() { return image; } /** * Set the Image on this Component. * * This method is required so that this Component is a bean * with the image property. * * @param image The Image to use. */ public void setImage(Image image) { this.image = image; repaint(); } // Methods that provide the desired GUI functionality /** * Is drag enabled for this Component? * * @return true if enabled; false otherwise */ public boolean getDragEnabled() { return dragEnabled; } /** * Render this Component. * * @param g The rendering engine to use */ public void paint(Graphics g) { super.paint(g); if (image != null) { int width = getWidth(); int height = getHeight(); int iw = image.getWidth(null); int ih = image.getHeight(null); g.drawImage(image, width/2 - iw/2, height/2 - ih/2, null); } } /** * Determine whether drag is enabled on this Component. * * @param enabled true to enable; false to disable */ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } // Methods required of DropTargetListener objects /** * Handle dragEnter messages. * * @param dtde The event that generated the message */ public void dragEnter(DropTargetDragEvent dtde) { } /** * Handle dragOver messages. * * @param dtde The event that generated the message */ public void dragOver(DropTargetDragEvent dtde) { } /** * Handle dropActionChanged messages. * * @param dtde The event that generated the message */ public void dropActionChanged(DropTargetDragEvent dtde) { } /** * Handle dragEnter messages. * * @param dtde The event that generated the message */ public void dragExit(DropTargetEvent dte) { } /** * Handle drop messages. * * @param dtde The event that generated the message */ public void drop(DropTargetDropEvent dtde) { // Invoke the TransferHandler object's importData() method. // The TransferHandler will, in turn, invoke this Component's // setImage() method. TransferHandler.TransferSupport support; support = new TransferHandler.TransferSupport(this, dtde.getTransferable()); getTransferHandler().importData(support); } // Methods required by DragSourceListener objects /** * Handle dragEnter messages. * * @param dsde The event that generated the message */ public void dragEnter(DragSourceDragEvent dsde) { } /** * Handle dragOver messages. * * @param dsde The event that generated the message */ public void dragOver(DragSourceDragEvent dsde) { } /** * Handle dropActionChanged messages. * * @param dsde The event that generated the message */ public void dropActionChanged(DragSourceDragEvent dsde) { } /** * Handle dragExit messages. * * @param dsde The event that generated the message */ public void dragExit(DragSourceEvent dse) {} /** * Handle dragDropEnd messages. * * @param dsde The event that generated the message */ public void dragDropEnd(DragSourceDropEvent dsde) { // Since this Component only supports move actions, // delete the Image. setImage(null); } // Methods required by DragGestureListener objects /** * Handle dragGestureRecognized messages. * * @param dge The event that generated the message */ public void dragGestureRecognized(DragGestureEvent dge) { source.startDrag(dge, DragSource.DefaultMoveDrop, new ImageSelection(image), this); } }
TransferHandler
Transferable
(which can now use
a more flexible DataFlavor
)TransferHandler
TransferHandler
(cont.)import java.awt.*; /** * The requirements of a Component that can transfer an Image * (e.g., using drag-and-drop or cut/copy/paste). * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public interface ImageTransferer { /** * Get the current image. * * @return The Image */ public abstract Image getImage(); /** * Get the source actions supported by this ImageTransferer. * * @return TransferHandler.MOVE or TransferHandler.COPY */ public abstract int getSourceActions(); /** * Set the current image. * * @param The Image to use */ public abstract void setImage(Image image); }
TransferHandler
(cont.)Transferable
import java.awt.*; import java.awt.datatransfer.*; /** * A Transferable wrapper for an Image object. * * @author Prof. David Bernstein, James Madison University * @version For use with an ImageTransferHandler */ public class ImageSelection implements Transferable { public static final DataFlavor IMAGE_FLAVOR = DataFlavor.imageFlavor; private Image image; /** * Construct an ImageSelection. * * @param image The Image to wrap */ public ImageSelection(Image image) { this.image = image; } /** * Get the DataFlavor objects supported by this Transferable. * * @return The DataFlavor objects */ public DataFlavor[] getTransferDataFlavors() { return new DataFlavor[] { DataFlavor.imageFlavor }; } /** * Is the given DataFlavor supported by this Transferable? * * @param DataFlavor The DataFlavor of interest * @return true if it is supported; false otherwise */ public boolean isDataFlavorSupported(DataFlavor flavor) { for (DataFlavor supported: getTransferDataFlavors()) { if (flavor.equals(supported)) return true; } return false; } /** * Get the Transferable Object for a particular DataFlavor. * * @param flavor The DataFlavor of interest */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (!isDataFlavorSupported(flavor)) throw new UnsupportedFlavorException(flavor); return image; } }
TransferHandler
(cont.)TransferHandler
import java.awt.*; import java.io.IOException; import java.awt.datatransfer.*; import javax.swing.*; /** * A TransferHandler that supports DnD and CCP on Component objects * that implement the ImageTransferer interface. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class ImageTransferHandler extends TransferHandler { private static final long serialVersionUID = 1L; private boolean shouldCutWhenDone; private ImageTransferer source; /** * Import the Transferable in the TransferSupport into the * JComponent in the TransferSupport. * * @param support The TransferSupport * @return true if the data was imported; false otherwise */ public boolean importData(TransferHandler.TransferSupport support) { JComponent c = (JComponent)support.getComponent(); Transferable t = support.getTransferable(); shouldCutWhenDone = false; if ((source != c) && canImport(support)) { ImageTransferer component = (ImageTransferer)c; try { Image image = (Image)t.getTransferData(ImageSelection.IMAGE_FLAVOR); component.setImage(image); shouldCutWhenDone = true; return true; } catch (IOException | UnsupportedFlavorException e) { return false; } } return false; } /** * Create a Transferable using the contents of the given JComponent. * */ protected Transferable createTransferable(JComponent c) { ImageTransferer component = (ImageTransferer)c; source = component; shouldCutWhenDone = true; // By default return new ImageSelection(component.getImage()); } /** * Get the source actions supported by this TransferHandler * for the given Component. * * @param c The Component of interest * @return The source actions supported (COPY_OR_MOVE) */ public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } /** * Complete the export process. * * @param c The Component * @param data The Transferable * @param action The action */ protected void exportDone(JComponent c, Transferable data, int action) { if ((action == MOVE) && shouldCutWhenDone) { ImageTransferer component = (ImageTransferer)c; component.setImage(null); } source = null; } /** * Return true if the Component in the TransferSupport can support one of the * DataFlavor objects in the TransferSupport. * * @param support The TransferSupport */ public boolean canImport(TransferHandler.TransferSupport support) { DataFlavor[] flavors = support.getDataFlavors(); for (DataFlavor flavor: flavors) { if (flavor.equals(ImageSelection.IMAGE_FLAVOR)) return true; } return false; } }
TransferHandler
(cont.)import java.awt.*; import java.awt.event.*; import javax.swing.*; /** * A Component that supports drag-and-drop for Image objects. * * @author Prof. David Bernstein * @version DnD (Move or Copy); Custom TransferHandler */ public class ImagePanel extends JPanel implements ImageTransferer { private static final long serialVersionUID = 1L; private boolean dragEnabled; private Image image; /** * Default Constructor. */ public ImagePanel() { super(); image = null; setTransferHandler(new ImageTransferHandler()); // Add a MouseListener that will forward mousePressed messages // to a TransferHandler as drags addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.MOVE); } } ); } // Methods required by ImageTransferer /** * Get the Image associated with this ImagePanel. * * @return The Image */ public Image getImage() { return image; } /** * Get the source actions supported by this ImageTransferer. * * @return TransferHandler.MOVE */ public int getSourceActions() { return TransferHandler.MOVE; } /** * Set the Image on this Component. * * @param image The Image to use. */ public void setImage(Image image) { this.image = image; repaint(); } // Methods that provide the desired GUI functionality /** * Is drag enabled for this Component? * * @return true if enabled; false otherwise */ public boolean getDragEnabled() { return dragEnabled; } /** * Render this Component. * * @param g The rendering engine to use */ public void paint(Graphics g) { super.paint(g); if (image != null) { int width = getWidth(); int height = getHeight(); int iw = image.getWidth(null); int ih = image.getHeight(null); g.drawImage(image, width/2 - iw/2, height/2 - ih/2, null); } } /** * Determine whether drag is enabled on this Component. * * @param enabled true to enable; false to disable */ public void setDragEnabled(boolean enabled) { dragEnabled = enabled; } }
Appointment
(which has Date
and String
attributes) from one GUI component
to anotherAppointment
class (which implements
Transferable
)TransferHandler
Appoinment
Classimport java.awt.datatransfer.*; import java.util.*; /** * An encapsulation of an Appointment. * * Note that this class implements the Transferable interface so there * is no reason to also create a wrapper. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class Appointment implements Transferable { public static DataFlavor APPOINTMENT_FLAVOR = new DataFlavor(Appointment.class, "Appointment"); private static DataFlavor[] flavors = {APPOINTMENT_FLAVOR}; private Date date; private String description; /** * Construct an Appointment with no description and the * current date/time. */ public Appointment() { this(new Date(), ""); } /** * Construct an Appointment with the given description and the * current date/time. * * @param description The description */ public Appointment(String description) { this(new Date(), description); } /** * Construct an Appointment with the given date and description. * * @param date The date * @param description The description */ public Appointment(Date date, String description) { this.date = date; this.description = description; } /** * Get the date. * * @return The date. */ public Date getDate() { return date; } /** * Get the description. * * @return The description. */ public String getDescription() { return description; } // Methods required by Transferable /** * Get the Object to transfer based on the given DataFlavor. * * @return The Object * @throws UnsupportedFlavorException if the DataFlavor isn't supported */ public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException { if (flavor.equals(Appointment.APPOINTMENT_FLAVOR)) { return this; } throw new UnsupportedFlavorException(flavor); } /** * Get an array of all of the DataFlavor objects that are supported. * * @return The DataFlavor objects */ public DataFlavor[] getTransferDataFlavors() { return flavors; } /** * Is the given DataFlavor supported? * * @param flavor The DataFlavor of interest * @return true if supported; false otherwise */ public boolean isDataFlavorSupported(DataFlavor flavor) { for (int i=0; i<flavors.length; i++) { if (flavor.equals(flavors[0])) return true; } return false; } }
/** * The requirements of a Component that can transfer an Appointment * (e.g., using drag-and-drop or cut/copy/paste). * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public interface AppointmentTransferer { /** * Get the current Appointment. * * @return The Appointment */ public abstract Appointment getAppointment(); /** * Get the source actions supported by this AppointmentTransferer. * * @return TransferHandler.MOVE or TransferHandler.COPY */ public abstract int getSourceActions(); /** * Set the current Appointment. * * @param The Appointment to use */ public abstract void setAppointment(Appointment appointment); }
TransferHandler
import java.io.IOException; import java.awt.datatransfer.*; import javax.swing.*; /** * A TransferHandler that supports DnD and CCP on Component objects * that implement the ImageTransferer interface. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class AppointmentTransferHandler extends TransferHandler { private static final long serialVersionUID = 1L; private boolean shouldCutWhenDone; private AppointmentTransferer source; /** * Import the Transferable in the TransferSupport into the * JComponent in the TransferSupport. * * @param support The TransferSupport * @return true if the data was imported; false otherwise */ public boolean importData(TransferHandler.TransferSupport support) { JComponent c = (JComponent)support.getComponent(); Transferable t = support.getTransferable(); shouldCutWhenDone = false; if ((source != c) && canImport(support)) { AppointmentTransferer component = (AppointmentTransferer)c; try { Appointment appointment = (Appointment)t.getTransferData(Appointment.APPOINTMENT_FLAVOR); component.setAppointment(appointment); shouldCutWhenDone = true; return true; } catch (IOException | UnsupportedFlavorException e) { return false; } } return false; } /** * Create a Transferable using the contents of the given JComponent. * */ protected Transferable createTransferable(JComponent c) { AppointmentTransferer component = (AppointmentTransferer)c; source = component; shouldCutWhenDone = true; // By default return component.getAppointment(); } /** * Get the source actions supported by this TransferHandler * for the given Component. * * @param c The Component of interest * @return The source actions supported (COPY_OR_MOVE) */ public int getSourceActions(JComponent c) { return COPY_OR_MOVE; } /** * Complete the export process. * * @param c The Component * @param data The Transferable * @param action The action */ protected void exportDone(JComponent c, Transferable data, int action) { if ((action == MOVE) && shouldCutWhenDone) { AppointmentTransferer component = (AppointmentTransferer)c; component.setAppointment(null); } source = null; } /** * Return true if the Component in the TransferSupport can support one of the * DataFlavor objects in the TransferSupport. * * @param support The TransferSupport */ public boolean canImport(TransferHandler.TransferSupport support) { DataFlavor[] flavors = support.getDataFlavors(); for (DataFlavor flavor: flavors) { if (flavor.equals(Appointment.APPOINTMENT_FLAVOR)) { return true; } } return false; } }
import java.awt.*; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import javax.swing.*; /** * A GUI Component that supports the drag-and-drop of Appointment objects. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class AppointmentPanel extends JPanel implements AppointmentTransferer { private static final long serialVersionUID = 1L; private Appointment appointment; private boolean dragEnabled; private JLabel dateLabel, descriptionLabel; /** * Construct an AppointmentPanel without a title. */ public AppointmentPanel() { this(null); } /** * Construct an AppointmentPanel with a title. * * @param title The title. */ public AppointmentPanel(String title) { super(); setLayout(new GridLayout(2,1)); if (title == null) setBorder(BorderFactory.createLineBorder(Color.BLACK)); else setBorder(BorderFactory.createTitledBorder(title)); descriptionLabel = new JLabel(); descriptionLabel.setBorder(BorderFactory.createTitledBorder("Description")); dateLabel = new JLabel(); dateLabel.setBorder(BorderFactory.createTitledBorder("Date/Time")); add(descriptionLabel); add(dateLabel); setDragEnabled(true); setTransferHandler(new AppointmentTransferHandler()); // Add a MouseListener that will forward mousePressed messages // to a TransferHandler as drags addMouseListener( new MouseAdapter() // An anonymous inner class that overrides mousePressed() in MouseAdapter { public void mousePressed(MouseEvent e) { JComponent c = (JComponent)e.getSource(); TransferHandler handler = c.getTransferHandler(); handler.exportAsDrag(c, e, TransferHandler.MOVE); } } ); } // Methods required by AppointmentTransferer /** * Get the Appointment on this AppointmentPanel. * * @return The Appointment */ public Appointment getAppointment() { return appointment; } /** * Get the source actions supported by this ImageTransferer. * * @return The supported source actions */ public int getSourceActions() { return TransferHandler.COPY_OR_MOVE; } /** * Set the Appointment to display on this AppointmentPanel. * * @param appointment The Appointment */ public void setAppointment(Appointment appointment) { this.appointment = appointment; if (appointment != null) { descriptionLabel.setText(appointment.getDescription()); dateLabel.setText(appointment.getDate().toString()); } else { descriptionLabel.setText(""); dateLabel.setText(""); } } // Other useful methods /** * Is drag enabled on this Component? * * @return true if enabled; false otherwise */ public boolean getDrageEnabled() { return dragEnabled; } /** * Set whether or not drag is enabled on this Component. * * @param enabled true to enable; false to disable */ public void setDragEnabled(boolean enabled) { this.dragEnabled= enabled; } }
import java.awt.*; import java.util.*; import javax.swing.*; /** * An application that demonstrates DnD for a custom object. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class AppointmentPanelDemo implements Runnable { /** * The entry point. * * @param args The command line arguments (which are ignored) */ public static void main(String[] args) throws Exception { SwingUtilities.invokeAndWait(new AppointmentPanelDemo()); } /** * The code to execute in the event dispatch thread. */ public void run() { String[] rooms = {"CS236", "CS243", "CS246", "CS248", "CS250"}; JFrame f = new JFrame(); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); f.setSize(640, 480); JPanel cp = (JPanel)f.getContentPane(); cp.setLayout(new GridLayout(1,2)); JPanel left = new JPanel(); left.setLayout(new GridLayout(3,1)); { AppointmentPanel ap = new AppointmentPanel(); ap.setAppointment(new Appointment(new Date(), "Database Demo")); left.add(ap); ap = new AppointmentPanel(); ap.setAppointment(new Appointment(new Date(), "Graphics Demo")); left.add(ap); ap = new AppointmentPanel(); ap.setAppointment(new Appointment(new Date(), "Robotics Demo")); left.add(ap); } cp.add(left); JPanel right = new JPanel(); right.setLayout(new GridLayout(rooms.length, 2)); { for (int i=0; i<rooms.length; i++) { AppointmentPanel ap = new AppointmentPanel(rooms[i]); right.add(ap); } } cp.add(right); f.setVisible(true); } }
TransferHandler
:
String
representation for the transfer
and create/parse as needed
(with
DataFlavor.stringFlavor
)DataFlavor.javaSerializedObjectMimeType
)new DataFlavor("application/x-java-jvm-local-objectref;class=Appointment")
)TransferHandler
:
DragSource
and or DropTarget
)