14.6 Custom Data Transfer

Example 14-5 is a custom Swing component named TransferableScribblePane. It allows the user to scribble with the mouse and to select one scribble at a time. It defines cut( ), copy( ), and paste( ) methods that operate on the selected line, and also allows the selected line to be dragged and dropped within the component or into another instance of the component. The scribbles are transferred using the TransferablePolyLine class defined in Example 14-4, of course.

This example does not use the high-level Swing TransferHandler class, but instead relies on the underlying data transfer and drag-and-drop architecture of java.awt.datatransfer and java.awt.dnd. The code for performing drag-and-drop is substantially more complex than the code for cut-and-paste. This is because the asynchronous drag-and-drop model requires a number of distinct event listeners and their corresponding event objects. The key interfaces are DragGestureListener, which triggers a new drag, and DragSourceListener and DropTargetListener, which notify the source of a drag and the target of a drop of important events that occur during the drag-and-drop process. The example implements all three interfaces as anonymous inner classes. When studying the example, pay particular attention to the dragGestureRecognized( ) method of the DragSourceListener and the drop( ) method of the DropTargetListener. The first is where the drag is initiated, and the second is where the data transfer actually transpires.

To test this component, display two copies of it using the ShowBean program. Draw a line with the mouse, then select the line by shift-clicking on it. Next, drag and drop the selected line while holding down the Shift or Ctrl keys. Also, use the Commands menu of ShowBean to invoke the cut( ), copy( ), and paste( ) methods.

Example 14-5. TransferableScribblePane.java
package je3.datatransfer; import java.awt.*;                  // Graphics, Rectangle, Stroke, etc. import java.awt.datatransfer.*;     // Transferable, DataFlavor import java.awt.dnd.*;              // Drag-and-drop listeners and events import java.awt.event.*;            // Mouse events import javax.swing.*;               // JComponent import javax.swing.border.*;        // LineBorder and BevelBorder import java.util.*;                 // ArrayList, etc. import java.util.List;    // Explicit import to disambiguate from java.awt.List import je3.graphics.PolyLine; // The Shape of scribbles /**  * This rewrite of ScribblePane allows individual PolyLine lines to be  * selected, cut, copied, pasted, dragged, and dropped.  */ public class TransferableScribblePane extends JComponent {     List lines;             // The PolyLines that comprise this scribble     PolyLine currentLine;   // The line currently being drawn     PolyLine selectedLine;  // The line that is current selected      boolean canDragImage;   // Can we drag an image of the line?     // Lines are 3 pixels wide, and the selected line is drawn dashed     static Stroke stroke = new BasicStroke(3.0f);     static Stroke selectedStroke = new BasicStroke(3, BasicStroke.CAP_BUTT,                                                    BasicStroke.JOIN_ROUND, 0f,                                                     new float[  ] { 3f, 3f, },0f);     // Different borders indicate receptivity to drops     static Border normalBorder = new LineBorder(Color.black, 3);     static Border canDropBorder = new BevelBorder(BevelBorder.LOWERED);     // The constructor method     public TransferableScribblePane( ) {         setPreferredSize(new Dimension(450,200)); // We need a default size         setBorder(normalBorder);                  // and a border.         lines = new ArrayList( );  // Start with an empty list of lines         // Register interest in mouse button and mouse motion events.         enableEvents(AWTEvent.MOUSE_EVENT_MASK |                      AWTEvent.MOUSE_MOTION_EVENT_MASK);         // Enable drag-and-drop by specifying a listener that will be          // notified when a drag begins.  dragGestureListener is defined later.         DragSource dragSource = DragSource.getDefaultDragSource( );         dragSource.createDefaultDragGestureRecognizer(this,                                      DnDConstants.ACTION_COPY_OR_MOVE,                                                       dragGestureListener);         // Enable drops on this component by registering a listener to          // be notified when something is dragged or dropped over us.         this.setDropTarget(new DropTarget(this, dropTargetListener));         // Check whether the system allows us to drag an image of the line         canDragImage = dragSource.isDragImageSupported( );     }     /** We override this method to draw ourselves. */     public void paintComponent(Graphics g) {         // Let the superclass do its painting first         super.paintComponent(g);         // Make a copy of the Graphics context so we can modify it         Graphics2D g2 = (Graphics2D) (g.create( ));          // Our superclass doesn't paint the background, so do this ourselves.         g2.setColor(getBackground( ));         g2.fillRect(0, 0, getWidth( ), getHeight( ));         // Set the line width and color to use for the foreground         g2.setStroke(stroke);         g2.setColor(this.getForeground( ));         // Now loop through the PolyLine shapes and draw them all         int numlines = lines.size( );         for(int i = 0; i < numlines; i++) {             PolyLine line = (PolyLine)lines.get(i);             if (line == selectedLine) {        // If it is the selected line                 g2.setStroke(selectedStroke);  // Set dash pattern                 g2.draw(line);                 // Draw the line                 g2.setStroke(stroke);          // Revert to solid lines             }             else g2.draw(line);  // Otherwise just draw the line         }     }     /**      * This method is called on mouse button events.  It begins a new line      * or tries to select an existing line.      */     public void processMouseEvent(MouseEvent e) {         if (e.getButton( ) == MouseEvent.BUTTON1) {         // Left mouse button             if (e.getID( ) == MouseEvent.MOUSE_PRESSED) {   // Pressed down                  if (e.isShiftDown( )) {                     // with Shift key                     // If the shift key is down, try to select a line                     int x = e.getX( );                     int y = e.getY( );                                      // Loop through the lines, checking to see if we hit one                     PolyLine selection = null;                     int numlines = lines.size( );                     for(int i = 0; i < numlines; i++) {                         PolyLine line = (PolyLine)lines.get(i);                         if (line.intersects(x-2, y-2, 4, 4)) {                             selection = line;                             e.consume( );                             break;                         }                     }                     // If we found an intersecting line, save it and repaint                     if (selection != selectedLine) { // If selection changed                         selectedLine = selection; // remember which is selected                         repaint( );                // will make selection dashed                     }                 }                 else if (!e.isControlDown( )) {   // no shift key or ctrl key                     // Start a new line on mouse down without shift or ctrl                     currentLine = new PolyLine(e.getX( ), e.getY( ));                     lines.add(currentLine);                     e.consume( );                 }             }             else if (e.getID( ) == MouseEvent.MOUSE_RELEASED) {// Left Button Up                 // End the line on mouse up                 if (currentLine != null) {                     currentLine = null;                     e.consume( );                 }             }         }         // The superclass method dispatches to registered event listeners         super.processMouseEvent(e);     }     /**      * This method is called for mouse motion events.      * We don't have to detect gestures that initiate a drag in this method.      * That is the job of the DragGestureRecognizer we created in the       * constructor: it will notify the DragGestureListener defined below.      */     public void processMouseMotionEvent(MouseEvent e) {         if (e.getID( ) == MouseEvent.MOUSE_DRAGGED &&     // If we're dragging             currentLine != null) {                       // and a line exists             currentLine.addSegment(e.getX( ), e.getY( ));  // Add a line segment             e.consume( );                                 // Eat the event             repaint( );                                   // Redisplay all lines         }         super.processMouseMotionEvent(e); // Invoke any listeners     }     /** Copy the selected line to the clipboard, then delete it */     public void cut( ) {         if (selectedLine == null) return; // Only works if a line is selected         copy( );                           // Do a Copy operation...         lines.remove(selectedLine);       // and then erase the selected line         selectedLine = null;         repaint( );                        // Repaint because a line was removed     }     /** Copy the selected line to the clipboard */     public void copy( ) {         if (selectedLine == null) return; // Only works if a line is selected         // Get the system Clipboard object.         Clipboard c = this.getToolkit( ).getSystemClipboard( );         // Wrap the selected line in a TransferablePolyLine object         // and pass it to the clipboard, with an object to receive notification         // when some other application takes ownership of the clipboard         c.setContents(new TransferablePolyLine((PolyLine)selectedLine.clone( )),                       new ClipboardOwner( ) {                          public void lostOwnership(Clipboard c,Transferable t){                              // This method is called when something else                              // is copied to the clipboard.  We could use it                              // to deselect the selected line, if we wanted.                          }                       });     }     /** Get a PolyLine from the clipboard, if one exists, and display it */     public void paste( ) {         // Get the system Clipboard and ask for its Transferable contents         Clipboard c = this.getToolkit( ).getSystemClipboard( );          Transferable t = c.getContents(this);         // See if we can extract a PolyLine from the Transferable object         PolyLine line;         try {             line = (PolyLine)t.getTransferData(TransferablePolyLine.FLAVOR);         }         catch(Exception e) {  // UnsupportedFlavorException or IOException             // If we get here, the clipboard doesn't hold a PolyLine we can use             getToolkit( ).beep( );   // So beep to indicate the error             return;         }                  lines.add(line); // We got a line from the clipboard, so add it to list         repaint( );       // And repaint to make the line appear     }     /** Erase all lines and repaint. */     public void clear( ) {         lines.clear( );         repaint( );     }     /**      * This DragGestureListener is notified when the user initiates a drag.      * We passed it to the DragGestureRecognizer we created in the constructor.      */     public DragGestureListener dragGestureListener = new DragGestureListener( ){             public void dragGestureRecognized(DragGestureEvent e) {                 // Don't start a drag if there isn't a selected line                 if (selectedLine == null) return;                 // Find out where the drag began                 MouseEvent trigger = (MouseEvent)e.getTriggerEvent( );                 int x = trigger.getX( );                 int y = trigger.getY( );                 // Don't do anything if the drag was not near the selected line                 if (!selectedLine.intersects(x-4, y-4, 8, 8)) return;                 // Make a copy of the selected line, adjust the copy so that                 // the point under the mouse is (0,0), and wrap the copy in a                  // Tranferable wrapper.                 PolyLine copy = (PolyLine)selectedLine.clone( );                 copy.translate(-x, -y);                 Transferable t = new TransferablePolyLine(copy);                                  // If the system allows custom images to be dragged, make                 // an image of the line on a transparent background                 Image dragImage = null;                 Point hotspot = null;                 if (canDragImage) {                     Rectangle box = copy.getBounds( );                     dragImage = createImage(box.width, box.height);                     Graphics2D g = (Graphics2D)dragImage.getGraphics( );                     g.setColor(new Color(0,0,0,0));  // transparent bg                     g.fillRect(0, 0, box.width, box.height);                     g.setColor(getForeground( ));                     g.setStroke(selectedStroke);                     g.translate(-box.x, -box.y);                     g.draw(copy);                     hotspot = new Point(-box.x, -box.y);                                      }                                  // Now begin dragging the line, specifying the listener                 // object to receive notifications about the progress of                 // the operation.  Note: the startDrag( ) method is defined by                 // the event object, which is unusual.                 e.startDrag(null,       // Use default drag-and-drop cursors                             dragImage,  // Use the image, if supported                             hotspot,    // Ditto for the image hotspot                             t,          // Drag this object                             dragSourceListener); // Send notifications here             }         };     /**      * If this component is the source of a drag, then this DragSourceListener      * will receive notifications about the progress of the drag.  The only      * one we use here is dragDropEnd( ) which is called after a drop occurs.      * We could use the other methods to change cursors or perform other      * "drag over effects"      */     public DragSourceListener dragSourceListener = new DragSourceListener( ) {             // Invoked when dragging stops             public void dragDropEnd(DragSourceDropEvent e) {                 if (!e.getDropSuccess( )) return; // Ignore failed drops                 // If the drop was a move, then delete the selected line                 if (e.getDropAction( ) == DnDConstants.ACTION_MOVE) {                     lines.remove(selectedLine);                     selectedLine = null;                     repaint( );                 }             }             // The following methods are unused here.  We could implement them             // to change custom cursors or perform other "drag over effects".             public void dragEnter(DragSourceDragEvent e) {  }             public void dragExit(DragSourceEvent e) {  }             public void dragOver(DragSourceDragEvent e) {  }             public void dropActionChanged(DragSourceDragEvent e) {  }         };     /**      * This DropTargetListener is notified when something is dragged over      * this component.      */     public DropTargetListener dropTargetListener = new DropTargetListener( ) {             // This method is called when something is dragged over us.             // If we understand what is being dragged, then tell the system             // we can accept it, and change our border to provide extra             // "drag under" visual feedback to the user to indicate our             // receptivity to a drop.             public void dragEnter(DropTargetDragEvent e) {                 if (e.isDataFlavorSupported(TransferablePolyLine.FLAVOR)) {                     e.acceptDrag(e.getDropAction( ));                     setBorder(canDropBorder);                 }             }             // Revert to our normal border if the drag moves off us.             public void dragExit(DropTargetEvent e) { setBorder(normalBorder); }             // This method is called when something is dropped on us.             public void drop(DropTargetDropEvent e) {                 // If a PolyLine is dropped, accept either a COPY or a MOVE                 if (e.isDataFlavorSupported(TransferablePolyLine.FLAVOR))                     e.acceptDrop(e.getDropAction( ));                 else {  // Otherwise, reject the drop and return                     e.rejectDrop( );                     return;                 }                 // Get the dropped object and extract a PolyLine from it                 Transferable t = e.getTransferable( );                 PolyLine line;                 try {                     line =                        (PolyLine)t.getTransferData(TransferablePolyLine.FLAVOR);                 }                 catch(Exception ex) {  // UnsupportedFlavor or IOException                     getToolkit( ).beep( );   // Something went wrong, so beep                     e.dropComplete(false); // Tell the system we failed                     return;                 }                 // Figure out where the drop occurred, and translate so the                 // point that was formerly (0,0) is now at that point.                 Point p = e.getLocation( );                 line.translate((float)p.getX( ), (float)p.getY( ));                 // Add the line to our list, and repaint                  lines.add(line);                 repaint( );                 // Tell the system that we successfully completed the transfer.                 // This means it is safe for the initiating component to delete                 // its copy of the line                 e.dropComplete(true);             }             // We could provide additional drag under effects with this method.             public void dragOver(DropTargetDragEvent e) {  }             // If we used custom cursors, we would update them here.             public void dropActionChanged(DropTargetDragEvent e) {  }          }; }


Java Examples in a Nutshell
Java Examples in a Nutshell, 3rd Edition
ISBN: 0596006209
EAN: 2147483647
Year: 2003
Pages: 285

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net