MyPasswordIsLasercats
MyPasswordIsLasercats

Reputation: 1630

Unexpected actionPerformed after a drag

I experience unexpected behavior when I combine ActionListeners with Drag'n'Drop.

Run the example below and try the following:

  1. Drag Button A somewhere and release the mousebutton
  2. Do not move the mouse over button A again
  3. Click Button B
  4. On System.out you will see that actionPerformed will be called for both buttons A and B

I don't want to call actionPerformed for button A. What am I doing wrong?

Here is the full example:

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.dnd.*;
import java.awt.event.*;
import java.io.IOException;
import javax.swing.*;

public class DragDropTest{

private static Color slightlyRed = new Color(255, 220, 220);
private static Color slightlyGreen = new Color(220, 255, 220);

private static class DragDropButton extends JButton implements DragSourceListener, DragGestureListener, Transferable, ActionListener{
    private DragSource dragSource;

    public DragDropButton(String string) {
        super(string);
        addActionListener(this);
        dragSource = new DragSource();
        dragSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this);
    }

    // Interface DragSourceListener
    @Override public void dragEnter(DragSourceDragEvent dsde) {}
    @Override public void dragOver(DragSourceDragEvent dsde) {}
    @Override public void dropActionChanged(DragSourceDragEvent dsde) {}
    @Override public void dragExit(DragSourceEvent dse) {}
    @Override public void dragDropEnd(DragSourceDropEvent dsde) {
        System.out.println("Dragging of [" + getText() + "] ended");
    }
    // Interface DragGestureListener
    @Override public void dragGestureRecognized(DragGestureEvent dge) {
        dragSource.startDrag(dge, DragSource.DefaultMoveDrop, this, this);
    }
    // Interface Transferable
    @Override public DataFlavor[] getTransferDataFlavors() {return null;}
    @Override public boolean isDataFlavorSupported(DataFlavor flavor) {return false;}
    @Override public Object getTransferData(DataFlavor flavor)
            throws UnsupportedFlavorException, IOException {return null;}
    // Interface ActionListener
    @Override public void actionPerformed(ActionEvent e) {
        System.out.println("Action on [" + getText() + "]");
    }
}

private static class DropPanel extends JPanel implements DropTargetListener{

    public DropPanel(){
        setBorder(BorderFactory.createLineBorder(Color.BLACK));
        setPreferredSize(new Dimension(300, 300));
        setBackground(slightlyGreen);

        new DropTarget(this, this);
    }

    @Override public void dragEnter(DropTargetDragEvent dtde) {}
    @Override public void dragOver(DropTargetDragEvent dtde) {}
    @Override public void dropActionChanged(DropTargetDragEvent dtde) {}
    @Override public void dragExit(DropTargetEvent dte) {}
    @Override public void drop(DropTargetDropEvent dtde) {
        System.out.println("Drop complete");
        dtde.dropComplete(true);
    }

}



// Create Panels

private static JPanel leftSide(){
    JPanel leftPanel = new JPanel();
    leftPanel.setPreferredSize(new Dimension(300, 300));
    leftPanel.setBackground(slightlyRed);
    leftPanel.add(new DragDropButton("A"));
    leftPanel.add(new DragDropButton("B"));
    return leftPanel;
}
private static JPanel rightSide(){
    JPanel leftPanel = new DropPanel();
    return leftPanel;
}

// Create Window

private static JFrame boringFrame(String text){

    JFrame boringFrame = new JFrame(text);
    boringFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    boringFrame.getContentPane().setLayout(new FlowLayout(FlowLayout.CENTER));

    boringFrame.setVisible(true);
    return boringFrame;
}

public static void main(String[] args){

    JFrame boringFrame = boringFrame("boring");

    boringFrame.add(leftSide());
    boringFrame.add(rightSide());   // try to comment out this line!
    boringFrame.pack();


}

}

There are a few more problems in my project but I wont bother you with them since it seems that this is the main source of my problems, but here is another observation I made:

The problem disappears if there is no DropTargetListener (like my DropPanel). Try to comment out the following line:

boringFrame.add(rightSide());

Upvotes: 2

Views: 372

Answers (1)

kiheru
kiheru

Reputation: 6618

The issue is the conflict between the mouse release handling of the button clicking and drag handling. You can work around it by calling getModel().setArmed(false) in your dragGestureRecognized(). That tells the button not to fire the action the next time the pressed property is set to false.

The downside is that button clicks that are not meant as drags, but trigger drag anyway because the user moves the mouse slightly, won't result in a button click. On the other hand, for draggable buttons the user's intent is rather unclear in such a situation anyway.

Upvotes: 3

Related Questions