JDK-7082955 : DnD DragSite receives unexpected drop action after keyboard drop action change
  • Type: Bug
  • Component: client-libs
  • Sub-Component: java.awt
  • Affected Version: 7
  • Priority: P4
  • Status: Closed
  • Resolution: Cannot Reproduce
  • OS: generic
  • CPU: generic
  • Submitted: 2011-08-24
  • Updated: 2014-04-01
  • Resolved: 2014-04-01
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 8
8-poolResolved
Related Reports
Relates :  
Description
SYNOPSIS
--------
DnD DragSite receives unexpected drop action after keyboard drop action change

OPERATING SYSTEM
----------------
Windows (tested with Windows XP SP3)
Linux (tested with RHEL 5)

FULL JDK VERSIONS
-----------------
Java 7 (tested with GA / b147)
Java 6 (tested with 1.6.0_26)

PROBLEM DESCRIPTION from LICENSEE
---------------------------------
CR 4869264 fixed DnD negotiation, but drop action is changed by keyboard operation, and the application cannot receive the expected drop action data

REPRODUCTION INSTRUCTIONS
-------------------------
1. Compile and run DnDButtonTest as follows:
   $ java DnDButtonTest abc xyz

2. Drag "abc" from Button1 and move mouse cursor to the "xyz" button.
   (Don't drop it. If you drop it, press "Put" button to restore the
   text.) You will see the following output:

=========================================================
DropSite: dragEnter: dropAction=MOVE, sourceActions=COPY_OR_MOVE
DragSite: dragEnter: userAction=MOVE, dropAction=MOVE, targetActions=MOVE, gestureModifiers=Button1
=========================================================

Note that both dropAction values are the same. The response order is DropSite, DragSite.

3. Press Ctrl key to change drop action from MOVE to COPY and keep it
   pressed, you will see the following output:

=========================================================
DragSite: dropActionChanged: userAction=COPY, dropAction=MOVE, targetActions=MOVE, gestureModifiers=Ctrl+Button1
DropSite: dropActionChanged: dropAction=COPY, sourceActions=COPY_OR_MOVE
=========================================================

Note that the dropAction values are NOT the same. The response order is DragSite, DropSite i.e. it seems DragSite's DragSourceDragEvent event was created before DropSite's DropTargetDragEvent event. Also, the dropAction value should be the same because DragSite can detect DropSite's drop action via dropActionChanged().

The expected result is:

=========================================================
DropSite: dropActionChanged: dropAction=COPY, sourceActions=COPY_OR_MOVE
DragSite: dropActionChanged: userAction=COPY, dropAction=COPY, targetActions=COPY, gestureModifiers=Ctrl+Button1
=========================================================

4. Release Ctrl key to change drop action from COPY to MOVE. You will
   see the following output:

=========================================================
DragSite: dropActionChanged: userAction=MOVE, dropAction=COPY, targetActions=COPY, gestureModifiers=Button1
DropSite: dropActionChanged: dropAction=MOVE, sourceActions=COPY_OR_MOVE
=========================================================

The expected result is:

=========================================================
DropSite: dropActionChanged: dropAction=MOVE, sourceActions=COPY_OR_MOVE
DragSite: dropActionChanged: userAction=MOVE, dropAction=MOVE, targetActions=MOVE, gestureModifiers=Button1
=========================================================

5. Move the mouse cursor to the TextField on "xyz". Mouse pointer says
   "drop action is rejected", you will see the following output:

=========================================================
DropSite: dragExit
DragSite: dragExit
=========================================================

6. Press Ctrl key and release Ctrl key, you will see the following
   output:

=========================================================
DragSite: dropActionChanged: userAction=COPY, dropAction=NONE, targetActions=NONE, gestureModifiers=Ctrl+Button1
DragSite: dropActionChanged: userAction=MOVE, dropAction=NONE, targetActions=NONE, gestureModifiers=Button1
=========================================================

It received the expected dropAction since dragExit had been called, as expected.

TESTCASE
--------
import java.io.*;
import java.util.*;
import java.awt.*;
import java.awt.dnd.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.beans.*;

public class DnDButtonTest extends Frame implements ActionListener, DragGestureListener, DragSourceListener, Transferable, DropTargetListener {
    private static DataFlavor flavors[] = {DataFlavor.stringFlavor};
    private Button btn = null;
    private TextField textf = null;
    private static Hashtable<Integer,String> dndAction = new Hashtable<Integer,String>(){{
            put(new Integer(DnDConstants.ACTION_COPY), new String("COPY"));
            put(new Integer(DnDConstants.ACTION_COPY_OR_MOVE), new String("COPY_OR_MOVE"));
            put(new Integer(DnDConstants.ACTION_LINK), new String("LINK"));
            put(new Integer(DnDConstants.ACTION_MOVE), new String("MOVE"));
            put(new Integer(DnDConstants.ACTION_NONE), new String("NONE"));
        }};

    public DnDButtonTest(String s){
        setTitle("DnDButtonTest");
        setLayout(new BorderLayout());
        btn = new Button(s);
        btn.setPreferredSize(new Dimension(300, 100));
        add(btn, BorderLayout.CENTER);
        Panel p = new Panel();
        p.setLayout(new FlowLayout());
        textf = new TextField(s, 15);
        p.add(textf);
        Button copyBtn = new Button("Put");
        copyBtn.addActionListener(this);
        p.add(copyBtn);
        add(p, BorderLayout.SOUTH);
        DragSource ds = DragSource.getDefaultDragSource();
        ds.createDefaultDragGestureRecognizer(btn, DnDConstants.ACTION_COPY_OR_MOVE, this);
        DropTarget dt = new DropTarget(btn, this);
        addWindowListener(new WindowAdapter() {
                              public void windowClosing(WindowEvent event) { System.exit(0);}
                          });
        pack();
        setLocationByPlatform(true);
        setVisible(true);
    }

    public void actionPerformed(ActionEvent ae) {
        btn.setLabel(textf.getText());
    }

    public void dragGestureRecognized(DragGestureEvent dge){
        dge.startDrag(null, this, this);
    }

    public void dragEnter(DragSourceDragEvent dsde){
        System.out.printf("DragSite: dragEnter: userAction=%s, dropAction=%s, targetActions=%s, gestureModifiers=%s%n",
                          dndAction.get(dsde.getUserAction()), dndAction.get(dsde.getDropAction()),
                          dndAction.get(dsde.getTargetActions()), KeyEvent.getKeyModifiersText(dsde.getGestureModifiers()));
    }
    public void dragOver(DragSourceDragEvent dsde){}
    public void dropActionChanged(DragSourceDragEvent dsde){
        System.out.printf("DragSite: dropActionChanged: userAction=%s, dropAction=%s, targetActions=%s, gestureModifiers=%s%n",
                          dndAction.get(dsde.getUserAction()), dndAction.get(dsde.getDropAction()),
                          dndAction.get(dsde.getTargetActions()), KeyEvent.getKeyModifiersText(dsde.getGestureModifiers()));
    }
    public void dragExit(DragSourceEvent dsde){
        System.out.println("DragSite: dragExit");
    }
    public void dragDropEnd(DragSourceDropEvent dsde){
        System.out.printf("DragSite: dragDropEnd: dropSuccess=%s, dropAction=%s%n",
                          dsde.getDropSuccess() ? "true" : "false", dndAction.get(dsde.getDropAction()));
    }

    public synchronized DataFlavor[] getTransferDataFlavors(){
        return flavors;
    }
    public boolean isDataFlavorSupported(DataFlavor flavor){
        return(flavor.equals(flavors[0]));
    }
    public synchronized Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        if (flavor.equals(flavors[0])) {
            System.out.printf("DragSite: getTransferData=[%s]%n", btn.getLabel());
            return(Object)btn.getLabel();
        }
        throw new UnsupportedFlavorException(flavor);
    }

    public void dragEnter(DropTargetDragEvent dtde) {
        int action = dtde.getDropAction();
        System.out.printf("DropSite: dragEnter: dropAction=%s, sourceActions=%s%n",
                          dndAction.get(action), dndAction.get(dtde.getSourceActions()));
        if (action == DnDConstants.ACTION_COPY || action == DnDConstants.ACTION_MOVE) {
            if (dtde.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                dtde.acceptDrag(action);
                return;
            }
        } else {
            dtde.rejectDrag();
            return;
        }
        dtde.rejectDrag();
    }
    public void dragOver(DropTargetDragEvent dtde) {
    }
    public void dropActionChanged(DropTargetDragEvent dtde) {
        System.out.printf("DropSite: dropActionChanged: dropAction=%s, sourceActions=%s%n",
                          dndAction.get(dtde.getDropAction()), dndAction.get(dtde.getSourceActions()));
    }
    public void dragExit(DropTargetEvent dte) {
        System.out.println("DropSite: dragExit");
    }
    public void drop(DropTargetDropEvent dtde) {
        int action = dtde.getDropAction();
        if (action == DnDConstants.ACTION_COPY) {
            dtde.acceptDrop(DnDConstants.ACTION_COPY);
        } else if (action == DnDConstants.ACTION_MOVE) {
            dtde.acceptDrop(DnDConstants.ACTION_MOVE);
        } else {
            dtde.dropComplete(false);
            return;
        }
        try {
            Transferable content = dtde.getTransferable();
            if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
                String s = (String)content.getTransferData(DataFlavor.stringFlavor);
                btn.setLabel(s);
                System.out.printf("DropSite: drop: getTransferData=[%s], dropAction=%s%n",
                                  s, dndAction.get(action));
                dtde.dropComplete(true);
            } else {
                dtde.dropComplete(false);
            }
        } catch (Exception e) {
            e.printStackTrace();
            dtde.dropComplete(false);
        }
    }

    public static void main(String args[]){
        for (String s : args) new DnDButtonTest(s);
    }
}

Comments
Seems like it was fixed already.
01-04-2014