package org.jabref.gui.fieldeditors; import java.awt.datatransfer.Clipboard; import java.awt.datatransfer.DataFlavor; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.dnd.DnDConstants; import java.io.IOException; import java.net.URL; import java.nio.file.Path; import java.util.ArrayList; import java.util.List; import javax.swing.JComponent; import javax.swing.SwingUtilities; import javax.swing.TransferHandler; import org.jabref.gui.EntryContainer; import org.jabref.gui.JabRefFrame; import org.jabref.gui.externalfiles.DroppedFileHandler; import org.jabref.gui.externalfiletype.ExternalFileTypes; import org.jabref.gui.groups.EntryTableTransferHandler; import org.jabref.model.util.FileHelper; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; class FileListEditorTransferHandler extends TransferHandler { private static final Log LOGGER = LogFactory.getLog(FileListEditorTransferHandler.class); private DataFlavor urlFlavor; private final DataFlavor stringFlavor; private final JabRefFrame frame; private final EntryContainer entryContainer; private final TransferHandler textTransferHandler; private DroppedFileHandler droppedFileHandler; /** * * @param frame * @param entryContainer * @param textTransferHandler is an instance of javax.swing.plaf.basic.BasicTextUI.TextTransferHandler. That class is not visible. Therefore, we have to "cheat" */ public FileListEditorTransferHandler(JabRefFrame frame, EntryContainer entryContainer, TransferHandler textTransferHandler) { this.frame = frame; this.entryContainer = entryContainer; this.textTransferHandler = textTransferHandler; stringFlavor = DataFlavor.stringFlavor; try { urlFlavor = new DataFlavor("application/x-java-url; class=java.net.URL"); } catch (ClassNotFoundException e) { LOGGER.info("Unable to configure drag and drop for file link table", e); } } /** * Overridden to indicate which types of drags are supported (only LINK + COPY). * COPY is supported as no support disables CTRL+C (copy of text) */ @Override public int getSourceActions(JComponent c) { return DnDConstants.ACTION_LINK | DnDConstants.ACTION_COPY; } @Override public void exportToClipboard(JComponent comp, Clipboard clip, int action) { if (this.textTransferHandler != null) { this.textTransferHandler.exportToClipboard(comp, clip, action); } } @Override public boolean importData(JComponent comp, Transferable t) { // If the drop target is the main table, we want to record which // row the item was dropped on, to identify the entry if needed: try { List<Path> files = new ArrayList<>(); // This flavor is used for dragged file links in Windows: if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { @SuppressWarnings("unchecked") List<Path> transferedFiles = (List<Path>) t.getTransferData(DataFlavor.javaFileListFlavor); files.addAll(transferedFiles); } if (t.isDataFlavorSupported(urlFlavor)) { URL dropLink = (URL) t.getTransferData(urlFlavor); LOGGER.debug("URL: " + dropLink); } // This is used when one or more files are pasted from the file manager // under Gnome. The data consists of the file paths, one file per line: if (t.isDataFlavorSupported(stringFlavor)) { String dropStr = (String) t.getTransferData(stringFlavor); files.addAll(EntryTableTransferHandler.getFilesFromDraggedFilesString(dropStr)); } SwingUtilities.invokeLater(() -> { for (Path file : files) { // Find the file's extension, if any: String name = file.toAbsolutePath().toString(); FileHelper.getFileExtension(name).ifPresent(extension -> ExternalFileTypes.getInstance() .getExternalFileTypeByExt(extension).ifPresent(fileType -> { if (droppedFileHandler == null) { droppedFileHandler = new DroppedFileHandler(frame, frame.getCurrentBasePanel()); } droppedFileHandler.handleDroppedfile(name, fileType, entryContainer.getEntry()); })); } }); if (!files.isEmpty()) { // Found some files, return return true; } } catch (IOException ioe) { LOGGER.warn("Failed to read dropped data. ", ioe); } catch (UnsupportedFlavorException | ClassCastException ufe) { LOGGER.warn("Drop type error. ", ufe); } // all supported flavors failed StringBuilder logMessage = new StringBuilder("Cannot transfer input:"); DataFlavor[] inflavs = t.getTransferDataFlavors(); for (DataFlavor inflav : inflavs) { logMessage.append(' ').append(inflav); } LOGGER.warn(logMessage.toString()); return false; } /** * This method is called to query whether the transfer can be imported. * * Will return true for urls, strings, javaFileLists */ @Override public boolean canImport(JComponent comp, DataFlavor[] transferFlavors) { // accept this if any input flavor matches any of our supported flavors for (DataFlavor inflav : transferFlavors) { if (inflav.match(urlFlavor) || inflav.match(stringFlavor) || inflav.match(DataFlavor.javaFileListFlavor)) { return true; } } // nope, never heard of this type return false; } }