package net.sf.jabref.external; import java.awt.Component; import java.awt.GridLayout; import java.awt.dnd.DnDConstants; import java.awt.dnd.DropTarget; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.LinkedList; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JFileChooser; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.SwingUtilities; import javax.xml.transform.TransformerException; import net.sf.jabref.BibtexDatabase; import net.sf.jabref.BibtexEntry; import net.sf.jabref.BibtexFields; import net.sf.jabref.EntryEditor; import net.sf.jabref.FieldEditor; import net.sf.jabref.Globals; import net.sf.jabref.JabRefFrame; import net.sf.jabref.MetaData; import net.sf.jabref.OpenFileFilter; import net.sf.jabref.UrlDragDrop; import net.sf.jabref.Util; import net.sf.jabref.gui.FileDialogs; import net.sf.jabref.net.URLDownload; import net.sf.jabref.util.XMPUtil; /** * Initial Version: * * @author alver * @version Date: May 7, 2005 Time: 7:17:42 PM * * Current Version: * * @author $Author: mortenalver $ * @version $Revision: 2950 $ ($Date: 2009-04-14 20:17:08 +0200 (Di, 14 Apr 2009) $) * */ public class ExternalFilePanel extends JPanel { private static final long serialVersionUID = 3653290879640642718L; private JButton browseBut, download, auto, xmp; private EntryEditor entryEditor; private JabRefFrame frame; private OpenFileFilter off; private BibtexEntry entry; private BibtexDatabase database; private MetaData metaData; public ExternalFilePanel(final String fieldName, final MetaData metaData, final BibtexEntry entry, final FieldEditor editor, final OpenFileFilter off) { this(null, metaData, null, fieldName, off, editor); this.entry = entry; this.entryEditor = null; } public ExternalFilePanel(final JabRefFrame frame, final MetaData metaData, final EntryEditor entryEditor, final String fieldName, final OpenFileFilter off, final FieldEditor editor) { this.frame = frame; this.metaData = metaData; this.off = off; this.entryEditor = entryEditor; setLayout(new GridLayout(2, 2)); browseBut = new JButton(Globals.lang("Browse")); download = new JButton(Globals.lang("Download")); auto = new JButton(Globals.lang("Auto")); xmp = new JButton(Globals.lang("Write XMP")); xmp.setToolTipText(Globals.lang("Write BibtexEntry as XMP-metadata to PDF.")); browseBut.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { browseFile(fieldName, editor); // editor.setText(chosenValue); entryEditor.storeFieldAction.actionPerformed(new ActionEvent(editor, 0, "")); } }); download.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { downLoadFile(fieldName, editor, frame); } }); auto.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { autoSetFile(fieldName, editor); } }); xmp.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent e) { pushXMP(fieldName, editor); } }); add(browseBut); add(download); add(auto); add(xmp); // Add drag and drop support to the field if (editor != null) ((JComponent) editor).setDropTarget(new DropTarget((Component) editor, DnDConstants.ACTION_NONE, new UrlDragDrop(entryEditor, frame, editor))); } /** * Change which entry this panel is operating on. This is used only when * this panel is not attached to an entry editor. */ public void setEntry(BibtexEntry entry, BibtexDatabase database) { this.entry = entry; this.database = database; } public BibtexDatabase getDatabase(){ return (database != null ? database : entryEditor.getDatabase()); } public BibtexEntry getEntry() { return (entry != null ? entry : entryEditor.getEntry()); } protected Object getKey() { return getEntry().getField(BibtexFields.KEY_FIELD); } protected void output(String s) { if (frame != null) frame.output(s); } public void pushXMP(final String fieldName, final FieldEditor editor) { (new Thread() { public void run() { output(Globals.lang("Looking for pdf...")); // Find the default directory for this field type, if any: String dir = metaData.getFileDirectory(fieldName); File file = null; if (dir != null) { File tmp = Util.expandFilename(editor.getText(), new String[] { dir, "." }); if (tmp != null) file = tmp; } if (file == null) { file = new File(editor.getText()); } final File finalFile = file; output(Globals.lang("Writing XMP to '%0'...", finalFile.getName())); try { XMPUtil.writeXMP(finalFile, getEntry(), getDatabase()); output(Globals.lang("Wrote XMP to '%0'.", finalFile.getName())); } catch (IOException e) { JOptionPane.showMessageDialog(editor.getParent(), Globals.lang( "Error writing XMP to file: %0", e.getLocalizedMessage()), Globals .lang("Writing XMP"), JOptionPane.ERROR_MESSAGE); Globals.logger(Globals.lang("Error writing XMP to file: %0", finalFile .getAbsolutePath())); output(Globals.lang("Error writing XMP to file: %0", finalFile.getName())); } catch (TransformerException e) { JOptionPane.showMessageDialog(editor.getParent(), Globals.lang( "Error converting Bibtex to XMP: %0", e.getLocalizedMessage()), Globals .lang("Writing XMP"), JOptionPane.ERROR_MESSAGE); Globals.logger(Globals.lang("Error while converting BibtexEntry to XMP %0", finalFile.getAbsolutePath())); output(Globals.lang("Error converting XMP to '%0'...", finalFile.getName())); } } }).start(); } public void browseFile(final String fieldName, final FieldEditor editor) { String directory = metaData.getFileDirectory(fieldName); if ((directory != null) && directory.equals("")) directory = null; String dir = editor.getText(), retVal = null; if ((directory == null) || !(new File(dir)).isAbsolute()) { if (directory != null) dir = directory; else dir = Globals.prefs.get(fieldName + Globals.FILETYPE_PREFS_EXT, ""); } String chosenFile = FileDialogs.getNewFile(frame.getFrame(), new File(dir), "." + fieldName, JFileChooser.OPEN_DIALOG, false); if (chosenFile != null) { File newFile = new File(chosenFile); String position = newFile.getParent(); if ((directory != null) && position.startsWith(directory)) { // Construct path relative to pdf base dir String relPath = position.substring(directory.length(), position.length()) + File.separator + newFile.getName(); // Remove leading path separator if (relPath.startsWith(File.separator)) { relPath = relPath.substring(File.separator.length(), relPath.length()); // Set relative path as field value } retVal = relPath; } else retVal = newFile.getPath(); editor.setText(retVal); Globals.prefs.put(fieldName + Globals.FILETYPE_PREFS_EXT, newFile.getPath()); } } public void downLoadFile(final String fieldName, final FieldEditor fieldEditor, final Component parent) { final String res = JOptionPane.showInputDialog(parent, Globals .lang("Enter URL to download")); if (res == null || res.trim().length() == 0) return; /* * If this panel belongs in an entry editor, note which entry is * currently shown: */ final BibtexEntry targetEntry; if (entryEditor != null) targetEntry = entryEditor.getEntry(); else targetEntry = entry; (new Thread() { public String getPlannedFileName(String res) { String suffix = off.getSuffix(res); if (suffix == null) suffix = "." + fieldName.toLowerCase(); String plannedName = null; if (getKey() != null) plannedName = getKey() + suffix; else { plannedName = JOptionPane.showInputDialog(parent, Globals .lang("BibTeX key not set. Enter a name for the downloaded file")); if (plannedName != null && !off.accept(plannedName)) plannedName += suffix; } /* * [ 1548875 ] download pdf produces unsupported filename * * http://sourceforge.net/tracker/index.php?func=detail&aid=1548875&group_id=92314&atid=600306 * */ if (Globals.ON_WIN) { plannedName = plannedName.replaceAll( "\\?|\\*|\\<|\\>|\\||\\\"|\\:|\\.$|\\[|\\]", ""); } else if (Globals.ON_MAC) { plannedName = plannedName.replaceAll(":", ""); } return plannedName; } public void run() { String originalText = fieldEditor.getText(); fieldEditor.setEnabled(false); boolean updateEditor = true; try { fieldEditor.setText(Globals.lang("Downloading...")); output(Globals.lang("Downloading...")); String plannedName = getPlannedFileName(res); // Find the default directory for this field type: String directory = metaData.getFileDirectory(fieldName); if (!new File(directory).exists()) { JOptionPane.showMessageDialog(parent, Globals.lang( "Could not find directory for %0-files: %1", fieldName, directory), Globals.lang("Download file"), JOptionPane.ERROR_MESSAGE); Globals.logger(Globals.lang("Could not find directory for %0-files: %1", fieldName, directory)); return; } File file = new File(new File(directory), plannedName); URL url = new URL(res); URLDownload udl = new URLDownload(parent, url, file); try { udl.download(); } catch (IOException e2) { JOptionPane.showMessageDialog(parent, Globals.lang("Invalid URL")+": " + e2.getMessage(), Globals.lang("Download file"), JOptionPane.ERROR_MESSAGE); Globals.logger("Error while downloading " + url.toString()); return; } output(Globals.lang("Download completed")); String textToSet = file.getPath(); if (textToSet.startsWith(directory)) { // Construct path relative to pdf base dir textToSet = textToSet.substring(directory.length(), textToSet.length()); // Remove leading path separator if (textToSet.startsWith(File.separator)) { textToSet = textToSet.substring(File.separator.length()); } } /* * Check if we should update the editor text field, or * update the target entry directly: */ if (entryEditor == null || entryEditor.getEntry() != targetEntry) { /* * Editor has probably changed to show a different * entry. So we must update the target entry directly * and not set the text of the editor. */ targetEntry.setField(fieldName, textToSet); fieldEditor.setText(textToSet); fieldEditor.setEnabled(true); updateEditor = false; } else { /* * Need to set the fieldEditor first before running * updateField-Action, because otherwise we might get a * race condition. * * (Hopefully a) Fix for: [ 1545601 ] downloading pdf * corrupts pdf field text * * http://sourceforge.net/tracker/index.php?func=detail&aid=1545601&group_id=92314&atid=600306 */ fieldEditor.setText(textToSet); fieldEditor.setEnabled(true); updateEditor = false; SwingUtilities.invokeLater(new Thread() { public void run() { entryEditor.updateField(fieldEditor); } }); } } catch (MalformedURLException e1) { JOptionPane.showMessageDialog(parent, Globals.lang("Invalid URL"), Globals .lang("Download file"), JOptionPane.ERROR_MESSAGE); } finally { // If stuff goes wrong along the road, put back original // value if (updateEditor) { fieldEditor.setText(originalText); fieldEditor.setEnabled(true); } } } }).start(); } /** * Starts a thread that searches the external file directory for the given * field name, including subdirectories, and looks for files named after the * current entry's bibtex key. Returns a reference to the thread for callers * that may want to wait for the thread to finish (using join()). * * @param fieldName * The field to set. * @param editor * An EntryEditor instance where to set the value found. * @return A reference to the Thread that performs the operation. */ public Thread autoSetFile(final String fieldName, final FieldEditor editor) { Object o = getKey(); if ((o == null) || (Globals.prefs.get(fieldName + "Directory") == null)) { output(Globals.lang("You must set both BibTeX key and %0 directory", fieldName .toUpperCase()) + "."); return null; } output(Globals.lang("Searching for %0 file", fieldName.toUpperCase()) + " '" + o + "." + fieldName + "'..."); Thread t = (new Thread() { public void run() { /* * Find the following directories to look in for: * * default directory for this field type. * * directory of bibtex-file. // NOT POSSIBLE at the moment. * * JabRef-directory. */ LinkedList<String> list = new LinkedList<String>(); list.add(metaData.getFileDirectory(fieldName)); /* * File fileOfDb = frame.basePanel().file(); if (fileOfDb != * null){ list.add(fileOfDb.getParentFile().getPath()); } */ list.add("."); String found = Util.findPdf(getEntry(), fieldName, list .toArray(new String[list.size()]));// , off); // To activate findFile: // String found = Util.findFile(getEntry(), null, dir, // ".*[bibtexkey].*"); if (found != null) { editor.setText(found); if (entryEditor != null) entryEditor.updateField(editor); output(Globals.lang("%0 field set", fieldName.toUpperCase()) + "."); } else { output(Globals.lang("No %0 found", fieldName.toUpperCase()) + "."); } } }); t.start(); return t; } }