package net.sf.jabref.gui; import java.awt.BorderLayout; import java.awt.event.*; import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.regex.Pattern; import javax.swing.*; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import net.sf.jabref.GUIGlobals; import net.sf.jabref.Globals; import net.sf.jabref.JabRefFrame; import net.sf.jabref.MetaData; import net.sf.jabref.Util; import net.sf.jabref.external.ConfirmCloseFileListEntryEditor; import net.sf.jabref.external.ExternalFileType; import net.sf.jabref.external.UnknownExternalFileType; import com.jgoodies.forms.builder.ButtonBarBuilder; import com.jgoodies.forms.builder.DefaultFormBuilder; import com.jgoodies.forms.layout.FormLayout; /** * This class produces a dialog box for editing a single file link from a Bibtex entry. * * The information to be edited includes the file description, the link itself and the * file type. The dialog also includes convenience buttons for quick linking. * * For use when downloading files, this class also offers a progress bar and a "Downloading..." * label that can be hidden when the download is complete. */ public class FileListEntryEditor { JDialog diag; JTextField link = new JTextField(), description = new JTextField(); JButton ok = new JButton(Globals.lang("Ok")), cancel = new JButton(Globals.lang("Cancel")), open = new JButton(Globals.lang("Open")); JComboBox types; JProgressBar prog = new JProgressBar(JProgressBar.HORIZONTAL); JLabel downloadLabel = new JLabel(Globals.lang("Downloading...")); ConfirmCloseFileListEntryEditor externalConfirm = null; private AbstractAction okAction; private FileListEntry entry; private MetaData metaData; private boolean okPressed = false, okDisabledExternally = false, openBrowseWhenShown = false, dontOpenBrowseUntilDisposed = false; public static Pattern remoteLinkPattern = Pattern.compile("[a-z]+://.*"); public FileListEntryEditor(JabRefFrame frame, FileListEntry entry, boolean showProgressBar, boolean showOpenButton, MetaData metaData) { this.entry = entry; this.metaData = metaData; okAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { // If OK button is disabled, ignore this event: if (!ok.isEnabled()) return; // If necessary, ask the external confirm object whether we are ready to close. if (externalConfirm != null) { // Construct an updated FileListEntry: FileListEntry testEntry = new FileListEntry("", "", null); storeSettings(testEntry); if (!externalConfirm.confirmClose(testEntry)) return; } diag.dispose(); storeSettings(FileListEntryEditor.this.entry); okPressed = true; } }; types = new JComboBox(); types.addItemListener(new ItemListener() { public void itemStateChanged(ItemEvent itemEvent) { if (!okDisabledExternally) ok.setEnabled(types.getSelectedItem() != null); } }); DefaultFormBuilder builder = new DefaultFormBuilder(new FormLayout ("left:pref, 4dlu, fill:150dlu, 4dlu, fill:pref, 4dlu, fill:pref", "")); builder.append(Globals.lang("Link")); builder.append(link); final BrowseListener browse = new BrowseListener(frame.getFrame(), link); final JButton browseBut = new JButton(Globals.lang("Browse")); browseBut.addActionListener(browse); builder.append(browseBut); if (showOpenButton) builder.append(open); builder.nextLine(); builder.append(Globals.lang("Description")); builder.append(description, 3); builder.getPanel().setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); builder.nextLine(); builder.append(Globals.lang("File type")); builder.append(types, 3); if (showProgressBar) { builder.nextLine(); builder.append(downloadLabel); builder.append(prog, 3); } ButtonBarBuilder bb = new ButtonBarBuilder(); bb.addGlue(); //bb.addGridded(open); //bb.addRelatedGap(); bb.addRelatedGap(); bb.addGridded(ok); bb.addGridded(cancel); bb.addGlue(); ok.addActionListener(okAction); // Add OK action to the two text fields to simplify entering: link.addActionListener(okAction); description.addActionListener(okAction); open.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent actionEvent) { openFile(); } }); AbstractAction cancelAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { diag.dispose(); } }; cancel.addActionListener(cancelAction); // Key bindings: ActionMap am = builder.getPanel().getActionMap(); InputMap im = builder.getPanel().getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(Globals.prefs.getKey("Close dialog"), "close"); am.put("close", cancelAction); link.getDocument().addDocumentListener(new DocumentListener() { public void insertUpdate(DocumentEvent documentEvent) { checkExtension(); } public void removeUpdate(DocumentEvent documentEvent) { } public void changedUpdate(DocumentEvent documentEvent) { checkExtension(); } }); diag = new JDialog(frame.getFrame(), Globals.lang("Edit file link"), true); diag.getContentPane().add(builder.getPanel(), BorderLayout.CENTER); diag.getContentPane().add(bb.getPanel(), BorderLayout.SOUTH); diag.pack(); Util.placeDialog(diag, frame); diag.addWindowListener(new WindowAdapter() { public void windowActivated(WindowEvent event) { if (openBrowseWhenShown && !dontOpenBrowseUntilDisposed) { dontOpenBrowseUntilDisposed = true; SwingUtilities.invokeLater(new Runnable() { public void run() { browse.actionPerformed(new ActionEvent(browseBut, 0, "")); } }); } } @Override public void windowClosed(WindowEvent event) { dontOpenBrowseUntilDisposed = false; } }); setValues(entry); } private void checkExtension() { if ((types.getSelectedIndex() == -1) && (link.getText().trim().length() > 0)) { // Check if this looks like a remote link: if (remoteLinkPattern.matcher(link.getText()).matches()) { ExternalFileType type = Globals.prefs.getExternalFileTypeByExt("html"); if (type != null) { types.setSelectedItem(type); return; } } // Try to guess the file type: String theLink = link.getText().trim(); ExternalFileType type = Globals.prefs.getExternalFileTypeForName(theLink); if (type != null) types.setSelectedItem(type); } } public void openFile() { ExternalFileType type = (ExternalFileType)types.getSelectedItem(); if (type != null) try { Util.openExternalFileAnyFormat(metaData, link.getText(), type); } catch (IOException e) { e.printStackTrace(); } } public void setExternalConfirm(ConfirmCloseFileListEntryEditor eC) { this.externalConfirm = eC; } public void setOkEnabled(boolean enabled) { okDisabledExternally = !enabled; ok.setEnabled(enabled); } public JProgressBar getProgressBar() { return prog; } public JLabel getProgressBarLabel() { return downloadLabel; } public void setEntry(FileListEntry entry) { this.entry = entry; setValues(entry); } public void setVisible(boolean visible, boolean openBrowse) { openBrowseWhenShown = openBrowse && Globals.prefs.getBoolean("allowFileAutoOpenBrowse"); if (visible) okPressed = false; diag.setVisible(visible); } public boolean isVisible() { return diag != null && diag.isVisible(); } public void setValues(FileListEntry entry) { description.setText(entry.getDescription()); link.setText(entry.getLink()); //if (link.getText().length() > 0) // checkExtension(); types.setModel(new DefaultComboBoxModel(Globals.prefs.getExternalFileTypeSelection())); types.setSelectedIndex(-1); // See what is a reasonable selection for the type combobox: if ((entry.getType() != null) && !(entry.getType() instanceof UnknownExternalFileType)) types.setSelectedItem(entry.getType()); // If the entry has a link but not a file type, try to deduce the file type: else if ((entry.getLink() != null) && (entry.getLink().length() > 0)) { checkExtension(); } } public void storeSettings(FileListEntry entry) { entry.setDescription(description.getText().trim()); // See if we should trim the file link to be relative to the file directory: try { String fileDir = metaData.getFileDirectory(GUIGlobals.FILE_FIELD); if ((fileDir == null) ||(fileDir.equals(""))) { entry.setLink(link.getText().trim()); } else { String canPath = (new File(fileDir)).getCanonicalPath(); File fl = new File(link.getText().trim()); if (fl.isAbsolute()) { String flPath = fl.getCanonicalPath(); if ((flPath.length() > canPath.length()) && (flPath.startsWith(canPath))) { String relFileName = fl.getCanonicalPath().substring(canPath.length()+1); entry.setLink(relFileName); } else entry.setLink(link.getText().trim()); } else entry.setLink(link.getText().trim()); } } catch (java.io.IOException ex) { ex.printStackTrace(); // Don't think this should happen, but set the file link directly as a fallback: entry.setLink(link.getText().trim()); } entry.setType((ExternalFileType)types.getSelectedItem()); } public boolean okPressed() { return okPressed; } class BrowseListener implements ActionListener { private JFrame parent; private JTextField comp; public BrowseListener(JFrame parent, JTextField comp) { this.parent = parent; this.comp = comp; } public void actionPerformed(ActionEvent e) { File initial = new File(comp.getText().trim()); if (comp.getText().trim().length() == 0) { // Nothing in the field. Go to the last file dir used: initial = new File(Globals.prefs.get("fileWorkingDirectory")); } String chosen = FileDialogs.getNewFile(parent, initial, Globals.NONE, JFileChooser.OPEN_DIALOG, false); if (chosen != null) { File newFile = new File(chosen); // Store the directory for next time: Globals.prefs.put("fileWorkingDirectory", newFile.getParent()); // If the file is below the file directory, make the path relative: ArrayList<File> dirs = new ArrayList<File>(); String fileDir = metaData.getFileDirectory(GUIGlobals.FILE_FIELD); if (fileDir != null) dirs.add(new File(fileDir)); if (dirs.size() > 0) { newFile = FileListEditor.relativizePath(newFile, dirs); } comp.setText(newFile.getPath()); comp.requestFocus(); } } } }