1

I try to get familar with drag and drop in java but all the tutorials I found were... (getting me angry)

All I want is to drag a "PublicUserLabel" from a JList (included in a selfmade JPanel called "UserPanel") and drop it in a selfmade class inerited from JTabbedPanel. It is very important to drag the object itself and not its stringrepresentation!!!

That is what I have so far: PublicUserLabel

public class PublicUserLabel extends JLabel implements DragSourceListener, DragGestureListener, Transferable
    {
        private DragSource ds;
        private PublicUser user;

        public PublicUserLabel(PublicUser user)
        {
            super(user.getName());
            this.user = user;
            ds = new DragSource();
            ds.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY_OR_MOVE, this);
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent e)
        {
            ds.startDrag(e, DragSource.DefaultCopyDrop, this, this);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException
        {
            if (flavor.equals(PublicUserFlavor.publicUserFlavor))
            {
                return this;//TODO ?
            }
            throw new UnsupportedFlavorException(flavor);
        }

        @Override
        public DataFlavor[] getTransferDataFlavors()
        {
            DataFlavor[] df = new DataFlavor[2];
            df[0] = DataFlavor.stringFlavor;
            df[1] = PublicUserFlavor.publicUserFlavor;
            return df;
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor)
        {
            return flavor.equals(PublicUserFlavor.publicUserFlavor);
        }

    //some more methods
    }

UserPanel:

public class UserPanel extends JPanel
{
    private JTextField search;
    private List<PublicUser> allUser;
    private JList<PublicUserLabel> list;
    private JScrollPane scrollPane;
    private DefaultListModel<PublicUserLabel> listModel;

    public UserPanel()
    {
        allUser = new LinkedList<PublicUser>();
        listModel = new DefaultListModel<PublicUserLabel>();
        list = new JList<PublicUserLabel>(listModel);
        list.setDragEnabled(true);

PublicUserFlavor:

public class PublicUserFlavor extends DataFlavor
{
    public static DataFlavor publicUserFlavor;

    static
    {
        publicUserFlavor = new DataFlavor(PublicUser.class, "PublicUser");
    }
}

TabPanel:

public class TabPanel extends JTabbedPane implements DropTargetListener
{
    public TabPanel()
    {
        setTabPlacement(JTabbedPane.BOTTOM);
        addNewTabComponent("bla");
        addNewTabComponent("blub");
        setDropTarget(new DropTarget(this, this));
    }

    @Override
    public void drop(DropTargetDropEvent e)
    {
        Transferable transferable = e.getTransferable();
        if (transferable.isDataFlavorSupported(PublicUserFlavor.publicUserFlavor))
        {
            e.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
            try
            {
                Object o = transferable.getTransferData(PublicUserFlavor.publicUserFlavor);
                System.out.println(o);
                if (o instanceof PublicUserLabel)
                {
                    PublicUserLabel l = (PublicUserLabel)o;
                    PublicUser u = l.getUser();
                    System.out.println(u);
                }
            }
            catch (UnsupportedFlavorException e1)
            {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            catch (IOException e1)
            {
                // TODO Auto-generated catch block
                e1.printStackTrace();
            }
            e.getDropTargetContext().dropComplete(true);
        }
    }

In the drop method are some syso's that shall be executed if a User is droped in the panel. But that is not the fact. Am I doing something completly wrong? Thank you for helping!

Andrew Thompson
  • 168,117
  • 40
  • 217
  • 433
Coding
  • 134
  • 3
  • 10
  • 1
    What was wrong with the online tutorials? – Zach Latta Mar 20 '13 at 10:26
  • _It is very important to drag the object itself and not its stringrepresentation_ 1) Why ? 2) Will be difficult if you start your drag from a `JList` as the `JList` will not even contain that `JLabel`, only a rendered version – Robin Mar 20 '13 at 10:27
  • It is important because a user has not only a name. A user also has a public rsa key and a uuid. I have to get at least the uuid and the key from the droped object. I stored a List with PublicUser containing all information I need so i do not have to use the JList for that. One example for a bad tutorial was the heading "Putting it All Together". There was absolutly not ALL together. Even an overwritten method's signature was missing. – Coding Mar 20 '13 at 10:39
  • 1
    @zachlatta The online tutorials are contradictive. Unless you know what you're looking it. There is, generally, two distinct d'n'd APIs in Swing. There is the low level API and there is the newer "component" API. Not many of the tutorials openly explain which one they are using and it makes what is already a difficult subject just more convoluted - IMHO – MadProgrammer Mar 20 '13 at 10:52
  • writing good tutorials is hard work - if that hard work hasn't the result you want, you might consider hiring a personal tutor ... – kleopatra Mar 20 '13 at 11:52

1 Answers1

5

The major issues I can see are you are mixing two different API's. You're using both the low level D'n'D API and the newer "component D'n'D" API.

Personally, the newer API annoys me, so I tend to avoid it.

I can't see why you need to transfer the label, when it would be better to transfer the underlying PublicUser object, but that's just me.

enter image description here

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridBagLayout;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DragGestureEvent;
import java.awt.dnd.DragGestureListener;
import java.awt.dnd.DragGestureRecognizer;
import java.awt.dnd.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.io.IOException;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;

public class TestDnD {

    public static void main(String[] args) {
        new TestDnD();
    }

    public TestDnD() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                try {
                    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
                } catch (ClassNotFoundException ex) {
                } catch (InstantiationException ex) {
                } catch (IllegalAccessException ex) {
                } catch (UnsupportedLookAndFeelException ex) {
                }

                JFrame frame = new JFrame("Test");
                frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                frame.setLayout(new BorderLayout());
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }

        });
    }

    public class TestPane extends JPanel {

        private JList list;

        public TestPane() {
            setLayout(new BorderLayout());
            list = new JList();
            DefaultListModel model = new DefaultListModel();
            model.addElement(new User("Shaun"));
            model.addElement(new User("Andy"));
            model.addElement(new User("Luke"));
            model.addElement(new User("Han"));
            model.addElement(new User("Liea"));
            model.addElement(new User("Yoda"));
            list.setModel(model);
            add(new JScrollPane(list), BorderLayout.WEST);

            DragGestureRecognizer dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                            list,
                            DnDConstants.ACTION_COPY_OR_MOVE,
                            new DragGestureHandler(list));

            JPanel panel = new JPanel(new GridBagLayout());
            add(panel);

            DropTarget dt = new DropTarget(
                            panel,
                            DnDConstants.ACTION_COPY_OR_MOVE,
                            new DropTargetHandler(panel),
                            true);

        }

    }

    public static class User {

        private String name;

        public User(String name) {
            this.name = name;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return name;
        }

    }

    public static class UserTransferable implements Transferable {

        public static final DataFlavor USER_DATA_FLAVOR = new DataFlavor(User.class, "User");
        private User user;

        public UserTransferable(User user) {
            this.user = user;
        }

        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{USER_DATA_FLAVOR};
        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return USER_DATA_FLAVOR.equals(flavor);
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            Object value = null;
            if (USER_DATA_FLAVOR.equals(flavor)) {
                value = user;
            } else {
                throw new UnsupportedFlavorException(flavor);
            }
            return value;
        }

    }

    protected class DragGestureHandler implements DragGestureListener {

        private JList list;

        public DragGestureHandler(JList list) {
            this.list = list;
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            Object selectedValue = list.getSelectedValue();
            if (selectedValue instanceof User) {
                User user = (User) selectedValue;
                Transferable t = new UserTransferable(user);
                DragSource ds = dge.getDragSource();
                ds.startDrag(
                                dge,
                                null,
                                t,
                                new DragSourceHandler());
            }

        }

    }

    protected class DragSourceHandler implements DragSourceListener {

        public void dragEnter(DragSourceDragEvent dsde) {
        }

        public void dragOver(DragSourceDragEvent dsde) {
        }

        public void dropActionChanged(DragSourceDragEvent dsde) {
        }

        public void dragExit(DragSourceEvent dse) {
        }

        public void dragDropEnd(DragSourceDropEvent dsde) {

            System.out.println("Drag ended...");

        }

    }

    protected class DropTargetHandler implements DropTargetListener {

        private JPanel panel;

        public DropTargetHandler(JPanel panel) {
            this.panel = panel;
        }

        public void dragEnter(DropTargetDragEvent dtde) {
            if (dtde.getTransferable().isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
                System.out.println("Accept...");
                dtde.acceptDrag(DnDConstants.ACTION_COPY_OR_MOVE);
            } else {
                System.out.println("Drag...");
                dtde.rejectDrag();
            }
        }

        public void dragOver(DropTargetDragEvent dtde) {
        }

        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        public void dragExit(DropTargetEvent dte) {
        }

        public void drop(DropTargetDropEvent dtde) {
            System.out.println("Dropped...");
            if (dtde.getTransferable().isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
                Transferable t = dtde.getTransferable();
                if (t.isDataFlavorSupported(UserTransferable.USER_DATA_FLAVOR)) {
                    try {
                        Object transferData = t.getTransferData(UserTransferable.USER_DATA_FLAVOR);
                        if (transferData instanceof User) {
                            User user = (User) transferData;
                            dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
                            panel.add(new JLabel(user.getName()));
                            panel.revalidate();
                            panel.repaint();
                        } else {
                            dtde.rejectDrop();
                        }
                    } catch (UnsupportedFlavorException ex) {
                        ex.printStackTrace();
                        dtde.rejectDrop();
                    } catch (IOException ex) {
                        ex.printStackTrace();
                        dtde.rejectDrop();
                    }
                } else {
                    dtde.rejectDrop();
                }
            }
        }

    }

}
MadProgrammer
  • 343,457
  • 22
  • 230
  • 366
  • _missing_ or _mixing_ - your little angel again ?-) – kleopatra Mar 20 '13 at 11:55
  • personally, I think the newer api is much easier to use - pity it's so hard to implement with components that don't have it by default – kleopatra Mar 20 '13 at 11:58
  • @kleopatra Huh? What newer API? – Andrew Thompson Mar 20 '13 at 12:03
  • @AndrewThompson the part that hooks in when setting setDragEnabled/setTransferHandler, that is the abstraction on top of the low-level (package .dnd) for some components like tree/table/list ... :-) – kleopatra Mar 20 '13 at 12:05
  • 1
    @kleopatra As you say, if its not built it in to the component, it's a pain. I've also found that its deliberately disable portions of the API in such away that its impossible for other developers to reintroduce them, drag cursor/image for instance. I've wanted to implement some of the ideas from [The Rabbit Hole](http://rabbit-hole.blogspot.com.au/) blog, but haven't found the time to sit down and do it – MadProgrammer Mar 20 '13 at 19:17
  • 1
    WOW! I didn't know that there are different APIs. You are right, I do not need to export the Label, only its content (was a fix for a problem with dnd). I watched your code executed it and inline most small classes in my project. I think it will work. But one question i have:DropTarget dt = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, new DropTargetHandler(), true); The object is never touched but if I delete it it will not work. why does a panel know the DropTarget? You helped me a very lot. It's amazing what you did. Maybe you shall do a tutorial? Thank you a lot! – Coding Mar 20 '13 at 20:45
  • 1
    `DropTarget` does a lot of internal registering to setup the component to allow it to respond to drop events (mouse events in particular). The reason I keep a reference to in this is so I can unregister it later if I want to (by removing the `DropTargetListener` – MadProgrammer Mar 20 '13 at 22:30
  • 1
    This is a few years old, but it was still the best tutorial I could find to implement this! Thanks – barna10 Jul 23 '17 at 04:49
  • So this Tutorial is very very old, but still the best thing I could find, thank you so much for it. – Snorik Feb 28 '22 at 21:53