JDK-4950103 : InvalidDnDOperationException when Dragging/Dropping in the same JTable
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.2
  • Priority: P4
  • Status: Closed
  • Resolution: Duplicate
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2003-11-06
  • Updated: 2003-11-10
  • Resolved: 2003-11-10
Related Reports
Duplicate :  
Relates :  
Description

Name: rmT116609			Date: 11/06/2003


FULL PRODUCT VERSION :
java version "1.4.2_01"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.2_01-b06)
Java HotSpot(TM) Client VM (build 1.4.2_01-b06, mixed mode)

FULL OS VERSION :
Microsoft Windows XP [Version 5.1.2600]

A DESCRIPTION OF THE PROBLEM :
When trying to move rows around in a JTable (by dragging and ropping them).  I get an InvalidDnDOperationException with the message "Drag and drop in progress".

This only happens if you try to initiate a drag with the exact row that you just dropped, without changing the selection.  The drag and drop will be successful if you try other rows, or you change the selection.

I am pretty sure I am accepting and completing the drop correctly in the drop method of the DropTargetListener.

The exact stack trace is below.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1) Run RowDraggingTable
2) Click & Drag the first row and drop it on the second row (for example)... the rows should swap
3) Click & Drag the new second row anywhere... the exception should occur

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The newly dropped row should be able to be dragged like any other row
ACTUAL -
I get the InvalidDnDOperationException when I try to initiate a drag with the newly dropped row

ERROR MESSAGES/STACK TRACES THAT OCCUR :
java.awt.dnd.InvalidDnDOperationException: Drag and drop in progress
	at sun.awt.dnd.SunDragSourceContextPeer.setDragDropInProgress(SunDragSourceContextPeer.java:328)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:285)
	at java.awt.dnd.DragSource.startDrag(DragSource.java:402)
	at java.awt.dnd.DragGestureEvent.startDrag(DragGestureEvent.java:201)
	at RowDraggingTable.dragGestureRecognized(RowDraggingTable.java:83)
	at java.awt.dnd.DragGestureRecognizer.fireDragGestureRecognized(DragGestureRecognizer.java:339)
	at sun.awt.windows.WMouseDragGestureRecognizer.mouseDragged(WMouseDragGestureRecognizer.java:205)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:262)
	at java.awt.AWTEventMulticaster.mouseDragged(AWTEventMulticaster.java:261)
	at java.awt.Component.processMouseMotionEvent(Component.java:5148)
	at javax.swing.JComponent.processMouseMotionEvent(JComponent.java:2779)
	at java.awt.Component.processEvent(Component.java:4901)
	at java.awt.Container.processEvent(Container.java:1569)
	at java.awt.Component.dispatchEventImpl(Component.java:3615)
	at java.awt.Container.dispatchEventImpl(Container.java:1627)
	at java.awt.Component.dispatchEvent(Component.java:3477)
	at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:3483)
	at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3215)
	at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3128)
	at java.awt.Container.dispatchEventImpl(Container.java:1613)
	at java.awt.Window.dispatchEventImpl(Window.java:1606)
	at java.awt.Component.dispatchEvent(Component.java:3477)
	at java.awt.EventQueue.dispatchEvent(EventQueue.java:456)
	at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:201)
	at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:151)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:145)
	at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:137)
	at java.awt.EventDispatchThread.run(EventDispatchThread.java:100)

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import javax.swing.table.TableModel;
import javax.swing.table.TableColumnModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.*;
import java.awt.dnd.*;
import java.awt.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.util.Vector;
import java.util.HashSet;
import java.util.Set;
import java.util.Iterator;
import java.io.IOException;


public class RowDraggingTable extends JTable implements Autoscroll, DropTargetListener, DragGestureListener {
	private static final int AUTOSCROLL_MARGIN = 25;
	protected Insets autoscrollInsets = new Insets(0, 0, 0, 0);

	private Set listeners = new HashSet();

	public RowDraggingTable() {
		initdrag();
	}

	public RowDraggingTable(int numRows, int numColumns) {
		super(numRows, numColumns);
		initdrag();
	}

	public RowDraggingTable(Object[][] rowData, Object[] columnNames) {
		super(rowData, columnNames);
		initdrag();
	}

	public RowDraggingTable(TableModel dm) {
		super(dm);
		initdrag();
	}

	public RowDraggingTable(TableModel dm, TableColumnModel cm) {
		super(dm, cm);
		initdrag();
	}

	public RowDraggingTable(TableModel dm, TableColumnModel cm, ListSelectionModel sm) {
		super(dm, cm, sm);
		initdrag();
	}

	public RowDraggingTable(Vector rowData, Vector columnNames) {
		super(rowData, columnNames);
		initdrag();
	}

	private void initdrag() {
		setDragEnabled(true);
		DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
		setDropTarget(new DropTarget(this, DnDConstants.ACTION_MOVE, this));
	}

	/** Add a listener to tell when a row is dragged to a new spot */
	public void addRowDragListener(RowDragListener l) {
		listeners.add(l);
	}

	/** Remove the row drag listener */
	public void removeRowDragListener(RowDragListener l) {
		listeners.remove(l);
	}

	public void dragGestureRecognized(DragGestureEvent e) {
		if (getDragEnabled() == true) {
			int row = getSelectedRow();

			if (row > -1) {
				if (isEditing() == true) {
					getCellEditor().stopCellEditing();
				}

				try {
					e.startDrag(DragSource.DefaultMoveDrop, new IntegerTransfer(row));
				} catch (InvalidDnDOperationException dnd) {
					for (Iterator it = listeners.iterator(); it.hasNext();) {
						RowDragListener l = (RowDragListener) it.next();
						l.dropError(this, dnd);
					}
				}
			}
		}
	}


	public void autoscroll(Point location) {
		int top = 0;
		int left = 0;
		int bottom = 0;
		int right = 0;

		Dimension size = getSize();
		Rectangle rect = getVisibleRect();
		int bottomEdge = rect.y + rect.height;
		int rightEdge = rect.x + rect.width;

		if (((location.y - rect.y) <= AUTOSCROLL_MARGIN) && (rect.y > 0)) {
			top = AUTOSCROLL_MARGIN;
		}

		if (((location.x - rect.x) <= AUTOSCROLL_MARGIN) && (rect.x > 0)) {
			left = AUTOSCROLL_MARGIN;
		}

		if (((bottomEdge - location.y) <= AUTOSCROLL_MARGIN) && (bottomEdge < size.height)) {
			bottom = AUTOSCROLL_MARGIN;
		}

		if (((rightEdge - location.x) <= AUTOSCROLL_MARGIN) && (rightEdge < size.width)) {
			right = AUTOSCROLL_MARGIN;
		}

		rect.x += right - left;
		rect.y += bottom - top;

		scrollRectToVisible(rect);
	}


	public Insets getAutoscrollInsets() {
		Dimension size = getSize();
		Rectangle rect = getVisibleRect();
		autoscrollInsets.top = rect.y + AUTOSCROLL_MARGIN;
		autoscrollInsets.left = rect.x + AUTOSCROLL_MARGIN;
		autoscrollInsets.bottom = size.height - (rect.y + rect.height) + AUTOSCROLL_MARGIN;
		autoscrollInsets.right = size.width - (rect.x + rect.width) + AUTOSCROLL_MARGIN;
		return autoscrollInsets;
	}


	public void dragEnter(DropTargetDragEvent dtde) {
		if (dtde.getDropAction() != DnDConstants.ACTION_MOVE)
			dtde.acceptDrag(DnDConstants.ACTION_MOVE);

	};

	public void dragExit(DropTargetEvent dte) {
	}

	public void dragOver(DropTargetDragEvent dtde) {
		if (dtde.getDropAction() != DnDConstants.ACTION_MOVE)
			dtde.acceptDrag(DnDConstants.ACTION_MOVE);
	}

	public void drop(DropTargetDropEvent dtde) {
		DataFlavor df = IntegerTransfer.flavor;
		if (dtde.isDataFlavorSupported(df)) {
			dtde.acceptDrop(dtde.getDropAction());
			Transferable transferable = dtde.getTransferable();

			try {
				int source_row = ((Integer) transferable.getTransferData(df)).intValue();
				int dest_row = rowAtPoint(dtde.getLocation());

				//notify...
				for (Iterator it = listeners.iterator(); it.hasNext();) {
					RowDragListener l = (RowDragListener) it.next();
					l.rowDragged(this, source_row, dest_row);
				}

				dtde.dropComplete(true);
			} catch (Exception e) {
				for (Iterator it = listeners.iterator(); it.hasNext();) {
					RowDragListener l = (RowDragListener) it.next();
					l.dropError(this, e);
				}
				dtde.dropComplete(false);
			}
		} else {
			dtde.rejectDrop();
		}
	}

	public void dropActionChanged(DropTargetDragEvent dtde) {
		if (dtde.getDropAction() != DnDConstants.ACTION_MOVE)
			dtde.acceptDrag(DnDConstants.ACTION_MOVE);
	};


	private static class IntegerTransfer implements Transferable {
		static DataFlavor flavor = new DataFlavor(Integer.class, "Integer");
		private Integer row = null;

		public IntegerTransfer(int row) {
			this.row = new Integer(row);
		}

		public DataFlavor[] getTransferDataFlavors() {
			return new DataFlavor[]{flavor};
		}

		public boolean isDataFlavorSupported(DataFlavor flavor) {
			return this.flavor.equals(flavor);
		}

		public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
			if (!isDataFlavorSupported(flavor)) throw new UnsupportedFlavorException(flavor);

			return row;
		}
	}

	/** Use to listen to drag events from this RowDraggingTable */
	public static interface RowDragListener {
		/** Called when there is some sort of error while dragging */
		void dropError(RowDraggingTable source, Exception e);

		/** Called when a drop is complete... it is up to the listener
		 * to modify the model to reflect any needed changes */
		void rowDragged(RowDraggingTable source, int source_row, int dest_row);
	}


	public static final void main(String[] args) {
		JFrame testframe = new JFrame("Test Window");
		testframe.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		testframe.getContentPane().setLayout(new BorderLayout(0, 0));

		DefaultTableModel model = new DefaultTableModel(new Object[] {"Col 1", "Col 2"}, 0);
		for (int i = 0; i < 100; i++) {
			model.addRow(new Object[] {new Integer(i), "Row Data " + i});
		}

		RowDraggingTable rdt = new RowDraggingTable(model);
		rdt.addRowDragListener(new RowDragListener() {
			public void dropError(RowDraggingTable source, Exception e) {
				e.printStackTrace();
			}

			public void rowDragged(RowDraggingTable source, int source_row, int dest_row) {
				DefaultTableModel model = (DefaultTableModel) source.getModel();
				model.moveRow(source_row, source_row, dest_row);
				source.getSelectionModel().setSelectionInterval(dest_row, dest_row);
			}
		});

		testframe.getContentPane().add(new JScrollPane(rdt), BorderLayout.CENTER);

		testframe.pack();
		testframe.setVisible(true);
	}
}

---------- END SOURCE ----------

(Incident Review ID: 223420) 
======================================================================

Comments
EVALUATION Name: agR10216 Date: 11/10/2003 The test app mixes both Swing's DnD support and AWT's DnD support. The constructor of RowDraggingTable essentially does the folowing: - installs Swing's drag gesture recognizer and Swing's drop target (via call to super() - JTable's constructor), - then adds AWT's drag gesture recognizer and - replaces Swing's drop target by AWT's one. So the instance of RowDraggingTable has both Swing's and AWT's drag gesture recognizers. Actually InvalidDnDOperationException is thrown if a selected row is being dragged because after Swing's drag gesture recognizer has started a DnD operation, AWT's drag gesture recognizer tries to start it too and only one DnD operation is allowed at a time. A drag succeeds if a previously unselected row is dragged because Swing can't initiate a drag in such a case (see the bug 4521075: Drag gesture in JAVA different from Windows) and AWT's drag gesture recognizer successfully starts a DnD operation alone. Swing users are strongly recommended to use only Swing's DnD support with Swing components. The app should be rewritten so that it uses only Swing's DnD support. I'm closing this bug a as duplicate of 4290983: Drag and Drop Support for Swing Components. ###@###.### 2003-11-10 Diplicate of: 4290983 ======================================================================
10-11-2003