1 package net.sf.jabref.external;
4 import java.io.IOException;
5 import java.util.Iterator;
8 import javax.swing.ButtonGroup;
9 import javax.swing.JCheckBox;
10 import javax.swing.JLabel;
11 import javax.swing.JOptionPane;
12 import javax.swing.JPanel;
13 import javax.swing.JRadioButton;
14 import javax.swing.event.ChangeEvent;
15 import javax.swing.event.ChangeListener;
17 import net.sf.jabref.BasePanel;
18 import net.sf.jabref.BibtexEntry;
19 import net.sf.jabref.Globals;
20 import net.sf.jabref.JabRefFrame;
21 import net.sf.jabref.KeyCollisionException;
22 import net.sf.jabref.Util;
23 import net.sf.jabref.gui.MainTable;
24 import net.sf.jabref.undo.NamedCompound;
25 import net.sf.jabref.undo.UndoableFieldChange;
26 import net.sf.jabref.util.XMPUtil;
28 import com.jgoodies.forms.builder.DefaultFormBuilder;
29 import com.jgoodies.forms.layout.FormLayout;
32 * This class holds the functionality of autolinking to a file that's dropped
35 * Options for handling the files are:
37 * 1) Link to the file in its current position (disabled if the file is remote)
39 * 2) Copy the file to ??? directory, rename after bibtex key, and extension
41 * 3) Move the file to ??? directory, rename after bibtex key, and extension
43 public class DroppedFileHandler {
44 private JabRefFrame frame;
46 private BasePanel panel;
48 private JRadioButton linkInPlace = new JRadioButton(), copyRadioButton = new JRadioButton(),
49 moveRadioButton = new JRadioButton();
51 private JLabel destDirLabel = new JLabel();
53 private JCheckBox renameCheckBox = new JCheckBox();
55 private JPanel optionsPanel = new JPanel();
57 public DroppedFileHandler(JabRefFrame frame, BasePanel panel) {
62 ButtonGroup grp = new ButtonGroup();
64 grp.add(copyRadioButton);
65 grp.add(moveRadioButton);
66 copyRadioButton.setSelected(true);
68 DefaultFormBuilder builder = new DefaultFormBuilder(optionsPanel, new FormLayout(
70 builder.append(linkInPlace);
71 builder.append(destDirLabel);
72 builder.append(copyRadioButton);
73 builder.append(moveRadioButton);
74 builder.append(renameCheckBox);
78 * Offer copy/move/linking options for a dragged external file. Perform the
79 * chosen operation, if any.
82 * The name of the dragged file.
84 * The FileType associated with the file.
86 * Indicate whether this is a local file, or a remote file copied
87 * to a local temporary file.
89 * The MainTable the file was dragged to.
91 * The row where the file was dropped.
93 public void handleDroppedfile(String fileName, ExternalFileType fileType, boolean localFile,
94 MainTable mainTable, int dropRow) {
96 NamedCompound edits = new NamedCompound(Globals.lang("Drop %0", fileType.extension));
98 if (tryXmpImport(fileName, fileType, localFile, mainTable, edits)) {
99 panel.undoManager.addEdit(edits);
103 BibtexEntry entry = mainTable.getEntryAt(dropRow);
106 boolean newEntry = false;
107 boolean rename = entry.getCiteKey() != null && entry.getCiteKey().length() > 0;
108 String citeKeyOrReason = (rename ? entry.getCiteKey() : Globals.lang("Entry has no citekey"));
109 int reply = showLinkMoveCopyRenameDialog(Globals.lang("Link to file %0", fileName),
110 fileType, rename, citeKeyOrReason, newEntry, false);
112 if (reply != JOptionPane.OK_OPTION)
116 * Ok, we're ready to go. See first if we need to do a file copy before
119 boolean success = true;
122 if (linkInPlace.isSelected()) {
123 destFilename = fileName;
125 destFilename = (renameCheckBox.isSelected() ? entry.getCiteKey() + "." + fileType.extension : fileName);
126 if (copyRadioButton.isSelected()) {
127 success = doCopy(fileName, fileType, destFilename, edits);
128 } else if (moveRadioButton.isSelected()) {
129 success = doRename(fileName, fileType, destFilename, edits);
134 doLink(entry, fileType, destFilename, edits);
135 panel.markBaseChanged();
138 panel.undoManager.addEdit(edits);
142 private boolean tryXmpImport(String fileName, ExternalFileType fileType, boolean localFile,
143 MainTable mainTable, NamedCompound edits) {
145 if (!fileType.extension.equals("pdf")) {
149 List xmpEntriesInFile = null;
151 xmpEntriesInFile = XMPUtil.readXMP(fileName);
152 } catch (Exception e) {
156 if ((xmpEntriesInFile == null) || (xmpEntriesInFile.size() == 0)) {
160 JLabel confirmationMessage = new JLabel(
162 .lang("The PDF contains one or several bibtex-records.\nDo you want to import these as new entries into the current database?"));
164 int reply = JOptionPane.showConfirmDialog(frame, confirmationMessage, Globals.lang(
165 "XMP metadata found in PDF: %0", fileName), JOptionPane.YES_NO_CANCEL_OPTION,
166 JOptionPane.QUESTION_MESSAGE);
168 if (reply == JOptionPane.CANCEL_OPTION) {
169 return true; // The user canceled thus that we are done.
171 if (reply == JOptionPane.NO_OPTION) {
175 // reply == JOptionPane.YES_OPTION)
178 * TODO Extract Import functionality from ImportMenuItem then we could
181 * ImportMenuItem importer = new ImportMenuItem(frame, (mainTable ==
182 * null), new PdfXmpImporter());
184 * importer.automatedImport(new String[] { fileName });
187 boolean isSingle = xmpEntriesInFile.size() == 1;
188 BibtexEntry single = (isSingle ? (BibtexEntry) xmpEntriesInFile.get(0) : null);
190 reply = showLinkMoveCopyRenameDialog(Globals.lang("Link to PDF %0", fileName), fileType,
191 isSingle, (isSingle ? single.getCiteKey() : Globals.lang("Cannot rename for several entries.")),
194 boolean success = true;
198 if (linkInPlace.isSelected()) {
199 destFilename = fileName;
201 if (renameCheckBox.isSelected()) {
202 destFilename = fileName;
204 destFilename = single.getCiteKey() + "." + fileType.extension;
207 if (copyRadioButton.isSelected()) {
208 success = doCopy(fileName, fileType, destFilename, edits);
209 } else if (moveRadioButton.isSelected()) {
210 success = doRename(fileName, fileType, destFilename, edits);
215 Iterator it = xmpEntriesInFile.iterator();
217 while (it.hasNext()) {
219 BibtexEntry entry = (BibtexEntry) it.next();
220 entry.setId(Util.createNeutralId());
221 panel.getDatabase().insertEntry(entry);
222 doLink(entry, fileType, destFilename, edits);
223 } catch (KeyCollisionException ex) {
227 panel.markBaseChanged();
228 panel.updateEntryEditorIfShowing();
233 public int showLinkMoveCopyRenameDialog(String dialogTitle, ExternalFileType fileType,
234 final boolean allowRename, String citekeyOrReason, boolean newEntry,
235 final boolean multipleEntries) {
237 String dir = panel.metaData().getFileDirectory(fileType.getFieldName());
238 if ((dir == null) || !(new File(dir)).exists()) {
239 destDirLabel.setText(Globals.lang("%0 directory is not set or does not exist!", fileType.getName()));
240 copyRadioButton.setEnabled(false);
241 moveRadioButton.setEnabled(false);
242 linkInPlace.setSelected(true);
244 destDirLabel.setText(Globals.lang("%0 directory is '%1':", fileType.getName(), dir));
245 copyRadioButton.setEnabled(true);
246 moveRadioButton.setEnabled(true);
249 ChangeListener cl = new ChangeListener() {
250 public void stateChanged(ChangeEvent arg0) {
251 renameCheckBox.setEnabled(!linkInPlace.isSelected()
252 && allowRename && (!multipleEntries));
256 if (multipleEntries) {
257 linkInPlace.setText(Globals
258 .lang("Leave files in their current directory."));
259 copyRadioButton.setText(Globals.lang("Copy files to %0.", fileType
262 moveRadioButton.setText(Globals.lang("Move files to %0.", fileType
265 linkInPlace.setText(Globals
266 .lang("Leave file in its current directory."));
267 copyRadioButton.setText(Globals.lang("Copy file to %0.", fileType
269 moveRadioButton.setText(Globals.lang("Move file to %0.", fileType
273 renameCheckBox.setText(Globals.lang("Rename to match citekey") + ": " + citekeyOrReason);
274 linkInPlace.addChangeListener(cl);
275 cl.stateChanged(new ChangeEvent(linkInPlace));
278 return JOptionPane.showConfirmDialog(frame, optionsPanel, dialogTitle,
279 JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
281 linkInPlace.removeChangeListener(cl);
286 * Make a extension to the file.
289 * The entry to extension from.
291 * The FileType associated with the file.
293 * The path to the file.
295 * An NamedCompound action this action is to be added to. If none
296 * is given, the edit is added to the panel's undoManager.
298 private void doLink(BibtexEntry entry, ExternalFileType fileType, String filename,
299 NamedCompound edits) {
301 UndoableFieldChange edit = new UndoableFieldChange(entry, fileType.getFieldName(), entry
302 .getField(fileType.getFieldName()), filename);
303 entry.setField(fileType.getFieldName(), filename);
306 panel.undoManager.addEdit(edit);
313 * Move the given file to the base directory for its file type, and rename
314 * it to the given filename.
317 * The name of the source file.
319 * The FileType associated with the file.
320 * @param destFilename
321 * The destination filename.
323 * TODO we should be able to undo this action
324 * @return true if the operation succeeded.
326 private boolean doRename(String fileName, ExternalFileType fileType, String destFilename,
327 NamedCompound edits) {
328 String dir = panel.metaData().getFileDirectory(fileType.getFieldName());
329 if ((dir == null) || !(new File(dir)).exists()) {
330 // OOps, we don't know which directory to put it in, or the given
331 // dir doesn't exist....
332 // This should not happen!!
335 destFilename = new File(destFilename).getName();
336 File f = new File(fileName);
337 File destFile = new File(new StringBuffer(dir).append(System.getProperty("file.separator"))
338 .append(destFilename).toString());
339 f.renameTo(destFile);
344 * Copy the given file to the base directory for its file type, and give it
348 * The name of the source file.
350 * The FileType associated with the file.
352 * The destination filename. An existing path-component will be removed.
354 * TODO we should be able to undo this!
357 private boolean doCopy(String fileName, ExternalFileType fileType, String toFile,
358 NamedCompound edits) {
360 String dir = panel.metaData().getFileDirectory(fileType.getFieldName());
361 if ((dir == null) || !(new File(dir)).exists()) {
362 // OOps, we don't know which directory to put it in, or the given
363 // dir doesn't exist....
364 System.out.println("dir: " + dir + "\t ext: " + fileType.getExtension());
367 toFile = new File(toFile).getName();
369 File destFile = new File(new StringBuffer(dir).append(System.getProperty("file.separator"))
370 .append(toFile).toString());
371 if (destFile.equals(new File(fileName))){
372 // File is already in the correct position. Don't override!
376 if (destFile.exists()) {
377 int answer = JOptionPane.showConfirmDialog(frame, "'" + destFile.getPath() + "' "
378 + Globals.lang("exists.Overwrite?"), Globals.lang("File exists"),
379 JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
380 if (answer == JOptionPane.NO_OPTION)
384 Util.copyFile(new File(fileName), destFile, true);
385 } catch (IOException e) {