Cut, Copy and Paste
in Java |
Prof. David Bernstein |
Computer Science Department |
bernstdh@jmu.edu |
Component
that supports the actionsTransferHandler
TransferHandler
(cont.)import java.awt.event.*; import java.beans.*; import javax.swing.*; /** * An ActionForwarder keeps track of which GUI Component has the * focus and forwards actionPerformed messages to the appropriate * Component. * * @author Prof. David Bernstein, James Madison University * @version 1.0 */ public class ActionForwarder implements ActionListener, PropertyChangeListener { private JComponent focusOwner; /** * Default Constructor. */ public ActionForwarder() { focusOwner = null; } /** * Handle propertyChange messages. Specifically, keep track of * the Component with the focus. * * @param pce The event that generated the message */ public void propertyChange(PropertyChangeEvent pce) { Object source = pce.getNewValue(); if (source instanceof JComponent) { focusOwner = (JComponent)source; } else { focusOwner = null; } } /** * Handle actionPerformed messages. Specifically, forward them to * the Component with the focus (if that Component has an * appropriate Action). * * @param ae The event that generated the message. */ public void actionPerformed(ActionEvent e) { if (focusOwner == null) return; String actionCommand = (String)e.getActionCommand(); Action action = focusOwner.getActionMap().get(actionCommand); if (action != null) { action.actionPerformed(new ActionEvent(focusOwner, ActionEvent.ACTION_PERFORMED, null)); } } }
TransferHandler
(cont.)ActionForwarder
in the ApplicationKeyboardFocusManager manager = KeyboardFocusManager.getCurrentKeyboardFocusManager(); ActionForwarder forwarder = new ActionForwarder(); manager.addPropertyChangeListener("permanentFocusOwner", forwarder); // Enable the GUI components to indicate when they have the focus manager.addPropertyChangeListener("permanentFocusOwner", left); manager.addPropertyChangeListener("permanentFocusOwner", right);
TransferHandler
(cont.)setFocusable(true); ActionMap map = getActionMap(); map.put(TransferHandler.getCutAction().getValue(Action.NAME), TransferHandler.getCutAction()); map.put(TransferHandler.getCopyAction().getValue(Action.NAME), TransferHandler.getCopyAction()); map.put(TransferHandler.getPasteAction().getValue(Action.NAME), TransferHandler.getPasteAction()); InputMap imap = this.getInputMap(); imap.put(KeyStroke.getKeyStroke("ctrl X"), TransferHandler.getCutAction().getValue(Action.NAME)); imap.put(KeyStroke.getKeyStroke("ctrl C"), TransferHandler.getCopyAction().getValue(Action.NAME)); imap.put(KeyStroke.getKeyStroke("ctrl V"), TransferHandler.getPasteAction().getValue(Action.NAME));
TransferHandler
(cont.)TransferHandler
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
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
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
import java.awt.*; import java.awt.event.*; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; /** * A Component that supports drag-and-drop and cut/copy/paste * for Image objects. * * @author Prof. David Bernstein * @version Using a custom Transferable and TransferHandler */ public class ImagePanel extends JPanel implements ImageTransferer, PropertyChangeListener { private static final long serialVersionUID = 1L; private boolean dragEnabled; private Image image; /** * Default Constructor. */ public ImagePanel(String id) { super(); image = null; setTransferHandler(new ImageTransferHandler()); setFocusable(true); ActionMap map = getActionMap(); map.put(TransferHandler.getCutAction().getValue(Action.NAME), TransferHandler.getCutAction()); map.put(TransferHandler.getCopyAction().getValue(Action.NAME), TransferHandler.getCopyAction()); map.put(TransferHandler.getPasteAction().getValue(Action.NAME), TransferHandler.getPasteAction()); InputMap imap = this.getInputMap(); imap.put(KeyStroke.getKeyStroke("ctrl X"), TransferHandler.getCutAction().getValue(Action.NAME)); imap.put(KeyStroke.getKeyStroke("ctrl C"), TransferHandler.getCopyAction().getValue(Action.NAME)); imap.put(KeyStroke.getKeyStroke("ctrl V"), TransferHandler.getPasteAction().getValue(Action.NAME)); // Forward mousePressed messages to a TrasnferHandler at // the start of a drag 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 or TransferHandler.COPY */ public int getSourceActions() { return TransferHandler.COPY_OR_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; } // Methods required by PropertyChangeListener /** * Handle propertyChange messages. Specifically, keep track of * the Component with the focus. * * @param pce The event that generated the message */ public void propertyChange(PropertyChangeEvent pce) { Object source = pce.getNewValue(); if (source == this) setBackground(Color.WHITE); else setBackground(Color.GRAY); repaint(); } }