AbstractListTransfertHandler.java
/*******************************************************************************
* Copyhacked (H) 2012-2020.
* This program and the accompanying materials
* are made available under no term at all, use it like
* you want, but share and discuss about it
* every time possible with every body.
*
* Contributors:
* ron190 at ymail dot com - initial implementation
******************************************************************************/
package com.jsql.view.swing.list;
import com.jsql.util.LogLevelUtil;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import javax.swing.*;
import java.awt.*;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.io.File;
import java.io.IOException;
import java.util.List;
/**
* Handler for processing cut/copy/paste/drag/drop action on a JList items.
*/
public abstract class AbstractListTransfertHandler extends TransferHandler {
/**
* Log4j logger sent to view.
*/
private static final Logger LOGGER = LogManager.getRootLogger();
/**
* List of cut/copy/paste/drag/drop items.
*/
protected transient List<ItemList> dragPaths = null;
protected abstract String initializeTransferable();
protected abstract void parseStringDrop(TransferSupport support, DnDList list, DefaultListModel<ItemList> listModel);
protected abstract List<Integer> initializeStringPaste(String clipboardText, int selectedIndex, DefaultListModel<ItemList> listModel);
@Override
public int getSourceActions(JComponent c) {
return TransferHandler.COPY_OR_MOVE;
}
@Override
protected Transferable createTransferable(JComponent c) {
DnDList list = (DnDList) c;
this.dragPaths = list.getSelectedValuesList();
var stringTransferable = this.initializeTransferable();
return new StringSelection(stringTransferable.trim());
}
@SuppressWarnings("unchecked")
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
if (action == TransferHandler.MOVE) {
JList<ItemList> list = (JList<ItemList>) c;
DefaultListModel<ItemList> model = (DefaultListModel<ItemList>) list.getModel();
for (ItemList itemPath: this.dragPaths) {
// Unhandled ArrayIndexOutOfBoundsException #56115 on remove()
try {
model.remove(model.indexOf(itemPath));
} catch (ArrayIndexOutOfBoundsException e) {
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e.getMessage(), e);
}
}
this.dragPaths = null;
}
}
@Override
public boolean canImport(TransferSupport support) {
return
support.isDataFlavorSupported(DataFlavor.stringFlavor)
|| support.isDataFlavorSupported(DataFlavor.javaFileListFlavor);
}
@Override
public boolean importData(TransferSupport support) {
if (!this.canImport(support)) {
return false;
}
DnDList list = (DnDList) support.getComponent();
DefaultListModel<ItemList> listModel = (DefaultListModel<ItemList>) list.getModel();
//This is a drop
if (support.isDrop()) {
if (support.isDataFlavorSupported(DataFlavor.stringFlavor)) {
this.parseStringDrop(support, list, listModel);
} else if (support.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
this.parseFileDrop(support, list);
}
} else {
//This is a paste
var transferableFromClipboard = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
if (transferableFromClipboard != null) {
if (transferableFromClipboard.isDataFlavorSupported(DataFlavor.stringFlavor)) {
this.parseStringPaste(list, listModel, transferableFromClipboard);
} else if (transferableFromClipboard.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) {
this.parseFilePaste(list, transferableFromClipboard);
}
}
}
return true;
}
@SuppressWarnings("unchecked")
private void parseFileDrop(TransferSupport support, DnDList list) {
JList.DropLocation dropLocation = (JList.DropLocation) support.getDropLocation();
int childIndex = dropLocation.getIndex();
try {
list.dropPasteFile(
(List<File>) support.getTransferable().getTransferData(DataFlavor.javaFileListFlavor),
childIndex
);
} catch (UnsupportedFlavorException | IOException e) {
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
}
}
private void parseStringPaste(DnDList list, DefaultListModel<ItemList> listModel, Transferable transferableFromClipboard) {
try {
String clipboardText = (String) transferableFromClipboard.getTransferData(DataFlavor.stringFlavor);
var selectedIndexPaste = Math.max(list.getSelectedIndex(), 0);
list.clearSelection();
List<Integer> selectedIndexes = this.initializeStringPaste(clipboardText, selectedIndexPaste, listModel);
var selectedIndexesPasted = new int[selectedIndexes.size()];
var i = 0;
for (Integer selectedIndex: selectedIndexes) {
selectedIndexesPasted[i] = selectedIndex;
i++;
}
list.setSelectedIndices(selectedIndexesPasted);
list.scrollRectToVisible(
list.getCellBounds(
list.getMinSelectionIndex(),
list.getMaxSelectionIndex()
)
);
} catch (NullPointerException | UnsupportedFlavorException | IOException e) {
// Fix #8831: Multiple Exception on scrollRectToVisible()
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
}
}
@SuppressWarnings("unchecked")
private void parseFilePaste(DnDList list, Transferable transferableFromClipboard) {
try {
var selectedIndex = Math.max(list.getSelectedIndex(), 0);
list.clearSelection();
list.dropPasteFile(
(List<File>) transferableFromClipboard.getTransferData(DataFlavor.javaFileListFlavor),
selectedIndex
);
} catch (UnsupportedFlavorException | IOException e) {
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
}
}
}