JDK-4874092 : dragEnter/dragExit methods of DragSourceListener fire without reaching target
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.2
  • Priority: P3
  • Status: Resolved
  • Resolution: Fixed
  • OS: windows_xp
  • CPU: x86
  • Submitted: 2003-06-04
  • Updated: 2003-07-25
  • Resolved: 2003-07-25
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
Other
5.0 tigerFixed
Related Reports
Relates :  
Relates :  
Relates :  
Relates :  
Relates :  
Description

Name: rmT116609			Date: 06/04/2003


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

FULL OS VERSION :
Windows XP Professional, Version 2002

A DESCRIPTION OF THE PROBLEM :
dragEnter and dragExit methods of DragSourceListener fire continuously as the dragging action occurs. They should fire only when the drop target is reached, but in 1.4.2-beta it happens even when the drop target is not designated.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Compile the submitted code. Launch the program (Test.main()). Start dragging action in the displayed window. The debugging output tracing the methods firing continuously will be shown in the console.

Running the same app against 1.4.1 shows dragEnter and dragExit methods firing only when the drag action ends (mouse released).



EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
In the given test case, the dragEnter and dragExit methods should fire only when the mouse is released.
ACTUAL -
The dragEnter and dragExit methods fire continuously, as the mouse drags across the window.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
// file Test.java

import java.awt.*;
import javax.swing.*;

public class Test
{
	private Controller controller;

	public Test()
	{
		controller = new Controller();
	}

	public static void main(String args[])
	{
		try
		{
			UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
			if (System.getProperty("os.name").startsWith("Windows"))
				UIManager.getDefaults().put("ScrollBar.track",
					new javax.swing.plaf.ColorUIResource (224, 224, 224));
			UIManager.put("ToolTip.font",new Font("SansSerif",Font.PLAIN,10));
		}
		catch (Exception exc) { System.out.println("Error loading L&F: " + exc); }
		new Test();
	}
}

// file Controller.java

import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import javax.swing.*;

public class Controller implements DragGestureListener, DragSourceListener, DragSourceMotionListener, Transferable
{
	private JFrame frame;
	private Container contentPane;
	private Rectangle windowRect;
	private Font plainFont, boldFont;
	private TransparentWindow cache;
	private DataFlavor[] dummyDataFlavors = { new DataFlavor(String.class, "") };

	public Controller()
	{
		frame = new JFrame();
		frame.addNotify();
		frame.pack();
		boldFont = new Font("SansSerif",Font.BOLD,18);
		contentPane = frame.getContentPane();
		contentPane.setLayout(null);
		frame.setSize(350, 250);
		frame.validate();
		windowRect = frame.getBounds();
		createViews();
		frame.setLocationRelativeTo(null);
		frame.setVisible(true);
		frame.doLayout();
		cache = new TransparentWindow();
	}

	public void createViews()
	{
/*
**		Drag source
*/
		DragSourceLabel dragSource = new DragSourceLabel("Drag from here...");
		dragSource.setFont(boldFont);
		dragSource.setSize(dragSource.getPreferredSize());
		dragSource.setLocation(20, 20);
		contentPane.add(dragSource);
		DragSource ds = DragSource.getDefaultDragSource();
		ds.addDragSourceMotionListener(this);
		DragGestureRecognizer dgr = ds.createDefaultDragGestureRecognizer(dragSource, DnDConstants.ACTION_MOVE, this);
		dragSource.setDragGestureRecognizer(dgr);
/*
**		Drag target
*/
		JLabel dragTarget = new JLabel("...to here");
		dragTarget.setFont(boldFont);
		dragTarget.setSize(dragTarget.getPreferredSize());
		dragTarget.setLocation(contentPane.getWidth() - dragTarget.getWidth() - 20, contentPane.getHeight() - dragTarget.getHeight() - 20);
		contentPane.add(dragTarget);
	}

	public void dragGestureRecognized(DragGestureEvent dge)
	{
		System.out.println("dragGestureRecognized");
		Point location = dge.getDragOrigin();
		SwingUtilities.convertPointToScreen(location, dge.getComponent());
		cache.setLocation(location);
		cache.setVisible(true);
		dge.startDrag(null, this, this);
	}

	public void dragDropEnd(DragSourceDropEvent dsde)
	{
		System.out.println("dragDropEnd");
		cache.setVisible(false);
	}

	public void dropActionChanged(DragSourceDragEvent dsde) {}
	public void dragEnter(DragSourceDragEvent dsde)
	{
		System.out.println("dragEnter");
	}

	public void dragExit(DragSourceEvent dse)
	{
		System.out.println("dragExit");
	}

	public void dragMouseMoved(DragSourceDragEvent dsde)
	{
		Point origin = dsde.getLocation();
		cache.setLocation(origin);
	}

	public void dragOver(DragSourceDragEvent dsde) {}
	public Object getTransferData(DataFlavor flavor) { return null; }
	public DataFlavor[] getTransferDataFlavors() { return (dummyDataFlavors); }
	public boolean isDataFlavorSupported(DataFlavor flavor) { return (true); }
}

// file DragSourceLabel.java

import javax.swing.*;
import java.util.*;
import java.awt.event.*;
import java.awt.dnd.*;

public class DragSourceLabel extends JLabel
{
	protected DragGestureListener dgl;
	protected DragGestureRecognizer dgr;

	public DragSourceLabel(String text)
	{
		super(text);
	}

	public void setDragGestureRecognizer(DragGestureRecognizer dgr)
	{
		this.dgr = dgr;
	}

	protected void processMouseEvent(MouseEvent e)
	{
		boolean suppressDND = false;
		switch (e.getID())
		{
			case MouseEvent.MOUSE_PRESSED:
				suppressDND = true;
				break;
			case MouseEvent.MOUSE_RELEASED:
				suppressDND = true;
				break;
		}
		if (suppressDND)
		{
			EventListener el[] = getListeners(DragGestureListener.class);
			for (int i = 0; i < el.length; i++)
				dgr.removeDragGestureListener((DragGestureListener) el[i]);
			super.processMouseEvent(e);
			for (int i = 0; i < el.length; i++)
			{
				try { dgr.addDragGestureListener((DragGestureListener) el[i]); }
				catch (TooManyListenersException exc) { System.out.println(exc); }
			}
		}
		else
			super.processMouseEvent(e);
	}
}

// file TransparentWindow.java

import java.awt.*;
import java.awt.image.*;
import javax.swing.*;

public class TransparentWindow extends JWindow
{
	private Container contentPane;
	private BufferedImage currentImage, baseImage;
	private Image tileImage[];
	private Rectangle baseRect, tile[];
	private Point origin, baseOrigin;
	private Robot r;
	private Font boldFont;
	private boolean drawBackgroundOnly;

	public TransparentWindow()
	{
		super(new Frame());
		tileImage = new Image[2];
		tileImage[0] = null;
		tileImage[1] = null;
		baseOrigin = new Point();
		pack();
		setSize(100, 100);
		boldFont = new Font("SansSerif",Font.BOLD,16);
		validate();
		try
		{
			r = new Robot();
		}
		catch (AWTException awe)
		{
			System.out.println("robot excepton occurred");
		}
	}

	protected void grabCurrentImage()
	{
		currentImage = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_RGB);
		Graphics g = currentImage.getGraphics();
		drawBackgroundOnly = true;
		paint(g);
		drawBackgroundOnly = false;
		g.dispose();
	}

	public void capture()
	{
		baseRect = getBounds();
		baseImage = r.createScreenCapture(baseRect);
	}

	public void setVisible(boolean flag)
	{
		if (flag)
		{
			capture();
			grabCurrentImage();
		}
		super.setVisible(flag);
	}

	public void setLocation(Point loc)
	{
/*
**		Calculate tiled rectangles
*/
		Rectangle oldFrame = getBounds();
		Rectangle newFrame = new Rectangle(loc.x, loc.y, oldFrame.width, oldFrame.height);
		baseRect = oldFrame.intersection(newFrame);
		tile = SwingUtilities.computeDifference(newFrame, oldFrame);
/*
**		Get base image from the captured ImageBuffer
*/
		if (baseRect.width > 0 && baseRect.height > 0)
			baseImage = currentImage.getSubimage(baseRect.x - oldFrame.x, baseRect.y - oldFrame.y, baseRect.width, baseRect.height);
		else
			baseImage = null;
/*
**		Capture tile images from the screen
*/
		tileImage[0] = null;
		tileImage[1] = null;
		for (int i = 0; i < tile.length; i++)
		{
			if (!tile[i].isEmpty())
				tileImage[i] = r.createScreenCapture(tile[i]);
		}
/*
**		Translate the rectangles to the origin of the new frame
*/
		baseRect.x -= newFrame.x;
		baseRect.y -= newFrame.y;
		for (int i = 0; i < tile.length; i++)
		{
			if (!tile[i].isEmpty())
			{
				tile[i].x -= newFrame.x;
				tile[i].y -= newFrame.y;
			}
		}
		super.setVisible(false);
		super.setLocation(loc);
		grabCurrentImage();
		super.setVisible(true);
	}

	public void paint(Graphics g)
	{
		super.paintComponents(g);
		if (baseImage != null)
			g.drawImage(baseImage, baseRect.x, baseRect.y, null);
		for (int i = 0; i < tile.length; i++)
		{
			if (tileImage[i] != null)
				g.drawImage(tileImage[i], tile[i].x, tile[i].y, null);
		}
		if (drawBackgroundOnly)
			return;
		g.setColor(Color.black);
		g.setFont(boldFont);
		g.drawString("Hello there", 5, 50);
		Rectangle rect = getBounds();
		g.setColor(Color.red);
		g.drawRect(0,0,rect.width - 1,rect.height - 1);
	}
}
---------- END SOURCE ----------

Release Regression From : 1.4.1_03
The above release value was the last known release where this 
bug was known to work. Since then there has been a regression.

(Review ID: 186668) 
======================================================================

Comments
CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: tiger FIXED IN: tiger INTEGRATED IN: tiger tiger-b13
24-08-2004

SUGGESTED FIX Name: agR10216 Date: 06/10/2003 Don't call dragExit() just before dragDropEnd() in SunDragSourceContextPeer.EventDispatcher.run() and remove the method SunDragSourceContextPeer.needsBogusExitBeforeDrop(). ###@###.### 2003-06-10 ======================================================================
10-06-2003

EVALUATION Name: agR10216 Date: 06/09/2003 Using JDK 1.4.1 build 21, and dragging over and dropping on the window with no associated DropTarget (as in the provided test case), the following methods of the DragSourceListener are invoked: - on Windows: dragExit dragDropEnd - on Solaris: dragExit dragExit ... dragExit dragDropEnd Using JDK 1.4.2 build 25: - on Windows and on Solaris: dragExit dragExit ... dragExit dragDropEnd This behavior change was introduced with the fix 4407521 (Cursor not modified during Drag and Drop). But as from the fix 4819437 (DragSourceListener.dragExit() is called unexpectedly) we have the following behavior: - on Windows: dragExit dragDropEnd - on Solaris: dragDropEnd According to the javadoc for DragSourceListener.dragExit(), this method must not be called if there hasn't been corresponding call of dragEnter(). Therefore Windows implementation must be revised, so that dragExit isn't called in this scenario. ###@###.### 2003-06-09 ======================================================================
09-06-2003