JDK-4869264 : drag & drop action negotiation does not honor the targets supported actions
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 1.4.1,1.4.2,5.0,6
  • Priority: P2
  • Status: Resolved
  • Resolution: Fixed
  • OS:
    generic,windows_nt,windows_2000,windows_xp generic,windows_nt,windows_2000,windows_xp
  • CPU: generic,x86
  • Submitted: 2003-05-23
  • Updated: 2017-05-16
  • Resolved: 2006-04-15
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.
JDK 6
6 b81Fixed
Related Reports
Duplicate :  
Duplicate :  
Duplicate :  
Duplicate :  
Relates :  
Relates :  
Description
Name: jl125535			Date: 05/23/2003


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

and others - see Description

FULL OPERATING SYSTEM VERSION :
Microsoft Windows 2000 [Version 5.00.2195]

ADDITIONAL OPERATING SYSTEMS : linux (i386)



A DESCRIPTION OF THE PROBLEM :
When a drop target supports another action than the default
(which seems to be move), and indicates that by calling the
DropTargetDragEvents acceptDrag(action) method during
dragEnter(), dragOver() or  dropActionChanged(), that action
should be made the current action, as long as the source
supports it and the user has not explicitely selected
another operation by holding down modidiers.

This does not work:
if the target requests another (source supported) action
than the current one, the source recieves notifications with
action=0, even though no explicit user action is requested
by holding down modifiers.

alternate actions (i.e. ACTION_COPY or ACTION_LINK) only
work when the user explicitely holds down the respective
modifier key.

for some reason, this does work correctly with jre 1.3.0
on linux (build 1.3.0_04), but the bug appears with
all windows versions i tested [(build 1.3.0-C), (build
1.3.1_02-b02), (build 1.4.0-b92)] and the jre 1.4.0 on linux
(build 1.4.0-b92)...

This seems to be a problem on the DragSource side. If you
drag items from a native application (i.e. a file from
explorer when running on windows) over a java target that
only accepts ACTION_LINK for example, that target request is
correctly handed back to the source (as indicated by the
changing cursors...)

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
1. run the attached example DndBug
2. drag the "source" object over the various targets without
holding down any modifier keys and watch cursor feedback and
STDOUT
3. Drag some native applications objects (a file from
explorer when running windows) over the various targets of
the DndBug application without holding down any modifier
keys and watch cursor feedback and STDOUT


EXPECTED VERSUS ACTUAL BEHAVIOR :
Expected:
When dragging the "source" button over other buttons, the console should display lines such as the following:

DST: drag over: action=LR, src-act=MCLR, force accept: LR
SRC: drag over: target=LR, user=0, action=LR

Actual:
When dragging the "source" button over other buttons, the console displays lines such as the following:

DST: drag over: action=M, src-act=MCLR, force accept: LR
SRC: drag over: target=LR, user=M, action=0

This bug can be reproduced always.

---------- BEGIN SOURCE ----------
import java.awt.* ;
import java.awt.datatransfer.* ;
import java.awt.dnd.* ;
import java.awt.event.* ;
import javax.swing.* ;
import javax.swing.border.* ;

/**
 * Demonstration of the DnD action negotiation bug.
 *  Just compile and run DndBug.
 *  Try to drag the "source" to the various targets.
 *  Observe STDOUT and cursor feedback.
 *  Enjoy.
 * to see, how the negotiation should work:
 *  Run DndBug.main on Windows (tested on w2k)
 *  drag file from the Windows Explorer over the various targets of DndBug.
 *  Observe STDOUT and cursor feedback.
 *  Enjoy.
 * Creation date: (30.05.2002 21:40:11)
 * @author: Heiko Hellweg (###@###.###)
 */
class DndBug {

    class TargetLabel extends JLabel implements DropTargetListener {
	Border std = new EtchedBorder() ;
	Border high = new LineBorder(Color.blue, 2) ;
	int accepted = 0 ;

	public TargetLabel(int actions) {
	    super() ;
	    // setTransferHandler(null) ; // does not improve it...
	    accepted = actions ;
	    setBorder(std) ;
	    new DropTarget (this, accepted, this);
	}
	private void printActions(DropTargetDragEvent dtde) {
	    System.out.println("action="
	    	+ formatActions(dtde.getDropAction())
	    	+ ", src-act="
	    	+ formatActions(dtde.getSourceActions())
	    	+ ", force accept: "
	    	+ formatActions(accepted)
	    ) ;
	}
	public void dragEnter(DropTargetDragEvent dtde) {
		System.out.print("DST: drag enter: ") ;
		printActions(dtde) ;
		setBorder(high) ;
		//should set action to accepted, if it is supported
		// by the source and no user action is in effect
		dtde.acceptDrag(accepted) ;
	}
	public void dragOver(DropTargetDragEvent dtde) {
		System.out.print("DST: drag over: ") ;
		printActions(dtde) ;
		//should set action to accepted, if it is supported
		//by the source and no user action is in effect
		dtde.acceptDrag(accepted) ;
	}
	public void dropActionChanged(DropTargetDragEvent dtde) {
		System.out.print("DST: drag action changed: ") ;
		printActions(dtde) ;
		//should set action to accepted, if it is supported
		// by the source and no user action is in effect
		dtde.acceptDrag(accepted) ;
	}
	public void dragExit(DropTargetEvent dtde) {
		System.out.println("DST: drag exit") ;
		setBorder(std) ;
	}
	public void drop(DropTargetDropEvent dtde) {
		System.out.println("DST: drop, action="
			+ formatActions(dtde.getDropAction())
			+ ", rejecting") ;
		setBorder(std) ;
		dtde.rejectDrop() ;
	}
    }
		
    class SourceLabel extends JLabel implements DragGestureListener,
DragSourceListener {

	public static final int allActions = DnDConstants.ACTION_COPY_OR_MOVE
	    | DnDConstants.ACTION_REFERENCE
	    | DnDConstants.ACTION_LINK ;
		
	public SourceLabel() {
	    super() ;
	    // setTransferHandler(null) ; // does not improve it...
	    setBorder(new EtchedBorder()) ;
	    DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer( this,
allActions, this);
	}
		
	private void printActions(DragSourceDragEvent dsde) {
	    System.out.println("target="+formatActions(dsde.getTargetActions())
			       +", user="+formatActions(dsde.getUserAction())
			       +", action="+formatActions(dsde.getDropAction())
			       ) ;
	}
	public void dragGestureRecognized(DragGestureEvent dge) {
	    System.out.println("gesture reckognized.") ;
	    StringSelection sel = new StringSelection("i am on my way ...") ;
	    dge.getSourceAsDragGestureRecognizer().setSourceActions(allActions) ;
	    dge.startDrag(null, sel, this);
	}
	public void dragDropEnd(DragSourceDropEvent dsde) {
	    System.err.println("SRC: drag ended (success="
			       + dsde.getDropSuccess()
			       + ", action="
			       + formatActions(dsde.getDropAction()) + ")") ;
	}
	public void dropActionChanged(DragSourceDragEvent dsde) {
	    System.err.print("SRC: drop action changed: ") ;
	    printActions(dsde) ;
	}
	public void dragEnter(DragSourceDragEvent dsde) {
	    System.err.print("SRC: drag enter: ") ;
	    printActions(dsde) ;
	}
	public void dragOver(DragSourceDragEvent dsde) {
	    System.err.print("SRC: drag over: ") ;
	    printActions(dsde) ;
	}
	public void dragExit(DragSourceEvent dse) {
	    System.err.println("SRC: drag exit.") ;
	}
    }

 
    public void bug() {
	JFrame frame = new JFrame();
	frame.setTitle("DnD bug demo") ;
	frame.setSize(new Dimension(100, 200));
	frame.getContentPane().setLayout(new FlowLayout()) ;
	
	JLabel label = new SourceLabel() ;
	label.setText("source") ;
	frame.getContentPane().add(label) ;
	
	label = new TargetLabel(DnDConstants.ACTION_REFERENCE) ;
	label.setText("ref. target") ;
	frame.getContentPane().add(label) ;
	
	label = new TargetLabel(DnDConstants.ACTION_LINK) ;
	label.setText("link target") ;
	frame.getContentPane().add(label) ;
	
	label = new TargetLabel(DnDConstants.ACTION_COPY) ;
	label.setText("copy target") ;
	frame.getContentPane().add(label) ;
	
	label = new TargetLabel(DnDConstants.ACTION_MOVE) ;
	label.setText("move target") ;
	frame.getContentPane().add(label) ;
	
	frame.addWindowListener(new WindowAdapter() {
		public void windowClosing(WindowEvent e) {
		    System.exit(0);
		};
	    });
	frame.show();
	
	System.out.println("now start dragging the \"source\" over the targets") ;
    }

    public static String formatActions(int actions) {
	StringBuffer buf = new StringBuffer() ;
	if(actions == (actions | DnDConstants.ACTION_MOVE)) {
	    buf.append("M") ;
	}
	if(actions == (actions | DnDConstants.ACTION_COPY)) {
	    buf.append("C") ;
	}
	if(actions == (actions | DnDConstants.ACTION_LINK)) {
	    buf.append("L") ;
	}
	if(actions == (actions | DnDConstants.ACTION_REFERENCE)) {
	    buf.append("R") ;
	}
	if(buf.length() == 0) {
	    buf.append("0") ;
	}
	return buf.toString() ;
    }


    public static void main(String[] args) {
        // does not improve anything
        // System.setProperty("suppressSwingDropSupport", "true") ; 

        new DndBug().bug() ;
    }
}

---------- END SOURCE ----------
(Review ID: 147281) 
======================================================================

Comments
SUGGESTED FIX Please refer to the CCC request http://ccc.sfbay/4869264 for a detailed description of the problem and API change. CCC approved the API change. As regards the implementation, the implementation of DragSourceDragEvent.getDropAction() is changed according to its new spec as well as the implementation of DragSourceContext.updateCurrentCursor(). Also, now we call DragSourceContext.updateCurrentCursor() with the parameters corresponding to the new spec. Webrev: http://javaweb.sfbay/jcg/1.6.0-mustang/awt/4869264
03-04-2006

EVALUATION Problem During a DnD operation the drag source and the drop target send events to each other. One type of events that are delivered to the drag source is java.awt.dnd.DragSourceDragEvent. The DnD operation may end with one of the resulting drop actions: ACTION_MOVE, ACTION_COPY, ACTION_LINK, or ACTION_NONE (refer to java.awt.dnd.DnDConstants). Target drop action is one of DnDConstants that represents the drop action selected by the drop target. User drop action is one of DnDConstants that represents the intersection of the drop actions supported by the drag source and the drop action selected by the user. The user can select an action by pressing the modifier keys. The expected resulting drop action is the intersection of the user drop action and the target drop action (refer to DragSourceDragEvent.getDropAction()). When the DnD operation is in progress, the mouse cursor is updated according to the expected resulting drop action via the method java.awt.dnd.DragSourceContext.updateCurrentCursor(). The drop target can choose a target drop action that is not equal to the user drop action and perform the chosen action at the end of the DnD operation. Due to the current treatment of the expected resulting drop action, the mouse cursor is NoDrop during the DnD operation. This is incorrect as the visual feedback should really match the expected resulting drop action. So the cursor should be updated according to the target drop action; the user drop action should not influence the cursor. Native applications (such as WordPad, StarOffice) follow this rule. The issue is proved to be very important since it affects a recent Swing DnD enhancement: http://ccc.sfbay/6379813. The enhancement allows the Swing drop target to choose a drop action. However the cursor does not reflect the chosen action chosen by the drop target (as described above). Solution Update the cursor in accordance with the target drop action, do not take into account the user drop action. Also, change the method DragSourceDragEvent.getDropAction() so that it does not take into account the user drop action. Specify this new behavior (refer to the Specification section). Specification Modify the spec for the method java.awt.dnd.DragSourceDragEvent.getDropAction(): /** ! * This method returns the logical intersection of * the target drop action and the set of drop actions supported by * the drag source. * ! * @return the logical intersection of the target drop action and ! * the set of drop actions supported by the drag source. */ public int getDropAction() { Change the name of one argument of the method java.awt.dnd.DragSourceContext.updateCurrentCursor() and modify the spec of this method: /** * If the default drag cursor behavior is active, this method ! * sets the default drag cursor for the specified actions ! * supported by the drag source, the drop target action, ! * and status, otherwise this method does nothing. * ! * @param sourceAct the actions supported by the drag source ! * @param targetAct the drop target action ! * @param status one of the fields <code>DEFAULT</code>, ! * <code>ENTER</code>, <code>OVER</code>, ! * <code>CHANGED</code> */ ! protected synchronized void updateCurrentCursor(int sourceAct, int targetAct, int status) {
03-04-2006

EVALUATION Necessary API changes are to be introduced.
24-03-2006

CONVERTED DATA BugTraq+ Release Management Values COMMIT TO FIX: mustang
17-09-2004

EVALUATION Name: dsR10078 Date: 07/29/2003 The class comments for java.awt.dnd.DragSourceDragEvent define the exact algorithm to determine the user drop action given the keyboard modifiers state and set of drop actions supported by the drag source. The algorithm specifies that if the user doesn't select a drop action (no modifier keys are pressed), the "user drop action" depends only on the drop actions supported by the drag source and is defined as follows: the set of drop actions supported by the drag source is searched for "move", "copy", "link" (in this order) and the first found action is considered the drop action selected by the user. This leads to counter-intuitive behavior if the user doesn't select a drop action and the drop target selects the drop action different from the "user drop action" as determined by the algorithm above. In this case the resulting drop action is ACTION_NONE, since the "user drop action" and the "target drop action" are different. A similar problem is described in the 4710682. It would more appropriate if the algorithm was defined to assume that if the user doesn't select a drop action, the "user drop action" is equal to the "target drop action". ###@###.### 2003-07-29 ====================================================================== Target mustang release. ###@###.### 2003-07-30 Implementation of this RFE also fixes the following bugs: 4710682 Drag from Java to Wordpad doesn't work correctly 5043688 DropTargetDragEvent.acceptDrag(int) doesn't work ###@###.### 2004-08-17
17-08-2004

SUGGESTED FIX Name: dsR10078 Date: 07/29/2003 1.Modify the definition of the "user drop action" in the javadoc for java.awt.dnd.DragSourceDragEvent as follows: --- DragSourceDragEvent.java Tue Jul 29 11:02:21 2003 *************** *** 39,51 **** * <code>DnDConstants.ACTION_NONE</code> if this drop action is not supported * by the drag source. * <p> ! * If the user doesn't select a drop action, the set of ! * <code>DnDConstants</code> that represents the set of drop actions supported ! * by the drag source is searched for <code>DnDConstants.ACTION_MOVE</code>, ! * then for <code>DnDConstants.ACTION_COPY</code>, then for ! * <code>DnDConstants.ACTION_LINK</code> and the <i>user drop action</i> is the ! * first constant found. If no constant is found the <i>user drop action</i> ! * is <code>DnDConstants.ACTION_NONE</code>. * * @version %I%, %G% * @since 1.2 --- 39,46 ---- * <code>DnDConstants.ACTION_NONE</code> if this drop action is not supported * by the drag source. * <p> ! * If the user doesn't select a drop action, the user drop action is equal to ! * the target drop action. * * @version %I%, %G% * @since 1.2 2.Update the implementation accordingly. 3.The class comments for java.awt.dnd.DropTargetDragEvent contains the same definition of the "user drop action" as in java.awt.dnd.DragSourceDragEvent. This is not appropriate as the drop action selected by the user is determined by the drag source and communicated to the drop target via the drag-and-drop protocol. So the drop target doesn't determine the user drop action itself, just receives it from the drag source. The drag source can reside in a native application that implements its own algorithm to determine the "user drop action" (e.g. always return ACTION_LINK) and on the drop target side the Java DnD subsystem should just report the action provided by the drag source, and the Java DnD subsystem is currently implemented in this way. So the definition of the "user drop action" in the class comments for java.awt.dnd.DropTargetDragEvent is inappropriate and should be modified as follows: --- DropTargetDragEvent.java Tue Jul 29 11:08:41 2003 *************** *** 26,52 **** * that represents the set of drop actions supported by the drag source for * this drag operation. * <p> ! * <i>User drop action</i> depends on the drop actions supported by the drag ! * source and the drop action selected by the user. The user can select a drop ! * action by pressing modifier keys during the drag operation: ! * <pre> ! * Ctrl + Shift -> ACTION_LINK ! * Ctrl -> ACTION_COPY ! * Shift -> ACTION_MOVE ! * </pre> ! * If the user selects a drop action, the <i>user drop action</i> is one of ! * <code>DnDConstants</code> that represents the selected drop action if this ! * drop action is supported by the drag source or ! * <code>DnDConstants.ACTION_NONE</code> if this drop action is not supported ! * by the drag source. ! * <p> ! * If the user doesn't select a drop action, the set of ! * <code>DnDConstants</code> that represents the set of drop actions supported ! * by the drag source is searched for <code>DnDConstants.ACTION_MOVE</code>, ! * then for <code>DnDConstants.ACTION_COPY</code>, then for ! * <code>DnDConstants.ACTION_LINK</code> and the <i>user drop action</i> is the ! * first constant found. If no constant is found the <i>user drop action</i> ! * is <code>DnDConstants.ACTION_NONE</code>. * * @version %I%, %G% * @since 1.2 --- 26,33 ---- * that represents the set of drop actions supported by the drag source for * this drag operation. * <p> ! * <i>User drop action</i> is one of <code>DnDConstants</code> ! * that represents the user drop action reported by the drag source. * * @version %I%, %G% * @since 1.2 ###@###.### 2003-07-29 ======================================================================
29-07-2003