Drag and drop : Drag Drop « Swing JFC « Java

Drag and drop

Drag and drop
 * Copyright (c) 2000 David Flanagan.  All rights reserved.
 * This code is from the book Java Examples in a Nutshell, 2nd Edition.
 * It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
 * You may study, use, and modify it for any non-commercial purpose.
 * You may distribute it non-commercially as long as you retain this notice.
 * For a commercial use license, or to purchase the book (recommended),
 * visit http://www.davidflanagan.com/javaexamples2.

import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import java.awt.geom.PathIterator;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.StringTokenizer;

import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.border.BevelBorder;
import javax.swing.border.Border;

 * This component can operate in two modes. In "draw mode", it allows the user
 * to scribble with the mouse. In "drag mode", it allows the user to drag
 * scribbles with the mouse. Regardless of the mode, it always allows scribbles
 * to be dropped on it from other applications.
public class ScribbleDragAndDrop extends JComponent implements
    DragGestureListener, // For recognizing the start of drags
    DragSourceListener, // For processing drag source events
    DropTargetListener, // For processing drop target events
    MouseListener, // For processing mouse clicks
    MouseMotionListener // For processing mouse drags
  ArrayList scribbles = new ArrayList(); // A list of Scribbles to draw

  Scribble currentScribble; // The scribble in progress

  Scribble beingDragged; // The scribble being dragged

  DragSource dragSource; // A central DnD object

  boolean dragMode; // Are we dragging or scribbling?

  // These are some constants we use
  static final int LINEWIDTH = 3;

  static final BasicStroke linestyle = new BasicStroke(LINEWIDTH);

  static final Border normalBorder = new BevelBorder(BevelBorder.LOWERED);

  static final Border dropBorder = new BevelBorder(BevelBorder.RAISED);

  /** The constructor: set up drag-and-drop stuff */
  public ScribbleDragAndDrop() {
    // Give ourselves a nice default border.
    // We'll change this border during drag-and-drop.

    // Register listeners to handle drawing

    // Create a DragSource and DragGestureRecognizer to listen for drags
    // The DragGestureRecognizer will notify the DragGestureListener
    // when the user tries to drag an object
    dragSource = DragSource.getDefaultDragSource();
    dragSource.createDefaultDragGestureRecognizer(this, // What component
        DnDConstants.ACTION_COPY_OR_MOVE, // What drag types?
        this);// the listener

    // Create and set up a DropTarget that will listen for drags and
    // drops over this component, and will notify the DropTargetListener
    DropTarget dropTarget = new DropTarget(this, // component to monitor
        this); // listener to notify
    this.setDropTarget(dropTarget); // Tell the component about it.

   * The component draws itself by drawing each of the Scribble objects.
  public void paintComponent(Graphics g) {
    Graphics2D g2 = (Graphics2D) g;
    g2.setStroke(linestyle); // Specify wide lines

    int numScribbles = scribbles.size();
    for (int i = 0; i < numScribbles; i++) {
      Scribble s = (Scribble) scribbles.get(i);
      g2.draw(s); // Draw the scribble

  public void setDragMode(boolean dragMode) {
    this.dragMode = dragMode;

  public boolean getDragMode() {
    return dragMode;

   * This method, and the following four methods are from the MouseListener
   * interface. If we're in drawing mode, this method handles mouse down
   * events and starts a new scribble.
  public void mousePressed(MouseEvent e) {
    if (dragMode)
    currentScribble = new Scribble();
    currentScribble.moveto(e.getX(), e.getY());

  public void mouseReleased(MouseEvent e) {

  public void mouseClicked(MouseEvent e) {

  public void mouseEntered(MouseEvent e) {

  public void mouseExited(MouseEvent e) {

   * This method and mouseMoved() below are from the MouseMotionListener
   * interface. If we're in drawing mode, this method adds a new point to the
   * current scribble and requests a redraw
  public void mouseDragged(MouseEvent e) {
    if (dragMode)
    currentScribble.lineto(e.getX(), e.getY());

  public void mouseMoved(MouseEvent e) {

   * This method implements the DragGestureListener interface. It will be
   * invoked when the DragGestureRecognizer thinks that the user has initiated
   * a drag. If we're not in drawing mode, then this method will try to figure
   * out which Scribble object is being dragged, and will initiate a drag on
   * that object.
  public void dragGestureRecognized(DragGestureEvent e) {
    // Don't drag if we're not in drag mode
    if (!dragMode)

    // Figure out where the drag started
    MouseEvent inputEvent = (MouseEvent) e.getTriggerEvent();
    int x = inputEvent.getX();
    int y = inputEvent.getY();

    // Figure out which scribble was clicked on, if any by creating a
    // small rectangle around the point and testing for intersection.
    Rectangle r = new Rectangle(x - LINEWIDTH, y - LINEWIDTH,
        LINEWIDTH * 2, LINEWIDTH * 2);
    int numScribbles = scribbles.size();
    for (int i = 0; i < numScribbles; i++) { // Loop through the scribbles
      Scribble s = (Scribble) scribbles.get(i);
      if (s.intersects(r)) {
        // The user started the drag on top of this scribble, so
        // start to drag it.

        // First, remember which scribble is being dragged, so we can
        // delete it later (if this is a move rather than a copy)
        beingDragged = s;

        // Next, create a copy that will be the one dragged
        Scribble dragScribble = (Scribble) s.clone();
        // Adjust the origin to the point the user clicked on.
        dragScribble.translate(-x, -y);

        // Choose a cursor based on the type of drag the user initiated
        Cursor cursor;
        switch (e.getDragAction()) {
        case DnDConstants.ACTION_COPY:
          cursor = DragSource.DefaultCopyDrop;
        case DnDConstants.ACTION_MOVE:
          cursor = DragSource.DefaultMoveDrop;
          return; // We only support move and copys

        // Some systems allow us to drag an image along with the
        // cursor. If so, create an image of the scribble to drag
        if (dragSource.isDragImageSupported()) {
          Rectangle scribbleBox = dragScribble.getBounds();
          Image dragImage = this.createImage(scribbleBox.width,
          Graphics2D g = (Graphics2D) dragImage.getGraphics();
          g.setColor(new Color(0, 0, 0, 0)); // transparent background
          g.fillRect(0, 0, scribbleBox.width, scribbleBox.height);
          g.translate(-scribbleBox.x, -scribbleBox.y);
          Point hotspot = new Point(-scribbleBox.x, -scribbleBox.y);

          // Now start dragging, using the image.
          e.startDrag(cursor, dragImage, hotspot, dragScribble, this);
        } else {
          // Or start the drag without an image
          e.startDrag(cursor, dragScribble, this);
        // After we've started dragging one scribble, stop looking

   * This method, and the four unused methods that follow it implement the
   * DragSourceListener interface. dragDropEnd() is invoked when the user
   * drops the scribble she was dragging. If the drop was successful, and if
   * the user did a "move" rather than a "copy", then we delete the dragged
   * scribble from the list of scribbles to draw.
  public void dragDropEnd(DragSourceDropEvent e) {
    if (!e.getDropSuccess())
    int action = e.getDropAction();
    if (action == DnDConstants.ACTION_MOVE) {
      beingDragged = null;

  // These methods are also part of DragSourceListener.
  // They are invoked at interesting points during the drag, and can be
  // used to perform "drag over" effects, such as changing the drag cursor
  // or drag image.
  public void dragEnter(DragSourceDragEvent e) {

  public void dragExit(DragSourceEvent e) {

  public void dropActionChanged(DragSourceDragEvent e) {

  public void dragOver(DragSourceDragEvent e) {

  // The next five methods implement DropTargetListener

   * This method is invoked when the user first drags something over us. If we
   * understand the data type being dragged, then call acceptDrag() to tell
   * the system that we're receptive. Also, we change our border as a "drag
   * under" effect to signal that we can accept the drop.
  public void dragEnter(DropTargetDragEvent e) {
    if (e.isDataFlavorSupported(Scribble.scribbleDataFlavor)
        || e.isDataFlavorSupported(DataFlavor.stringFlavor)) {

  /** The user is no longer dragging over us, so restore the border */
  public void dragExit(DropTargetEvent e) {

   * This is the key method of DropTargetListener. It is invoked when the user
   * drops something on us.
  public void drop(DropTargetDropEvent e) {
    this.setBorder(normalBorder); // Restore the default border

    // First, check whether we understand the data that was dropped.
    // If we supports our data flavors, accept the drop, otherwise reject.
    if (e.isDataFlavorSupported(Scribble.scribbleDataFlavor)
        || e.isDataFlavorSupported(DataFlavor.stringFlavor)) {
    } else {

    // We've accepted the drop, so now we attempt to get the dropped data
    // from the Transferable object.
    Transferable t = e.getTransferable(); // Holds the dropped data
    Scribble droppedScribble; // This will hold the Scribble object

    // First, try to get the data directly as a scribble object
    try {
      droppedScribble = (Scribble) t
    } catch (Exception ex) { // unsupported flavor, IO exception, etc.
      // If that doesn't work, try to get it as a String and parse it
      try {
        String s = (String) t.getTransferData(DataFlavor.stringFlavor);
        droppedScribble = Scribble.parse(s);
      } catch (Exception ex2) {
        // If we still couldn't get the data, tell the system we failed

    // If we get here, we've got the Scribble object
    Point p = e.getLocation(); // Where did the drop happen?
    droppedScribble.translate(p.getX(), p.getY()); // Move it there
    scribbles.add(droppedScribble); // add to display list
    repaint(); // ask for redraw
    e.dropComplete(true); // signal success!

  // These are unused DropTargetListener methods
  public void dragOver(DropTargetDragEvent e) {

  public void dropActionChanged(DropTargetDragEvent e) {

   * The main method. Creates a simple application using this class. Note the
   * buttons for switching between draw mode and drag mode.
  public static void main(String[] args) {
    // Create a frame and put a scribble pane in it
    JFrame frame = new JFrame("ScribbleDragAndDrop");
    final ScribbleDragAndDrop scribblePane = new ScribbleDragAndDrop();
    frame.getContentPane().add(scribblePane, BorderLayout.CENTER);

    // Create two buttons for switching modes
    JToolBar toolbar = new JToolBar();
    ButtonGroup group = new ButtonGroup();
    JToggleButton draw = new JToggleButton("Draw");
    JToggleButton drag = new JToggleButton("Drag");
    draw.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
    drag.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
    frame.getContentPane().add(toolbar, BorderLayout.NORTH);

    // Start off in drawing mode

    // Pop up the window
    frame.setSize(400, 400);

class Scribble implements Shape, Transferable, Serializable, Cloneable {
  protected double[] points = new double[64]; // The scribble data

  protected int numPoints = 0; // The current number of points

  double maxX = Double.NEGATIVE_INFINITY; // The bounding box

  double maxY = Double.NEGATIVE_INFINITY;

  double minX = Double.POSITIVE_INFINITY;

  double minY = Double.POSITIVE_INFINITY;

   * Begin a new polyline at (x,y). Note the use of Double.NaN in the points
   * array to mark the beginning of a new polyline
  public void moveto(double x, double y) {
    if (numPoints + 3 > points.length)
    // Mark this as the beginning of a new line
    points[numPoints++] = Double.NaN;
    // The rest of this method is just like lineto();
    lineto(x, y);

   * Add the point (x,y) to the end of the current polyline
  public void lineto(double x, double y) {
    if (numPoints + 2 > points.length)
    points[numPoints++] = x;
    points[numPoints++] = y;

    // See if the point enlarges our bounding box
    if (x > maxX)
      maxX = x;
    if (x < minX)
      minX = x;
    if (y > maxY)
      maxY = y;
    if (y < minY)
      minY = y;

   * Append the Scribble s to this Scribble
  public void append(Scribble s) {
    int n = numPoints + s.numPoints;
    double[] newpoints = new double[n];
    System.arraycopy(points, 0, newpoints, 0, numPoints);
    System.arraycopy(s.points, 0, newpoints, numPoints, s.numPoints);
    points = newpoints;
    numPoints = n;
    minX = Math.min(minX, s.minX);
    maxX = Math.max(maxX, s.maxX);
    minY = Math.min(minY, s.minY);
    maxY = Math.max(maxY, s.maxY);

   * Translate the coordinates of all points in the Scribble by x,y
  public void translate(double x, double y) {
    for (int i = 0; i < numPoints; i++) {
      if (Double.isNaN(points[i]))
      points[i++] += x;
      points[i] += y;
    minX += x;
    maxX += x;
    minY += y;
    maxY += y;

  /** An internal method to make more room in the data array */
  protected void reallocate() {
    double[] newpoints = new double[points.length * 2];
    System.arraycopy(points, 0, newpoints, 0, numPoints);
    points = newpoints;

  /** Clone a Scribble object and its internal array of data */
  public Object clone() {
    try {
      Scribble s = (Scribble) super.clone(); // make a copy of all fields
      s.points = (double[]) points.clone(); // copy the entire array
      return s;
    } catch (CloneNotSupportedException e) { // This should never happen
      return this;

  /** Convert the scribble data to a textual format */
  public String toString() {
    StringBuffer b = new StringBuffer();
    for (int i = 0; i < numPoints; i++) {
      if (Double.isNaN(points[i])) {
        b.append("m ");
      } else {
        b.append(' ');
    return b.toString();

   * Create a new Scribble object and initialize it by parsing a string of
   * coordinate data in the format produced by toString()
  public static Scribble parse(String s) throws NumberFormatException {
    StringTokenizer st = new StringTokenizer(s);
    Scribble scribble = new Scribble();
    while (st.hasMoreTokens()) {
      String t = st.nextToken();
      if (t.charAt(0) == 'm') {
        scribble.moveto(Double.parseDouble(st.nextToken()), Double
      } else {
        scribble.lineto(Double.parseDouble(t), Double.parseDouble(st
    return scribble;

  // ========= The following methods implement the Shape interface ========

  /** Return the bounding box of the Shape */
  public Rectangle getBounds() {
    return new Rectangle((int) (minX - 0.5f), (int) (minY - 0.5f),
        (int) (maxX - minX + 0.5f), (int) (maxY - minY + 0.5f));

  /** Return the bounding box of the Shape */
  public Rectangle2D getBounds2D() {
    return new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);

  /** Our shape is an open curve, so it never contains anything */
  public boolean contains(Point2D p) {
    return false;

  public boolean contains(Rectangle2D r) {
    return false;

  public boolean contains(double x, double y) {
    return false;

  public boolean contains(double x, double y, double w, double h) {
    return false;

   * Determine if the scribble intersects the specified rectangle by testing
   * each line segment individually
  public boolean intersects(Rectangle2D r) {
    if (numPoints < 4)
      return false;
    int i = 0;
    double x1, y1, x2 = 0.0, y2 = 0.0;
    while (i < numPoints) {
      if (Double.isNaN(points[i])) { // If we're beginning a new line
        i++; // Skip the NaN
        x2 = points[i++];
        y2 = points[i++];
      } else {
        x1 = x2;
        y1 = y2;
        x2 = points[i++];
        y2 = points[i++];
        if (r.intersectsLine(x1, y1, x2, y2))
          return true;

    return false;

  /** Test for intersection by invoking the method above */
  public boolean intersects(double x, double y, double w, double h) {
    return intersects(new Rectangle2D.Double(x, y, w, h));

   * Return a PathIterator object that tells Java2D how to draw this scribble
  public PathIterator getPathIterator(AffineTransform at) {
    return new ScribbleIterator(at);

   * Return a PathIterator that doesn't include curves. Ours never does.
  public PathIterator getPathIterator(AffineTransform at, double flatness) {
    return getPathIterator(at);

   * This inner class implements the PathIterator interface to describe the
   * shape of a scribble. Since a Scribble is composed of arbitrary movetos
   * and linetos, we simply return their coordinates
  public class ScribbleIterator implements PathIterator {
    protected int i = 0; // Position in array

    protected AffineTransform transform;

    public ScribbleIterator(AffineTransform transform) {
      this.transform = transform;

    /** How to determine insideness and outsideness for this shape */
    public int getWindingRule() {
      return PathIterator.WIND_NON_ZERO;

    /** Have we reached the end of the scribble path yet? */
    public boolean isDone() {
      return i >= numPoints;

    /** Move on to the next segment of the path */
    public void next() {
      if (Double.isNaN(points[i]))
        i += 3;
        i += 2;

     * Get the coordinates of the current moveto or lineto as floats
    public int currentSegment(float[] coords) {
      int retval;
      if (Double.isNaN(points[i])) { // If its a moveto
        coords[0] = (float) points[i + 1];
        coords[1] = (float) points[i + 2];
        retval = SEG_MOVETO;
      } else {
        coords[0] = (float) points[i];
        coords[1] = (float) points[i + 1];
        retval = SEG_LINETO;

      // If a transform was specified, use it on the coordinates
      if (transform != null)
        transform.transform(coords, 0, coords, 0, 1);

      return retval;

     * Get the coordinates of the current moveto or lineto as doubles
    public int currentSegment(double[] coords) {
      int retval;
      if (Double.isNaN(points[i])) {
        coords[0] = points[i + 1];
        coords[1] = points[i + 2];
        retval = SEG_MOVETO;
      } else {
        coords[0] = points[i];
        coords[1] = points[i + 1];
        retval = SEG_LINETO;
      if (transform != null)
        transform.transform(coords, 0, coords, 0, 1);
      return retval;

  //====== The following methods implement the Transferable interface =====

  // This is the custom DataFlavor for Scribble objects
  public static DataFlavor scribbleDataFlavor = new DataFlavor(
      Scribble.class, "Scribble");

  // This is a list of the flavors we know how to work with
  public static DataFlavor[] supportedFlavors = { scribbleDataFlavor,
      DataFlavor.stringFlavor };

  /** Return the data formats or "flavors" we know how to transfer */
  public DataFlavor[] getTransferDataFlavors() {
    return (DataFlavor[]) supportedFlavors.clone();

  /** Check whether we support a given flavor */
  public boolean isDataFlavorSupported(DataFlavor flavor) {
    return (flavor.equals(scribbleDataFlavor) || flavor

   * Return the scribble data in the requested format, or throw an exception
   * if we don't support the requested format
  public Object getTransferData(DataFlavor flavor)
      throws UnsupportedFlavorException {
    if (flavor.equals(scribbleDataFlavor)) {
      return this;
    } else if (flavor.equals(DataFlavor.stringFlavor)) {
      return toString();
    } else
      throw new UnsupportedFlavorException(flavor);

Related examples in the same category

1.Tree: Drag and DropTree: Drag and Drop
2.Adding Image-Dragging Behavior
3.Dropper - show File Drop Target from Drag-n-DropDropper - show File Drop Target from Drag-n-Drop
4.Demonstrate various aspects of Swing data transferDemonstrate various aspects of Swing data transfer
5.File Tree Drop TargetFile Tree Drop Target
6.File Tree Drag SourceFile Tree Drag Source
7.Editor Drop Target 4Editor Drop Target 4
8.Drag Drop Tree ExampleDrag Drop Tree Example
9.JLabel Drag Source JLabel Drag Source
10.Panel Drop TargetPanel Drop Target
11.Editor Drop Target 3Editor Drop Target 3
12.Editor Drop TargetEditor Drop Target
13.Editor Drop Target 2Editor Drop Target 2
14.JTextArea subclass allows TransferableColor objects toJTextArea subclass allows TransferableColor objects to
15.Transferable Color
16.Color Drag Source
17.A sample component for dragging and dropping a collection of files into a tree.A sample component for dragging and dropping a collection of files into a tree.
18.Test of the DragGesture classes and JList to see if weTest of the DragGesture classes and JList to see if we
19.A TransferHandler and JTextArea that will accept any drop at allA TransferHandler and JTextArea that will accept any drop at all
20.Drag and drop: TextAreaDrag and drop: TextArea
21.Drag capabilities: JListDrag capabilities: JList
22.A simple drop tester application for JDK 1.4 Swing componentsA simple drop tester application for JDK 1.4 Swing components
23.Drag and Drop:JList and ListDrag and Drop:JList and List
24.DnD (drag and drop)JTree code DnD (drag and drop)JTree code
25.Drop: TextAreaDrop: TextArea
26.Drag and drop: TextArea 2Drag and drop: TextArea 2
27.Label DnD (Drag and Drop) Label DnD (Drag and Drop)
28.LabelDnD2 allows dropping color onto the foreground of the JLabelLabelDnD2 allows dropping color onto the foreground of the JLabel
29.Drag List Demo Drag List Demo
30.Drag Picture Demo
31.Extended DnD (Drag and Drop) DemoExtended DnD (Drag and Drop) Demo
32.Drag Color DemoDrag Color Demo
33.Drag File DemoDrag File Demo
34.Drag Picture Demo 2
35.Drag Color TextField DemoDrag Color TextField Demo
36.BasicDnD (Drag and Drop)BasicDnD (Drag and Drop)
37.Drag and drop icons: use an icon property.
38.Implement drag & drop functionality in your application
39.Detect a drag initiating gesture in your application
40.Making a Component Draggable
41.Getting and Setting Text on the System Clipboard
42.Use drag and drop to reorder a list
43.Create a drag source a drop target and a transferable object.
44.implements DragGestureListener, Transferable
45.Comma separated text will be inserted into two or more rows.
46.TransferHandler subclass wraps another TransferHandler and delegates most of its operations to the wrapped handler
47.Setting text drag in a JTextArea
48.Built-in drag and drop support: utilize a TransferHandler class
49.This program demonstrates drag and drop in an image list.
50.This program demonstrates the basic Swing support for drag and drop.