I tried to fix this issue for the past week but somehow I cant seem to find a solution. There is not a lot of information about this topic so its hard to find examples or code to look at.
What I have here is a JList which uses a custom TransferHandler that creates a custom Transferable, for reference here's the code of the involved classes:
Transferable:
package org.dinhware.swing.special;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas
* Date: 20.10.2017
* Alias: Dinh
* Time: 20:03
*/
public class GenericTransferable<T> implements Transferable {
static DataFlavor FLAVOR;
private T object;
GenericTransferable(T object) {
GenericTransferable.FLAVOR = new DataFlavor(object.getClass(), object.getClass().getCanonicalName());
this.object = object;
}
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[]{FLAVOR};
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return flavor.equals(FLAVOR);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException, IOException {
return object;
}
}
TransferHandler:
package org.dinhware.swing.special;
import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas
* Date: 19.10.2017
* Alias: Dinh
* Time: 18:54
*/
@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {
@Override
protected Transferable createTransferable(JComponent component) {
JList<T> list = (JList<T>) component;
index = list.getSelectedIndex();
T transferredObject = list.getSelectedValue();
return new GenericTransferable<>(transferredObject);
}
@Override
public boolean canImport(TransferSupport info) {
return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
}
@Override
public int getSourceActions(JComponent c) {
return MOVE;
}
@Override
public boolean importData(TransferSupport info) {
if (!canImport(info)) {
return false;
}
JList<Object> target = (JList<Object>) info.getComponent();
JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
int index = dl.getIndex();
int max = listModel.getSize();
if (index < 0 || index > max)
index = max;
addIndex = index;
try {
Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
listModel.add(index, object);
target.addSelectionInterval(index, index);
return moveAllowed = true;
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
if (moveAllowed)
cleanup(c, action == MOVE, false);
}
private void cleanup(JComponent component, boolean remove, boolean bin) {
if (remove && index != -1) {
JList<T> source = (JList<T>) component;
DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
int removeAt = index > addIndex ? index + 1 : index;
model.remove(bin ? removeAt - 1 : removeAt);
}
index = -1;
addIndex = -1;
moveAllowed = false;
}
private int index = -1;
private int addIndex = -1;
private boolean moveAllowed = false;
}
HBin
package org.dinhware.swing.child;
import org.dinhware.swing.special.HListItemTransferHandler;
import javax.swing.*;
/**
* Created by: Niklas
* Date: 20.10.2017
* Alias: Dinh
* Time: 19:57
*/
public class HBin<T> extends HImageLabel {
public HBin(String text, Icon image, int distance) {
super(text, image, distance);
setTransferHandler(new HListItemTransferHandler<T>());
}
}
And a visualization of how it should work, sadly the container always disappears even when not dragged onto the HBin Container. I thought it was working the entire time until I accidentally moved it outside of my Frame and it still disappeared. The Code above only allows dragging/dropping inside of the List which is intended.
My Question is how to add the functionality to properly make a Container only disappear when dragged onto the HBin Container
The first part of Code I used was this
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
if (moveAllowed) cleanup(c, action == MOVE, false);
else try {
if (data.getTransferData(GenericTransferable.FLAVOR) instanceof RewardItem) {
cleanup(c, true, true);
}
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
}
My logic behind it was that both the List and HBin shared the same Type (RewardItem) which I could compare, later I realized (after making a more generic version of that method) that data will always be of the type RewardItem and will always result in a cleanup call. This results in the bug I am currently still facing.
The approach I took earlier today really made me question my mind and also made me do this post. I added a boolean to the TransferHandler called bin which was false by default. After the canImport check in importData I added bin = info.getComponent() instanceof HBin
which I thought should work. But this field always stood false. I went ahead and added a log for it
System.out.println("IMPORT");
if (info.getComponent() instanceof HBin) {
System.out.println("bin");
return bin = true;
}
It ended up printing IMPORT followed by bin. After importData exportData is called in which I then logged the value of bin, which for whatever reason was now false again. Meanwhile the moveAllowed field seems to change.
This was my full modified TransferHandler
package org.dinhware.swing.special;
import org.dinhware.swing.child.HBin;
import javax.swing.*;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.IOException;
/**
* Created by: Niklas Date: 19.10.2017 Alias: Dinh Time: 18:54
*/
@SuppressWarnings("unchecked")
public class HListItemTransferHandler<T> extends TransferHandler {
@Override
protected Transferable createTransferable(JComponent component) {
System.out.println("CREATE");
JList<T> list = (JList<T>) component;
index = list.getSelectedIndex();
T transferredObject = list.getSelectedValue();
return new GenericTransferable<>(transferredObject);
}
@Override
public boolean canImport(TransferSupport info) {
return info.isDataFlavorSupported(GenericTransferable.FLAVOR);
}
@Override
public int getSourceActions(JComponent c) {
System.out.println("ACTION");
return MOVE;
}
@Override
public boolean importData(TransferSupport info) {
System.out.println("IMPORT");
if (!canImport(info)) {
return false;
}
if (info.getComponent() instanceof HBin) {
System.out.println("bin");
return bin = true;
}
JList<Object> target = (JList<Object>) info.getComponent();
JList.DropLocation dl = (JList.DropLocation) info.getDropLocation();
DefaultListModel<Object> listModel = (DefaultListModel<Object>) target.getModel();
int index = dl.getIndex();
int max = listModel.getSize();
if (index < 0 || index > max)
index = max;
addIndex = index;
try {
Object object = info.getTransferable().getTransferData(GenericTransferable.FLAVOR);
listModel.add(index, object);
target.addSelectionInterval(index, index);
return moveAllowed = true;
} catch (UnsupportedFlavorException | IOException e) {
e.printStackTrace();
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
System.out.println("EXPORT " + moveAllowed + "/" + bin);
if (moveAllowed)
cleanup(c, action == MOVE, false);
else
cleanup(c, true, true);
}
private void cleanup(JComponent component, boolean remove, boolean bin) {
System.out.println("CLEAN");
if (remove && index != -1) {
JList<T> source = (JList<T>) component;
DefaultListModel<T> model = (DefaultListModel<T>) source.getModel();
int removeAt = index > addIndex ? index + 1 : index;
model.remove(bin ? removeAt - 1 : removeAt);
}
index = -1;
addIndex = -1;
moveAllowed = false;
}
private int index = -1;
private int addIndex = -1;
private boolean moveAllowed = false, bin = false;
}
When moving inside of the List everything works fine (prints)
ACTION
CREATE
IMPORT
EXPORT true/false
CLEAN
But when dropping onto the HBin Container I cant explain whats going on (prints)
ACTION
CREATE
IMPORT
bin
EXPORT false/false
I am farily sure that it should be false/true
Now I am stuck, not able to make the Container only disappear when dropped onto HBin whilst also being confused about the field value not changing when it clearly logs that it has been set to true.
Please.. help...