Home > Mobile >  How to make button clickable and textfield editable in my Java drag and drop app?
How to make button clickable and textfield editable in my Java drag and drop app?

Time:12-14

I'm learning the drag and drop feature in Java Swing, so I modified a sample app to look like the following :

import java.awt.*;
import java.awt.datatransfer.*;
import java.awt.event.*;
import java.io.*;
import java.util.Arrays;
import javax.activation.*;
import javax.swing.*;

public class Demo_Drag_And_Drop_Panel extends JPanel
{
  public Demo_Drag_And_Drop_Panel()
  {
    DefaultListModel<Item_Panel> m=new DefaultListModel<>();
    for (String s : Arrays.asList("error","information","question","warning")) m.addElement(new Item_Panel(s));
    JList<Item_Panel> list=new JList<>(m);
    list.getSelectionModel().setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
    list.setTransferHandler(new ListItem_TransferHandler());
    list.setDropMode(DropMode.INSERT);
    list.setDragEnabled(true);

    list.setLayoutOrientation(JList.HORIZONTAL_WRAP);                          // http://java-swing-tips.blogspot.jp/2008/10/rubber-band-selection-drag-and-drop.html
    list.setVisibleRowCount(0);
    list.setFixedCellWidth(300);
    list.setFixedCellHeight(36);
    list.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
    list.setCellRenderer(new ListCellRenderer<Item_Panel>()
    {
//      private final JPanel p=new JPanel(new BorderLayout());
      private final JPanel p=new JPanel(new FlowLayout(0,1,1));
      JLabel icon=new JLabel((Icon)null,JLabel.CENTER);
      JButton button=new JButton();
      JLabel label=new JLabel();
      JTextField textField=new JTextField();
      
      @Override public Component getListCellRendererComponent(JList list,Item_Panel value,int index,boolean isSelected,boolean cellHasFocus)
      {
        icon.setIcon(value.icon);
        button.setText(value.button.getText());
        button.addMouseListener(new MouseAdapter()
        {
          public void mouseClicked(MouseEvent e)
          {
            if ((e.getModifiers() & InputEvent.BUTTON1_MASK)!=0)
            {
//            Out("mouseClicked : Left");
              Out(value.name);
            }
            else if ((e.getModifiers() & InputEvent.BUTTON2_MASK)!=0)
            {
//            Out("mouseClicked : Middle");
            }
            else if ((e.getModifiers() & InputEvent.BUTTON3_MASK)!=0)
            {
//            Out("mouseClicked : Right");

            }
          }
        });
        label.setText(value.name);
        label.setForeground(isSelected?list.getSelectionForeground():list.getForeground());
        textField.setText(value.textField.getText());
        textField.setEditable(true);
        p.add(icon);
        p.add(button);
        p.add(label);
        p.add(textField);
        p.setBackground(isSelected?list.getSelectionBackground():list.getBackground());
        return p;
      }
    });
    add(new JScrollPane(list));
  }

  private static void out(String message) { System.out.print(message); }
  private static void Out(String message) { System.out.println(message); }

  public static void main(String[] args) { EventQueue.invokeLater(new Runnable() { @Override public void run() { createAndShowGUI(); } }); }

  public static void createAndShowGUI()
  {
    JFrame f=new JFrame("Demo_Drag_And_Drop_Panel");
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new Demo_Drag_And_Drop_Panel());
    f.setSize(350,213);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

class Item_Panel
{
  public final String name;
  public final Icon icon;
  public final JButton button;
  public final JTextField textField;
  
  public Item_Panel(String name)
  {
    this.name=name;
    icon=UIManager.getIcon("OptionPane." name "Icon");
    button=new JButton(name);
    textField=new JTextField(name);
  }
}
//@camickr already suggested above.
//http://docs.oracle.com/javase/tutorial/uiswing/dnd/dropmodedemo.html

class ListItem_TransferHandler extends TransferHandler
{
  private final DataFlavor localObjectFlavor;
  private Object[] transferedObjects=null;

  public ListItem_TransferHandler() { localObjectFlavor=new ActivationDataFlavor(Object[].class,DataFlavor.javaJVMLocalObjectMimeType,"Array of items"); }

  @SuppressWarnings("deprecation")
  @Override protected Transferable createTransferable(JComponent c)
  {
    JList list=(JList)c;
    indices=list.getSelectedIndices();
    transferedObjects=list.getSelectedValues();
    return new DataHandler(transferedObjects,localObjectFlavor.getMimeType());
  }

  @Override public boolean canImport(TransferSupport info)
  {
    if (!info.isDrop() || !info.isDataFlavorSupported(localObjectFlavor)) return false;
    return true;
  }

  @Override public int getSourceActions(JComponent c)
  {
    return MOVE; //TransferHandler.COPY_OR_MOVE;
  }

  @SuppressWarnings("unchecked")
  @Override public boolean importData(TransferSupport info)
  {
    if (!canImport(info)) return false;
    JList target=(JList)info.getComponent();
    JList.DropLocation dl=(JList.DropLocation)info.getDropLocation();
    DefaultListModel listModel=(DefaultListModel)target.getModel();
    int index=dl.getIndex();
    int max=listModel.getSize();
    if (index<0||index>max) index=max;
    addIndex=index;
    try
    {
      Object[] values=(Object[])info.getTransferable().getTransferData(localObjectFlavor);
      addCount=values.length;
      for (int i=0;i<values.length;i  )
      {
        int idx=index  ;
        listModel.add(idx,values[i]);
        target.addSelectionInterval(idx,idx);
      }
      return true;
    }
    catch (UnsupportedFlavorException ufe) { ufe.printStackTrace(); }
    catch (IOException ioe) { ioe.printStackTrace(); }
    return false;
  }

  @Override protected void exportDone(JComponent c,Transferable data,int action) { cleanup(c,action==MOVE); }

  private void cleanup(JComponent c,boolean remove)
  {
    if (remove&&indices!=null)
    {
      JList source=(JList)c;
      DefaultListModel model=(DefaultListModel)source.getModel();
      if (addCount>0)
      {
        //http://java-swing-tips.googlecode.com/svn/trunk/DnDReorderList/src/java/example/MainPanel.java
        for (int i=0;i<indices.length;i  ) if (indices[i]>=addIndex) indices[i] =addCount;
      }
      for (int i=indices.length-1;i>=0;i--)
      {
        model.remove(indices[i]);
      }
    }
    indices=null;
    addCount=0;
    addIndex=-1;
  }
  private int[] indices=null;
  private int addIndex=-1; //Location where items were added
  private int addCount=0;  //Number of items added.
}

But when I ran it, I noticed the buttons are not clickable and the textfields are not editable, how to fix them ? In other words, I want to be able to click the buttons and output some info and be able to click in the textfields and edit the text in them, how to do that ?

CodePudding user response:

JList doesn't support "edibility". A render is nothing more than a "rubber stamp" which is painted onto the surface of the component.

Now, drag'n'drop is not a "simple" API, it takes a bit of work to get it to work well (lots of boiler plating)

enter image description here

Important, you should beware that dragging components is, generally, a bad idea, you'll end up in all sorts of weird and random problems. Instead, you should focus on transferring the data.

So, in this example, I've decoupled the "data" from the "UI". There is an Item (the data), a ItemPane (a view of the data) which is placed into a scrollable container.

When you "drag" the ItemPane, it is first removed from the container and the Item is extracted and "exported". When you drop it, a new ItemPane is created and the Item applied to it

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
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.DropTargetContext;
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 java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    class Item {

        public final String name;
        public final Icon icon;

        public Item(String name) {
            this.name = name;
            icon = UIManager.getIcon("OptionPane."   name   "Icon");
        }
    }

    public class ItemPane extends JPanel {

        private Item item;

        public ItemPane(Item item) {
            this.item = item;
            setLayout(new FlowLayout(0, 1, 1));
            JLabel icon = new JLabel(item.icon, JLabel.CENTER);
            JButton button = new JButton(item.name);
            JLabel label = new JLabel(item.name);
            JTextField textField = new JTextField(item.name);

            add(icon);
            add(button);
            add(label);
            add(textField);
        }

        public Item getItem() {
            return item;
        }

    }

    public class TestPane extends JPanel {

        public TestPane() {
            JPanel mainPanel = new javax.swing.JPanel();
            mainPanel.setLayout(new GridLayout(4, 0));

            for (String s : Arrays.asList("error", "information", "question", "warning")) {
                ItemPane itemPane = new ItemPane(new Item(s));
                add(itemPane);
                DragDropManager.INSTANCE.installDrag(itemPane);
                mainPanel.add(itemPane);
            }

            DropHandler dropHandler = new DropHandler();
            DropTarget dropTarget = new DropTarget(mainPanel, DnDConstants.ACTION_MOVE, dropHandler, true);
            mainPanel.setDropTarget(dropTarget);

            setLayout(new BorderLayout());
            add(new JScrollPane(mainPanel));
        }

    }

    public enum DragDropManager {

        INSTANCE;

        private Map<ItemPane, DragManager> handlers = new HashMap<>(25);

        protected void installDrag(ItemPane comp) {
            handlers.put(comp, new DragManager(comp));
        }

        protected void uninstallDrag(Component comp) {
            DragManager manager = handlers.remove(comp);
            if (manager != null) {
                manager.uninstall();
            }
        }

        protected class DragManager {

            DragGestureHandler dragGestureHandler;
            DragGestureRecognizer dgr;

            public DragManager(ItemPane comp) {
                dragGestureHandler = new DragGestureHandler(comp);
                dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                        comp,
                        DnDConstants.ACTION_MOVE,
                        dragGestureHandler);
            }

            public void uninstall() {
                dgr.removeDragGestureListener(dragGestureHandler);
                dragGestureHandler = null;
                dgr = null;
            }
        }
    }

    public static class ItemDataFlavor extends DataFlavor {

        // This saves me having to make lots of copies of the same thing
        public static final ItemDataFlavor SHARED = new ItemDataFlavor();

        public ItemDataFlavor() {
            super(Item.class, null);
        }
    }

    public static class ItemTransferable implements Transferable {

        private DataFlavor[] flavors = new DataFlavor[]{ItemDataFlavor.SHARED};
        private Item item;

        public ItemTransferable(Item item) {
            this.item = item;
        }

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

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            // Okay, for this example, this is over kill, but makes it easier
            // to add new flavor support by subclassing
            boolean supported = false;
            for (DataFlavor mine : getTransferDataFlavors()) {
                if (mine.equals(flavor)) {
                    supported = true;
                    break;
                }
            }

            return supported;
        }

        public Item getItem() {
            return item;
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            Object data = null;
            if (isDataFlavorSupported(flavor)) {
                data = getItem();
            } else {
                throw new UnsupportedFlavorException(flavor);
            }
            return data;
        }
    }

    public static class DragGestureHandler implements DragGestureListener, DragSourceListener {

        private Container parent;
        private final ItemPane component;

        public DragGestureHandler(ItemPane child) {
            this.component = child;
        }

        public ItemPane getItemPane() {
            return component;
        }

        public Item getItem() {
            return component.getItem();
        }

        public void setParent(Container parent) {
            this.parent = parent;
        }

        public Container getParent() {
            return parent;
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            // Create our transferable wrapper
            Transferable transferable = new ItemTransferable(getItem());

            // Start the "drag" process...
            DragSource ds = dge.getDragSource();
            ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);

            // When the drag begins, we need to grab a reference to the
            // parent container so we can return it if the drop
            // is rejected
            Container parent = getItemPane().getParent();

            setParent(parent);

            // Remove the panel from the parent.  If we don't do this, it
            // can cause serialization issues.  We could over come this
            // by allowing the drop target to remove the component, but that's
            // an argument for another day
            parent.remove(getItemPane());

            // Update the display
            parent.invalidate();
            parent.repaint();

            DragDropManager.INSTANCE.uninstallDrag(getItemPane());
        }

        @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) {
            // If the drop was not sucessful, we need to
            // return the component back to it's previous
            // parent
            if (!dsde.getDropSuccess()) {

                getParent().add(getItemPane());

                getParent().invalidate();
                getParent().repaint();

            }
        }
    }

    public class DropHandler implements DropTargetListener {

        private JComponent spacer = new JPanel();

        public DropHandler() {
            spacer.setBackground(Color.RED);
        }

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {
            // Determine if can actual process the contents comming in.
            // You could try and inspect the transferable as well, but 
            // There is an issue on the MacOS under some circumstances
            // where it does not actually bundle the data until you accept the
            // drop.
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                dtde.acceptDrag(DnDConstants.ACTION_MOVE);
            } else {
                dtde.rejectDrag();
            }
        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                Point p = dtde.getLocation();
                DropTargetContext dtc = dtde.getDropTargetContext();
                Container parent = (Container) dtc.getComponent();
                Component target = parent.getComponentAt(p);
                int insertPoint = Math.max(0, parent.getComponentZOrder(target));
                if (spacer.getParent() == null) {
                    parent.add(spacer, insertPoint);
                } else {
                    parent.setComponentZOrder(spacer, insertPoint);
                }
                parent.revalidate();
                parent.repaint();

                Point pic = SwingUtilities.convertPoint(spacer, p, target);
                Rectangle bounds = spacer.getBounds();
                bounds.setLocation(pic);

                ((JComponent) parent).scrollRectToVisible(bounds);
            }
        }

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(DropTargetEvent dte) {
            Container parent = (Container) dte.getDropTargetContext().getComponent();
            parent.remove(spacer);
            parent.revalidate();
            parent.repaint();
        }

        @Override
        public void drop(DropTargetDropEvent dtde) {
            boolean success = false;

            // Basically, we want to unwrap the present...
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                Transferable transferable = dtde.getTransferable();
                try {
                    Object data = transferable.getTransferData(ItemDataFlavor.SHARED);
                    if (data instanceof Item) {
                        Item item = (Item) data;

                        DropTargetContext dtc = dtde.getDropTargetContext();
                        Component component = dtc.getComponent();
                        if (component instanceof JComponent) {
                            Container parent = (Container) component;

                            Point p = dtde.getLocation();
                            Component before = parent.getComponentAt(p);
                            int insertPoint = Math.max(0, parent.getComponentZOrder(before));
                            parent.remove(spacer);

                            ItemPane itemPane = new ItemPane(item);
                            parent.add(itemPane, insertPoint);
                            parent.revalidate();
                            parent.repaint();

                            DragDropManager.INSTANCE.installDrag(itemPane);

                            success = true;
                            dtde.acceptDrop(DnDConstants.ACTION_MOVE);

                            parent.invalidate();
                            parent.repaint();
                        } else {
                            success = false;
                            dtde.rejectDrop();
                        }

                    } else {
                        success = false;
                        dtde.rejectDrop();
                    }

                } catch (Exception exp) {
                    success = false;
                    dtde.rejectDrop();
                    exp.printStackTrace();
                }
            } else {
                success = false;
                dtde.rejectDrop();
            }
            dtde.dropComplete(success);
        }
    }
}

CodePudding user response:

@MadProgrammer : Very interesting, I just tried your modified version with Java 8, it compiled alright, and runs, but I've noticed some issues :

[1] After I closed the app, the process is still running, so I fixed that.

[2] I've added a listener for button click, now it can output some info.

[3] I tried to change some text in the JTextFiled, then tried to drag & drop the changed item, the changes were erased after more than one drag & drop, why ? Can it remember the changes ? After all the editable JTextFiled is for changing text, right ^_^ ?! However, if the changed field is not D&D [ other unchanged items are D&D ], then it seemed to remember the changes.

So, overall there is improvement, but if we can fix the changed field and remember the text change, then I'll be super happy. Here is my modified code :

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.Rectangle;
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.DropTargetContext;
import java.awt.dnd.DropTargetDragEvent;
import java.awt.dnd.DropTargetDropEvent;
import java.awt.dnd.DropTargetEvent;
import java.awt.dnd.DropTargetListener;
import java.awt.event.*;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import javax.swing.Icon;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;

public class Test {

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

    public Test() {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                JFrame frame = new JFrame();
                frame.add(new TestPane());
                frame.addWindowListener( new WindowAdapter()
                {
                  public void windowActivated(WindowEvent e) { }
                  public void windowClosed(WindowEvent e) { }
                  public void windowClosing(WindowEvent e) { System.exit(0); }
                });
                frame.pack();
                frame.setLocationRelativeTo(null);
                frame.setVisible(true);
            }
        });
    }

    class Item {

        public final String name;
        public final Icon icon;

        public Item(String name) {
            this.name = name;
            icon = UIManager.getIcon("OptionPane."   name   "Icon");
        }
    }

    public class ItemPane extends JPanel {

        private Item item;

        public ItemPane(Item item) {
            this.item = item;
            setLayout(new FlowLayout(0, 1, 1));
            JLabel icon = new JLabel(item.icon, JLabel.CENTER);
            JButton button = new JButton(item.name);
            button.addMouseListener(new MouseAdapter()
            {
              public void mouseClicked(MouseEvent e)
              {
                if ((e.getModifiers()&InputEvent.BUTTON1_MASK)!=0)
                {
//          Out("mouseClicked : Left");
                  System.out.println(item.name);
                }
                else if ((e.getModifiers()&InputEvent.BUTTON2_MASK)!=0)
                {
//          Out("mouseClicked : Middle");
                }
                else if ((e.getModifiers()&InputEvent.BUTTON3_MASK)!=0)
                {
//          Out("mouseClicked : Right");
                }
              }
            });
            JLabel label = new JLabel(item.name);
            JTextField textField = new JTextField(item.name);

            add(icon);
            add(button);
            add(label);
            add(textField);
        }

        public Item getItem() {
            return item;
        }

    }

    public class TestPane extends JPanel {

        public TestPane() {
            JPanel mainPanel = new javax.swing.JPanel();
            mainPanel.setLayout(new GridLayout(4, 0));

            for (String s : Arrays.asList("error", "information", "question", "warning")) {
                ItemPane itemPane = new ItemPane(new Item(s));
                add(itemPane);
                DragDropManager.INSTANCE.installDrag(itemPane);
                mainPanel.add(itemPane);
            }

            DropHandler dropHandler = new DropHandler();
            DropTarget dropTarget = new DropTarget(mainPanel, DnDConstants.ACTION_MOVE, dropHandler, true);
            mainPanel.setDropTarget(dropTarget);

            setLayout(new BorderLayout());
            add(new JScrollPane(mainPanel));
        }

    }

    public enum DragDropManager {

        INSTANCE;

        private Map<ItemPane, DragManager> handlers = new HashMap<>(25);

        protected void installDrag(ItemPane comp) {
            handlers.put(comp, new DragManager(comp));
        }

        protected void uninstallDrag(Component comp) {
            DragManager manager = handlers.remove(comp);
            if (manager != null) {
                manager.uninstall();
            }
        }

        protected class DragManager {

            DragGestureHandler dragGestureHandler;
            DragGestureRecognizer dgr;

            public DragManager(ItemPane comp) {
                dragGestureHandler = new DragGestureHandler(comp);
                dgr = DragSource.getDefaultDragSource().createDefaultDragGestureRecognizer(
                        comp,
                        DnDConstants.ACTION_MOVE,
                        dragGestureHandler);
            }

            public void uninstall() {
                dgr.removeDragGestureListener(dragGestureHandler);
                dragGestureHandler = null;
                dgr = null;
            }
        }
    }

    public static class ItemDataFlavor extends DataFlavor {

        // This saves me having to make lots of copies of the same thing
        public static final ItemDataFlavor SHARED = new ItemDataFlavor();

        public ItemDataFlavor() {
            super(Item.class, null);
        }
    }

    public static class ItemTransferable implements Transferable {

        private DataFlavor[] flavors = new DataFlavor[]{ItemDataFlavor.SHARED};
        private Item item;

        public ItemTransferable(Item item) {
            this.item = item;
        }

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

        @Override
        public boolean isDataFlavorSupported(DataFlavor flavor) {
            // Okay, for this example, this is over kill, but makes it easier
            // to add new flavor support by subclassing
            boolean supported = false;
            for (DataFlavor mine : getTransferDataFlavors()) {
                if (mine.equals(flavor)) {
                    supported = true;
                    break;
                }
            }

            return supported;
        }

        public Item getItem() {
            return item;
        }

        @Override
        public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
            Object data = null;
            if (isDataFlavorSupported(flavor)) {
                data = getItem();
            } else {
                throw new UnsupportedFlavorException(flavor);
            }
            return data;
        }
    }

    public static class DragGestureHandler implements DragGestureListener, DragSourceListener {

        private Container parent;
        private final ItemPane component;

        public DragGestureHandler(ItemPane child) {
            this.component = child;
        }

        public ItemPane getItemPane() {
            return component;
        }

        public Item getItem() {
            return component.getItem();
        }

        public void setParent(Container parent) {
            this.parent = parent;
        }

        public Container getParent() {
            return parent;
        }

        @Override
        public void dragGestureRecognized(DragGestureEvent dge) {
            // Create our transferable wrapper
            Transferable transferable = new ItemTransferable(getItem());

            // Start the "drag" process...
            DragSource ds = dge.getDragSource();
            ds.startDrag(dge, Cursor.getPredefinedCursor(Cursor.MOVE_CURSOR), transferable, this);

            // When the drag begins, we need to grab a reference to the
            // parent container so we can return it if the drop
            // is rejected
            Container parent = getItemPane().getParent();

            setParent(parent);

            // Remove the panel from the parent.  If we don't do this, it
            // can cause serialization issues.  We could over come this
            // by allowing the drop target to remove the component, but that's
            // an argument for another day
            parent.remove(getItemPane());

            // Update the display
            parent.invalidate();
            parent.repaint();

            DragDropManager.INSTANCE.uninstallDrag(getItemPane());
        }

        @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) {
            // If the drop was not sucessful, we need to
            // return the component back to it's previous
            // parent
            if (!dsde.getDropSuccess()) {

                getParent().add(getItemPane());

                getParent().invalidate();
                getParent().repaint();

            }
        }
    }

    public class DropHandler implements DropTargetListener {

        private JComponent spacer = new JPanel();

        public DropHandler() {
            spacer.setBackground(Color.RED);
        }

        @Override
        public void dragEnter(DropTargetDragEvent dtde) {
            // Determine if can actual process the contents comming in.
            // You could try and inspect the transferable as well, but 
            // There is an issue on the MacOS under some circumstances
            // where it does not actually bundle the data until you accept the
            // drop.
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                dtde.acceptDrag(DnDConstants.ACTION_MOVE);
            } else {
                dtde.rejectDrag();
            }
        }

        @Override
        public void dragOver(DropTargetDragEvent dtde) {
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                Point p = dtde.getLocation();
                DropTargetContext dtc = dtde.getDropTargetContext();
                Container parent = (Container) dtc.getComponent();
                Component target = parent.getComponentAt(p);
                int insertPoint = Math.max(0, parent.getComponentZOrder(target));
                if (spacer.getParent() == null) {
                    parent.add(spacer, insertPoint);
                } else {
                    parent.setComponentZOrder(spacer, insertPoint);
                }
                parent.revalidate();
                parent.repaint();

                Point pic = SwingUtilities.convertPoint(spacer, p, target);
                Rectangle bounds = spacer.getBounds();
                bounds.setLocation(pic);

                ((JComponent) parent).scrollRectToVisible(bounds);
            }
        }

        @Override
        public void dropActionChanged(DropTargetDragEvent dtde) {
        }

        @Override
        public void dragExit(DropTargetEvent dte) {
            Container parent = (Container) dte.getDropTargetContext().getComponent();
            parent.remove(spacer);
            parent.revalidate();
            parent.repaint();
        }

        @Override
        public void drop(DropTargetDropEvent dtde) {
            boolean success = false;

            // Basically, we want to unwrap the present...
            if (dtde.isDataFlavorSupported(ItemDataFlavor.SHARED)) {
                Transferable transferable = dtde.getTransferable();
                try {
                    Object data = transferable.getTransferData(ItemDataFlavor.SHARED);
                    if (data instanceof Item) {
                        Item item = (Item) data;

                        DropTargetContext dtc = dtde.getDropTargetContext();
                        Component component = dtc.getComponent();
                        if (component instanceof JComponent) {
                            Container parent = (Container) component;

                            Point p = dtde.getLocation();
                            Component before = parent.getComponentAt(p);
                            int insertPoint = Math.max(0, parent.getComponentZOrder(before));
                            parent.remove(spacer);

                            ItemPane itemPane = new ItemPane(item);
                            parent.add(itemPane, insertPoint);
                            parent.revalidate();
                            parent.repaint();

                            DragDropManager.INSTANCE.installDrag(itemPane);

                            success = true;
                            dtde.acceptDrop(DnDConstants.ACTION_MOVE);

                            parent.invalidate();
                            parent.repaint();
                        } else {
                            success = false;
                            dtde.rejectDrop();
                        }

                    } else {
                        success = false;
                        dtde.rejectDrop();
                    }

                } catch (Exception exp) {
                    success = false;
                    dtde.rejectDrop();
                    exp.printStackTrace();
                }
            } else {
                success = false;
                dtde.rejectDrop();
            }
            dtde.dropComplete(success);
        }
    }
}
  • Related