Member Avatar for rancosster

I have a problem with creating drag and drop in Swing. I have written class that represents the toolbar item here is the code shortened code for it:

public class ToolbarItem extends javax.swing.JToggleButton implements IToolbarItem, Transferable,
            DragSourceListener, DragGestureListener {

        private ModelEquivalent _modelEquivalent;
        private DragSource _dragAndDropSource;
        private TransferHandler _itemTransferHandler;

        public ToolbarItem(ModelEquivalent modelEquivalent) {
            this._modelEquivalent = modelEquivalent;
            try {
                this.initItemSettings();
            } catch (java.io.IOException ex) {
                System.out.println("Could not initialize toolbar item " + _modelEquivalent.toString());
            }
        }

        private void initItemSettings() throws java.io.IOException {
            initDragAndDropGestures();
        }

        private void initDragAndDropGestures() {
            this._itemTransferHandler = new TransferHandler() {
                @Override
                public Transferable createTransferable(JComponent c) {
                    return new ToolbarItem(_modelEquivalent);
                }
            };
            this.setTransferHandler(_itemTransferHandler);
            this._dragAndDropSource = new DragSource();
            _dragAndDropSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this);
        }

        @Override
        public ModelEquivalent getModelEquivalent() {
            return _modelEquivalent;
        }
        @Override
        public DataFlavor[] getTransferDataFlavors() {
            return new DataFlavor[]{new DataFlavor(ToolbarItem.class, _modelEquivalent.toString())};

        }

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            return true;

        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            return this;

        }

        @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) {
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            if (!this.isSelected()) {
                this.setBorder(null);
                _dragAndDropSource.startDrag(dge, DragSource.DefaultMoveDrop, new OnBoardItem(_modelEquivalent), this);
            }
        }
    }

The thing is when the drag gesture is recognized I create the object of other class which is represented by this class:

public class OnBoardItem extends JLabel implements Transferable, DragSourceListener, DragGestureListener {

    private static int _ID;
    private DragSource _dragAndDropSource;
    private TransferHandler _itemTransferHandler;
    private ModelEquivalent _modelEquivalent;

    public OnBoardItem(ModelEquivalent modelEquivalent) {
            OnBoardItem._ID++;
            this._modelEquivalent = modelEquivalent;
            initDragAndDropGestures();

    }

    private void initDragAndDropGestures() {
        this._itemTransferHandler = new TransferHandler() {
            @Override
            public Transferable createTransferable(JComponent c) {
                return OnBoardItem.this;
            }
        };
        this.setTransferHandler(_itemTransferHandler);
        this._dragAndDropSource = new DragSource();
        _dragAndDropSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{new DataFlavor(OnBoardItem.class, _modelEquivalent.toString())};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return true;
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return this;
    }

    @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) {
    }

    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {
        _dragAndDropSource.startDrag(dge, DragSource.DefaultMoveDrop, this, this);
    }

The last class which is the board class where I place dropped items:

public class DroppablePanel extends javax.swing.JPanel implements java.awt.dnd.DropTargetListener {

    private java.util.ArrayList<OnBoardItem> _items;
    private java.awt.dnd.DropTarget _target;
    private static int _itemsCounter = 0;

    public DroppablePanel() {
        _items = new ArrayList<>();
        _target = new DropTarget(this, this);
        setTransferHandler(new ToolsItemHandler());
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (isOpaque()) {
            g.setColor(getBackground());
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    @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) {
        try {
            Point location = dtde.getLocation();
            Transferable transferred = dtde.getTransferable();
            DataFlavor[] transferredData = transferred.getTransferDataFlavors();
            if (getTransferHandler().canImport(this, transferredData)) {
                ((ToolsItemHandler) getTransferHandler()).importData(this, (OnBoardItem) transferred.getTransferData(transferredData[0]), location);
                repaint();
            } else {
            }
        } catch (UnsupportedFlavorException | IOException ex) {
        } finally {
            dtde.dropComplete(true);
        }

    }

    class ToolsItemHandler extends TransferHandler {

        @Override
        public boolean canImport(JComponent c, DataFlavor[] f) {
            DataFlavor temp = new DataFlavor(OnBoardItem.class, "OnBoardItem");
            for (DataFlavor d : f) {
                if (d.equals(temp)) {
                    return true;
                }
            }
            return false;
        }

        public boolean importData(JComponent comp, Transferable t, Point p) {
            try {
                OnBoardItem temporaryOnboardItem = (OnBoardItem) t.getTransferData(new DataFlavor(ToolbarItem.class, "OnBoardItem"));
                System.out.println(getTransferHandler().getSourceActions(comp));
                //OnBoardItem temp = new OnBoardItem(temporaryOnboardItem.getModelEquivalent());
                if (_items.add(temporaryOnboardItem)) {
                    _itemsCounter++;
                    System.out.println(_items.size());
                    add(temporaryOnboardItem);
                    temporaryOnboardItem.setBounds((int) p.getX(), (int) p.getY(), 43, 43);
                    System.out.println("X: " + p.getX() + " Y: " + p.getY());
                }
                revalidate();
                repaint();
            } catch (UnsupportedFlavorException | IOException ex) {
            }
            return true;
        }
    }
}

So here is my problem: when I drag the object of a class ToolbarItem it creates the object of a class OnBoardItem which is placed on the DroppablePanel, the new object is added to the list and its all fine the main option is COPY in DnD that what I want, when I select the item that's been already placed on the DroppablePanel instead of moving the object to the different location it coppies it and place the same object in a different place, when all I want to do is just move them around on the panel. I know its because of a wrongly written TransferHandler in DroppablePanel, but I have spent hours trying to figure out how to fix this but nothing helped at all. If I would have to rewrite these classes or simplify them somehow please let me know how to do this the easiest way. Thanks in advance

EDIT: Graphical settings and methods of the classes have been cut to simplify the code.

  • crossposted

  • create an SSCCE, short, runnable, compilable, otherwise anyone can ended with "Whats ModelEquivalent"

Member Avatar for rancosster

Here you go:

//DroppablePanel.java


package drop;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DnDConstants;
import java.awt.dnd.DropTarget;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.io.IOException;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.TransferHandler;

public class DroppablePanel extends javax.swing.JPanel implements java.awt.dnd.DropTargetListener {

    private java.util.ArrayList<OnBoardItem> _onBoardItems;
    private java.awt.dnd.DropTarget _dropTarget;

    public DroppablePanel() {
        this.setLayout(null);
        this.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        _onBoardItems = new ArrayList<>();
        _dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this);
        this.setTransferHandler(new ItemsTransferHandler());
    }

    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        if (isOpaque()) {
            g.setColor(new Color(255, 255, 255));
            g.fillRect(0, 0, getWidth(), getHeight());
        }
    }

    @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) {
        Transferable transferred = dtde.getTransferable();
        DataFlavor[] transferredData = transferred.getTransferDataFlavors();
        try {
            OnBoardItem temp = (OnBoardItem) transferred.getTransferData(transferredData[0]);
            if (getTransferHandler().canImport(this, transferredData)) {
                if (validateItemExistance(temp.getID())) {
                    addItemOnTheBoard(temp, dtde.getLocation());
                    _onBoardItems.add(temp);
                } else {
                    changeItemLocation(temp, dtde.getLocation());
                }
            }
            revalidate();
            repaint();
        } catch (UnsupportedFlavorException | IOException ex) {
        } finally {
            dtde.dropComplete(true);
        }
    }

    private void addItemOnTheBoard(OnBoardItem currentItem, Point location) {
        this.add(currentItem);
        currentItem.setBounds((int) location.getX(), (int) location.getY(), 43, 43);
    }

    private void changeItemLocation(OnBoardItem currentItem, Point location) {
        currentItem.setBounds((int) location.getX(), (int) location.getY(), 43, 43);
    }

    private boolean validateItemExistance(int ID) {
        for (OnBoardItem d : _onBoardItems) {
            if (d.getID() == ID) {
                return false;
            }
        }
        return true;
    }
}

class ItemsTransferHandler extends TransferHandler {

    @Override
    public boolean canImport(JComponent c, DataFlavor[] f) {
        DataFlavor validatedFlavor = new DataFlavor(OnBoardItem.class, "ToolbarItem");
        for (DataFlavor d : f) {
            if (d.equals(validatedFlavor)) {
                return true;
            }
        }
        return false;
    }

    public static void main(String[] args) {
        JFrame jf = new JFrame("TEST");
        jf.setSize(800, 600);
        DroppablePanel dp = new DroppablePanel();
        JPanel jp = new JPanel();
        ToolbarItem ti = new ToolbarItem("HOST");
        jp.add(ti);
        jf.add(dp, BorderLayout.CENTER);
        jf.add(jp, BorderLayout.WEST);

        jf.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        jf.setVisible(true);
    }
}



//OnBoardItem


package drop;

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.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.TransferHandler;

public class OnBoardItem extends JLabel implements Transferable, DragSourceListener, DragGestureListener {

    private int _ID;
    private static int _itemsQuanity;
    private TransferHandler _itemTransferHandler;
    private DragSource _dragAndDropSource;

    public OnBoardItem(String name) {
        OnBoardItem._itemsQuanity++;
        this._ID = _itemsQuanity;
        this.setMaximumSize(new java.awt.Dimension(43, 43));
        this.setMinimumSize(new java.awt.Dimension(43, 43));
        this.setPreferredSize(new java.awt.Dimension(43, 43));
        this.setText(name + "ID: " + _ID);
        initDragAndDropGestures();
    }

    public int getID() {
        return _ID;
    }

    private void initDragAndDropGestures() {
        this._itemTransferHandler = new TransferHandler() {
            @Override
            public Transferable createTransferable(JComponent c) {
                return OnBoardItem.this;
            }
        };
        this.setTransferHandler(_itemTransferHandler);
        this._dragAndDropSource = new DragSource();
        _dragAndDropSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_MOVE, this);
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{new DataFlavor(OnBoardItem.class, "ITEM")};
    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return true;
    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return this;
    }

    @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) {

    }

    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {
        _dragAndDropSource.startDrag(dge, DragSource.DefaultMoveDrop, this, this);
    }
}



//ToolbarItem

package drop;

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.DragSource;
import java.awt.dnd.DragSourceDragEvent;
import java.awt.dnd.DragSourceDropEvent;
import java.awt.dnd.DragSourceEvent;
import java.awt.dnd.DragSourceListener;
import java.io.IOException;
import javax.swing.JComponent;
import javax.swing.TransferHandler;

public class ToolbarItem extends javax.swing.JToggleButton implements Transferable,
        DragSourceListener, DragGestureListener {

    private DragSource _dragAndDropSource;
    private TransferHandler _itemTransferHandler;

    public ToolbarItem(String name) {
        try {
            this.initItemSettings();
        } catch (java.io.IOException ex) {
            System.out.println("Could not initialize toolbar item ");
        }
    }

    private void initItemSettings() throws java.io.IOException {
        this.setMargin(new java.awt.Insets(2, 14, 2, 14));
        this.setMaximumSize(new java.awt.Dimension(43, 43));
        this.setMinimumSize(new java.awt.Dimension(43, 43));
        this.setPreferredSize(new java.awt.Dimension(43, 43));
        initDragAndDropGestures();
    }

    private void initDragAndDropGestures() {
        this._itemTransferHandler = new TransferHandler() {
            @Override
            public Transferable createTransferable(JComponent c) {
                return new ToolbarItem("HOST");
            }
        };
        this.setTransferHandler(_itemTransferHandler);
        this._dragAndDropSource = new DragSource();
        _dragAndDropSource.createDefaultDragGestureRecognizer(this, DnDConstants.ACTION_COPY, this);
    }

    private void itemMouseEntered(java.awt.event.MouseEvent evt) {
        if (getModel().isRollover() && !this.isSelected()) {
            this.setBorderPainted(true);
            this.setBorder(new javax.swing.border.SoftBevelBorder(javax.swing.border.BevelBorder.RAISED));
        }
    }

    @Override
    public DataFlavor[] getTransferDataFlavors() {
        return new DataFlavor[]{new DataFlavor(ToolbarItem.class, "HOST")};

    }

    @Override
    public boolean isDataFlavorSupported(DataFlavor flavor) {
        return true;

    }

    @Override
    public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
        return this;

    }

    @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) {
    }

    @Override
    public void dragGestureRecognized(DragGestureEvent dge) {
        _dragAndDropSource.startDrag(dge, DragSource.DefaultMoveDrop, new OnBoardItem("HOST"), this);
    }
}

What I want to do is to drag that button from the left side to the center panel and then being able to select the placed one and move them around the on panel.

search for DragLayout made by @camickr will helpo you with most of issues

Member Avatar for rancosster

Thanks, I tried that but it did not fix any of my problems. Did you try to run this? I updated it a little still cant move placed items inside the panel.

Your target panel now seems to add the dragged item to the panel, regardless of where it was dragged from. So for an "internal" drag it will get added again. In the earlier version of the code you seemed to using a list of added items in an attempt to avoid adding the same item twice. That seemed like a reasonable thing to try. Have you abandoned that approach? Alternatively, if the drag starts with an existing OnBoardItem you could record that info somewhere and use that to change the bounds of the existing item rather than adding it again?

Member Avatar for rancosster

I did update the SSCCE code to the point it was before. It checks if I got the same item in the arrayList, so it would not add the same one again. The problem is, it does not change the location of existing item no matter what I do. When I drag an existing item I get the reference to it but any changes I do don't seem to work.

Can you re-post the relevant bit of code where you check for existing and set the new bounds?

Member Avatar for rancosster

It is the SSCCE one I posted I've just updated it.

EDIT: I just checked if JAVA did not create a copy of these objects I compare for existence, but it did not. They both have the same hashCode

EDIT2: I just fixed the problem it was my fault I guess I was changing the reference variable location instead of the object inside of the array list. Here is the part of it that fixes it:

 @Override
    public void drop(DropTargetDropEvent dtde) {
        Transferable transferred = dtde.getTransferable();
        DataFlavor[] transferredData = transferred.getTransferDataFlavors();
        try {
            OnBoardItem temp = (OnBoardItem) transferred.getTransferData(transferredData[0]);
            if (getTransferHandler().canImport(this, transferredData)) {
                if (validateItemExistance(temp.getID())) {
                    addItemOnTheBoard(temp, dtde.getLocation());
                    _onBoardItems.add(temp);
                } else {
                    changeItemLocation(_onBoardItems.get(temp.getID()-1), dtde.getLocation()); //Here was the main change that fixed it
                }
            }
            revalidate();
            repaint();
        } catch (UnsupportedFlavorException | IOException ex) {
        } finally {
            dtde.dropComplete(true);
        }
    }
Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.