TabTransferHandler.java
package com.jsql.view.swing.tab.dnd;
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.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.dnd.DragSource;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Objects;
import java.util.Optional;
public class TabTransferHandler extends TransferHandler {
/**
* Log4j logger sent to view.
*/
private static final Logger LOGGER = LogManager.getRootLogger();
protected final DataFlavor localObjectFlavor;
protected DnDTabbedPane source;
public TabTransferHandler() {
super();
this.localObjectFlavor = new DataFlavor(DnDTabData.class, "DnDTabData");
}
@Override
protected Transferable createTransferable(JComponent c) {
if (c instanceof DnDTabbedPane) {
this.source = (DnDTabbedPane) c;
}
return new Transferable() {
@Override
public DataFlavor[] getTransferDataFlavors() {
return new DataFlavor[] { TabTransferHandler.this.localObjectFlavor };
}
@Override
public boolean isDataFlavorSupported(DataFlavor flavor) {
return Objects.equals(TabTransferHandler.this.localObjectFlavor, flavor);
}
@Override
public Object getTransferData(DataFlavor flavor) throws UnsupportedFlavorException {
if (this.isDataFlavorSupported(flavor)) {
return new DnDTabData(TabTransferHandler.this.source);
} else {
throw new UnsupportedFlavorException(flavor);
}
}
};
}
@Override
public boolean canImport(TransferHandler.TransferSupport support) {
if (!support.isDrop() || !support.isDataFlavorSupported(this.localObjectFlavor)) {
return false;
}
support.setDropAction(TransferHandler.MOVE);
var tdl = support.getDropLocation();
var pt = tdl.getDropPoint();
DnDTabbedPane target = (DnDTabbedPane) support.getComponent();
target.autoScrollTest(pt);
DnDTabbedPane.DnDDropLocation dl = target.dropLocationForPointDnD(pt);
int idx = dl.getIndex();
var isDroppable = false;
boolean isAreaContains = target.getTabAreaBounds().contains(pt) && idx >= 0;
if (target.equals(this.source)) {
isDroppable = isAreaContains && idx != target.dragTabIndex && idx != target.dragTabIndex + 1;
} else {
isDroppable = Optional.ofNullable(this.source).map(c -> !c.isAncestorOf(target)).orElse(false) && isAreaContains;
}
// [JDK-6700748] Cursor flickering during D&D when using CellRendererPane with validation - Java Bug System
// https://bugs.openjdk.java.net/browse/JDK-6700748
Cursor cursor = isDroppable ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop;
Component glassPane = target.getRootPane().getGlassPane();
glassPane.setCursor(cursor);
target.setCursor(cursor);
support.setShowDropLocation(isDroppable);
dl.setDroppable(isDroppable);
target.setDropLocation(dl, isDroppable);
return isDroppable;
}
private BufferedImage makeDragTabImage(DnDTabbedPane tabbedPane) {
Rectangle rect = tabbedPane.getBoundsAt(tabbedPane.dragTabIndex);
var image = new BufferedImage(tabbedPane.getWidth(), tabbedPane.getHeight(), BufferedImage.TYPE_INT_ARGB);
Graphics g2 = image.createGraphics();
tabbedPane.paint(g2);
g2.dispose();
if (rect.x < 0) {
rect.translate(-rect.x, 0);
}
if (rect.y < 0) {
rect.translate(0, -rect.y);
}
if (rect.x + rect.width > image.getWidth()) {
rect.width = image.getWidth() - rect.x;
}
if (rect.y + rect.height > image.getHeight()) {
rect.height = image.getHeight() - rect.y;
}
return image.getSubimage(rect.x, rect.y, rect.width, rect.height);
}
@Override
public int getSourceActions(JComponent c) {
if (c instanceof DnDTabbedPane) {
DnDTabbedPane src = (DnDTabbedPane) c;
c.getRootPane().setGlassPane(new GhostGlassPane(src));
if (src.dragTabIndex < 0) {
return TransferHandler.NONE;
}
this.setDragImage(this.makeDragTabImage(src));
c.getRootPane().getGlassPane().setVisible(true);
return TransferHandler.MOVE;
}
return TransferHandler.NONE;
}
@Override
public boolean importData(TransferHandler.TransferSupport support) {
if (!this.canImport(support)) {
return false;
}
DnDTabbedPane target = (DnDTabbedPane) support.getComponent();
DnDTabbedPane.DnDDropLocation dl = target.getDropLocation();
try {
DnDTabData data = (DnDTabData) support.getTransferable().getTransferData(this.localObjectFlavor);
DnDTabbedPane src = data.tabbedPane;
int index = dl.getIndex();
if (target.equals(src)) {
src.convertTab(src.dragTabIndex, index);
} else {
src.exportTab(src.dragTabIndex, target, index);
}
return true;
} catch (UnsupportedFlavorException | IOException e) {
LOGGER.log(LogLevelUtil.CONSOLE_JAVA, e, e);
}
return false;
}
@Override
protected void exportDone(JComponent c, Transferable data, int action) {
DnDTabbedPane src = (DnDTabbedPane) c;
src.getRootPane().getGlassPane().setVisible(false);
src.setDropLocation(null, false);
src.repaint();
src.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
}
}