/* All programs in this directory and subdirectories are published under the GNU General Public License as described below. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Further information about the GNU GPL is available at: http://www.gnu.org/copyleft/gpl.ja.html */ package net.sf.jabref.groups; 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.awt.event.InputEvent; import java.awt.event.MouseEvent; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import javax.swing.JComponent; import javax.swing.JTable; import javax.swing.TransferHandler; import net.sf.jabref.BasePanel; import net.sf.jabref.Globals; import net.sf.jabref.JabRefFrame; import net.sf.jabref.external.DroppedFileHandler; import net.sf.jabref.external.ExternalFileType; import net.sf.jabref.external.TransferableFileLinkSelection; import net.sf.jabref.gui.MainTable; import net.sf.jabref.gui.MainTableFormat; import net.sf.jabref.imports.ImportMenuItem; import net.sf.jabref.imports.OpenDatabaseAction; import net.sf.jabref.imports.ParserResult; import net.sf.jabref.net.URLDownload; import spl.JabRefDraggedFilesEvent; import spl.listener.PdfImportListener; public class EntryTableTransferHandler extends TransferHandler { protected final MainTable entryTable; protected JabRefFrame frame; private BasePanel panel; protected DataFlavor urlFlavor; protected DataFlavor stringFlavor; private static final PdfImportListener pdfImportListener = new PdfImportListener(); protected static boolean DROP_ALLOWED = true; /** * Construct the transfer handler. * * @param entryTable * The table this transfer handler should operate on. This * argument is allowed to equal * @null, in which case the transfer handler can assume that it works for a * JabRef instance with no databases open, attached to the empty * tabbed pane. * @param frame * The JabRefFrame instance. * @param panel * The BasePanel this transferhandler works for. */ public EntryTableTransferHandler(MainTable entryTable, JabRefFrame frame, BasePanel panel) { this.entryTable = entryTable; this.frame = frame; this.panel = panel; stringFlavor = DataFlavor.stringFlavor; try { urlFlavor = new DataFlavor("application/x-java-url; class=java.net.URL"); } catch (ClassNotFoundException e) { Globals.logger("Unable to configure drag and drop for main table"); e.printStackTrace(); } //DOCEAR - add default spl pdfImportListener // frame.removeJabRefEventListener(pdfImportListener); frame.addJabRefEventListener(pdfImportListener); } /** * Overriden to indicate which types of drags are supported (only LINK). * * @override */ public int getSourceActions(JComponent c) { return DnDConstants.ACTION_LINK; } /** * This method is called when dragging stuff *from* the table. */ public Transferable createTransferable(JComponent c) { if (!draggingFile) { /* so we can assume it will never be called if entryTable==null: */ return new TransferableEntrySelection(entryTable.getSelectedEntries()); } else { draggingFile = false; return (new TransferableFileLinkSelection (panel, entryTable.getSelectedEntries()));//.getTransferable(); } } /** * This method is called when stuff is drag to the component. * * Imports the dropped URL or plain text as a new entry in the current * database. * */ 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: int dropRow = -1; if (comp instanceof JTable) { dropRow = ((JTable) comp).getSelectedRow(); } try { // This flavor is used for dragged file links in Windows: if (t.isDataFlavorSupported(DataFlavor.javaFileListFlavor)) { // JOptionPane.showMessageDialog(null, "Received // javaFileListFlavor"); @SuppressWarnings("unchecked") List<File> l = (List<File>) t.getTransferData(DataFlavor.javaFileListFlavor); return handleDraggedFiles(l, dropRow); } // Done by MrDlib if (t.isDataFlavorSupported(urlFlavor)) { URL dropLink = (URL) t.getTransferData(urlFlavor); return handleDropTransfer(dropLink, dropRow); } if (t.isDataFlavorSupported(stringFlavor)) { // JOptionPane.showMessageDialog(null, "Received stringFlavor: // "+dropStr); String dropStr = (String) t.getTransferData(stringFlavor); return handleDropTransfer(dropStr, dropRow); } } catch (IOException ioe) { System.err.println("failed to read dropped data: " + ioe.toString()); } catch (UnsupportedFlavorException ufe) { System.err.println("drop type error: " + ufe.toString()); } // all supported flavors failed System.err.println("can't transfer input: "); DataFlavor inflavs[] = t.getTransferDataFlavors(); for (int i = 0; i < inflavs.length; i++) { System.out.println(" " + inflavs[i].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) { if (!DROP_ALLOWED) return false; // accept this if any input flavor matches any of our supported flavors for (int i = 0; i < transferFlavors.length; i++) { DataFlavor inflav = transferFlavors[i]; if (inflav != null && (inflav.match(urlFlavor) || inflav.match(stringFlavor) || inflav.match(DataFlavor.javaFileListFlavor))) return true; } // System.out.println("drop type forbidden"); // nope, never heard of this type return false; } boolean draggingFile = false; public void exportAsDrag(JComponent comp, InputEvent e, int action) { /* TODO: add support for dragging file link from table icon into other apps */ if (e instanceof MouseEvent) { MouseEvent me = (MouseEvent)e; int col = entryTable.columnAtPoint(me.getPoint()); String[] res = entryTable.getIconTypeForColumn(col); if (res == null) { super.exportAsDrag(comp, e, DnDConstants.ACTION_LINK); return; } // We have an icon column: if (res == MainTableFormat.FILE) { System.out.println("dragging file"); draggingFile = true; } } super.exportAsDrag(comp, e, DnDConstants.ACTION_LINK); } protected void exportDone(JComponent source, Transferable data, int action) { // default implementation is OK super.exportDone(source, data, action); } public void exportToClipboard(JComponent comp, Clipboard clip, int action) { // default implementation is OK super.exportToClipboard(comp, clip, action); } // add-ons ----------------------- protected boolean handleDropTransfer(String dropStr, final int dropRow) throws IOException { if (dropStr.startsWith("file:")) { // This appears to be a dragged file link and not a reference // format. Check if we can map this to a set of files: if (handleDraggedFilenames(dropStr, dropRow)) return true; // If not, handle it in the normal way... } else if (dropStr.startsWith("http:")) { // This is the way URL links are received on OS X and KDE (Gnome?): URL url = new URL(dropStr); // JOptionPane.showMessageDialog(null, "Making URL: // "+url.toString()); return handleDropTransfer(url, dropRow); } File tmpfile = java.io.File.createTempFile("jabrefimport", ""); tmpfile.deleteOnExit(); FileWriter fw = new FileWriter(tmpfile); fw.write(dropStr); fw.close(); // System.out.println("importing from " + tmpfile.getAbsolutePath()); ImportMenuItem importer = new ImportMenuItem(frame, false); importer.automatedImport(new String[] { tmpfile.getAbsolutePath() }); return true; } /** * Translate a String describing a set of files or URLs dragged into JabRef * into a List of File objects, taking care of URL special characters. * * @param s * String describing a set of files or URLs dragged into JabRef * @return a List<File> containing the individual file objects. * */ public static List<File> getFilesFromDraggedFilesString(String s) { // Split into lines: String[] lines = s.replaceAll("\r", "").split("\n"); List<File> files = new ArrayList<File>(); for (int i = 0; i < lines.length; i++) { String line = lines[i]; // Try to use url.toURI() to translate URL specific sequences like %20 into // standard characters: File fl = null; try { URL url = new URL(line); fl = new File(url.toURI()); } catch(URISyntaxException e) { e.printStackTrace(); } catch (MalformedURLException e) { e.printStackTrace(); } // Unless an exception was thrown, we should have the sanitized path: if (fl != null) line = fl.getPath(); else if (line.startsWith("file:")) line = line.substring(5); else continue; // Under Gnome, the link is given as file:///...., so we // need to strip the extra slashes: if (line.startsWith("//")) line = line.substring(2); File f = new File(line); if (f.exists()) { files.add(f); } } return files; } /** * Handle a String describing a set of files or URLs dragged into JabRef. * * @param s * String describing a set of files or URLs dragged into JabRef * @param dropRow The row in the table where the files were dragged. * @return success status for the operation * */ private boolean handleDraggedFilenames(String s, final int dropRow) { return handleDraggedFiles(getFilesFromDraggedFilesString(s), dropRow); } /** * Handle a List containing File objects for a set of files to import. * * @param files * A List containing File instances pointing to files. * @param dropRow @param dropRow The row in the table where the files were dragged. * @return success status for the operation */ private boolean handleDraggedFiles(List<File> files, final int dropRow) { final String[] fileNames = new String[files.size()]; int i = 0; for (Iterator<File> iterator = files.iterator(); iterator.hasNext();) { File file = iterator.next(); fileNames[i] = file.getAbsolutePath(); i++; } // Try to load bib files normally, and import the rest into the current database. // This process must be spun off into a background thread: // new Thread(new Runnable() { // public void run() { // // Done by MrDlib // final String[] newfileNames = new PdfImporter(frame, panel, entryTable, dropRow).importPdfFiles(fileNames); // if(newfileNames.length > 0){ // loadOrImportFiles(newfileNames, dropRow); // } // //loadOrImportFiles(fileNames, dropRow); // // Done by MrDlib // } // }).start(); JabRefDraggedFilesEvent event = new JabRefDraggedFilesEvent(frame, panel, entryTable, dropRow, fileNames, this); frame.dispatchJabRefEvent(event); if(event.consumed()) { return true; } loadOrImportFiles(fileNames, dropRow); return true; } //DOCEAR - changed visibility /** * Take a set of filenames. Those with names indicating bib files are opened * as such if possible. All other files we will attempt to import into the * current database. * * @param fileNames * The names of the files to open. * @param dropRow success status for the operation */ public void loadOrImportFiles(String[] fileNames, int dropRow) { OpenDatabaseAction openAction = new OpenDatabaseAction(frame, false); ArrayList<String> notBibFiles = new ArrayList<String>(); String encoding = Globals.prefs.get("defaultEncoding"); for (int i = 0; i < fileNames.length; i++) { // Find the file's extension, if any: String extension = ""; ExternalFileType fileType = null; int index = fileNames[i].lastIndexOf('.'); if ((index >= 0) && (index < fileNames[i].length())) { extension = fileNames[i].substring(index + 1).toLowerCase(); fileType = Globals.prefs.getExternalFileTypeByExt(extension); } if (extension.equals("bib")) { File f = new File(fileNames[i]); try { ParserResult pr = OpenDatabaseAction.loadDatabase(f, encoding); if ((pr == null) || (pr == ParserResult.INVALID_FORMAT)) { notBibFiles.add(fileNames[i]); } else { openAction.addNewDatabase(pr, f, true); frame.getFileHistory().newFile(fileNames[i]); } } catch (IOException e) { notBibFiles.add(fileNames[i]); // No error message, since we want to try importing the // file? // // Util.showQuickErrorDialog(frame, Globals.lang("Open // database"), e); } continue; } /* * This is a linkable file. If the user dropped it on an entry, we * should offer options for autolinking to this files: * * TODO we should offer an option to highlight the row the user is on too. */ if (fileType != null && dropRow >= 0) { /* * TODO: need to signal if this is a local or autodownloaded * file */ boolean local = true; /* * TODO: make this an instance variable? */ DroppedFileHandler dfh = new DroppedFileHandler(frame, panel); dfh.handleDroppedfile(fileNames[i], fileType, local, entryTable, dropRow); continue; } /* if (extension.equals("pdf")) { Collection c; try { c = XMPUtil.readXMP(fileNames[i]); } catch (IOException e1) { c = null; frame.output(Globals.lang("No XMP metadata found in " + fileNames[i])); } if (c != null && c.size() > 0) { Iterator it = c.iterator(); BasePanel panel = frame.basePanel(); if (panel == null) { // // Create a new, empty, database. BibtexDatabase database = new BibtexDatabase(); frame.addTab(database, null, null, Globals.prefs.get("defaultEncoding"), true); frame.output(Globals.lang("New database created.")); panel = frame.basePanel(); } BibtexDatabase database = frame.basePanel().database(); NamedCompound ce = new NamedCompound(Globals.lang("Drog PDF")); while (it.hasNext()) { BibtexEntry e = (BibtexEntry) it.next(); try { e.setId(Util.createNeutralId()); database.insertEntry(e); ce.addEdit(new UndoableInsertEntry(database, e, panel)); } catch (Exception e2) { // Should not happen? } } ce.end(); panel.undoManager.addEdit(ce); panel.markBaseChanged(); continue; } } */ notBibFiles.add(fileNames[i]); } if (notBibFiles.size() > 0) { String[] toImport = new String[notBibFiles.size()]; notBibFiles.toArray(toImport); // Import into new if entryTable==null, otherwise into current // database: ImportMenuItem importer = new ImportMenuItem(frame, (entryTable == null)); importer.automatedImport(toImport); } } protected boolean handleDropTransfer(URL dropLink, int dropRow) throws IOException { File tmpfile = java.io.File.createTempFile("jabrefimport", ""); tmpfile.deleteOnExit(); // System.out.println("Import url: " + dropLink.toString()); // System.out.println("Temp file: "+tmpfile.getAbsolutePath()); new URLDownload(entryTable, dropLink, tmpfile).download(); // Import into new if entryTable==null, otherwise into current database: ImportMenuItem importer = new ImportMenuItem(frame, (entryTable == null)); importer.automatedImport(new String[] { tmpfile.getAbsolutePath() }); return true; } }