[ Team LiB ] |
![]() ![]() |
14.2 A Clock with Drag and Copy SupportAnother way to customize Swing drag-and-drop is to subclass a Swing component, define new property accessor methods for it, and then register a TransferHandler to transfer the value of the new property. This is what we do in Example 14-2: we define a custom Swing component that displays the current time and uses a TransferHandler to make the contents of its new time property available. Like Example 14-1, this program uses a MouseMotionListener to detect drags. It also defines a key binding so that Ctrl-C copies the time to the clipboard. This example defines a custom component, but not a main( ) method: use the ShowBean program of Chapter 11 to display the component. You may want to run ShowBean again to display a JTextField or similar component, so that you have somewhere to drop or paste the time values you've dragged or copied. Also try dropping or pasting the value into other non-Java applications (such as your text editor) that you have running on your desktop. Example 14-2 also demonstrates the javax.swing.Timer and java.text.DateFormat classes, and shows how to use the (new in Java 1.4) InputMap and ActionMap Swing classes for associating key bindings with components. Example 14-2. DigitalClock.javapackage je3.datatransfer; import javax.swing.*; import javax.swing.border.*; import java.awt.*; import java.awt.event.*; import java.awt.datatransfer.*; import javax.swing.Timer; // disambiguate from java.util.Timer import java.text.DateFormat; import java.util.Date; /** * A custom Swing component that displays a simple digital clock. * Demonstrates how to add copy and drag support to a Swing component * with TransferHandler. */ public class DigitalClock extends JLabel { DateFormat format; // How to display the time in string form int updateFrequency; // How often to update the time (in milliseconds) Timer timer; // Triggers repeated updates to the clock public DigitalClock( ) { // Set default values for our properties setFormat(DateFormat.getTimeInstance(DateFormat.MEDIUM, getLocale( ))); setUpdateFrequency(1000); // Update once a second // Specify a Swing TransferHandler object to do the dirty work of // copy-and-paste and drag-and-drop for us. This one will transfer // the value of the "time" property. Since this property is read-only // it will allow drags but not drops. setTransferHandler(new TransferHandler("time")); // Since JLabel does not normally support drag-and-drop, we need an // event handler to detect a drag and start the transfer. addMouseMotionListener(new MouseMotionAdapter( ) { public void mouseDragged(MouseEvent e) { getTransferHandler( ).exportAsDrag(DigitalClock.this, e, TransferHandler.COPY); } }); // Before we can have a keyboard binding for a Copy command, // the component needs to be able to accept keyboard focus. setFocusable(true); // Request focus when we're clicked on addMouseListener(new MouseAdapter( ) { public void mouseClicked(MouseEvent e) { requestFocus( ); } }); // Use a LineBorder to indicate when we've got the keyboard focus addFocusListener(new FocusListener( ) { public void focusGained(FocusEvent e) { setBorder(LineBorder.createBlackLineBorder( )); } public void focusLost(FocusEvent e) { setBorder(null); } }); // Now bind the Ctrl-C keystroke to a "Copy" command. InputMap im = new InputMap( ); im.setParent(getInputMap(WHEN_FOCUSED)); im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C,InputEvent.CTRL_MASK), "Copy"); setInputMap(WHEN_FOCUSED, im); // And bind the "Copy" command to a pre-defined Action that performs // a copy using the TransferHandler we've installed. ActionMap am = new ActionMap( ); am.setParent(getActionMap( )); am.put("Copy", TransferHandler.getCopyAction( )); setActionMap(am); // Create a javax.swing.Timer object that will generate ActionEvents // to tell us when to update the displayed time. Every updateFrequency // milliseconds, this timer will cause the actionPerformed( ) method // to be invoked. (For non-GUI applications, see java.util.Timer.) timer = new Timer(updateFrequency, new ActionListener( ) { public void actionPerformed(ActionEvent e) { setText(getTime( )); // set label to current time string } }); timer.setInitialDelay(0); // Do the first update immediately timer.start( ); // Start timing now! } // Return the current time as a String. // This is the property accessor method used by the TransferHandler. // Since there is a getter, but no setter, the TransferHandler will // reject any attempts to drop data on us. public String getTime( ) { // Use the DateFormat object to convert current time to a string return format.format(new Date( )); } // Here are two related property setter methods public void setFormat(DateFormat format) { this.format = format; } public void setUpdateFrequency(int ms) { this.updateFrequency = ms; } } When you try out this DigitalClock component, you may notice a shortcoming: the TransferHandler class calls getTime( ) when the time value is dropped or pasted, not when it is originally dragged or copied. This is counterintuitive, but it is how TransferHandler works. ![]() |
[ Team LiB ] |
![]() ![]() |