9e1805175701e7ce6d897ec7e93a47c325903771
[debian/jabref.git] / src / java / net / sf / jabref / external / SynchronizeFileField.java
1 package net.sf.jabref.external;
2
3 import net.sf.jabref.*;
4 import net.sf.jabref.gui.*;
5 import net.sf.jabref.undo.NamedCompound;
6 import net.sf.jabref.undo.UndoableFieldChange;
7
8 import javax.swing.*;
9 import java.util.Collection;
10 import java.util.ArrayList;
11 import java.util.Set;
12 import java.util.HashSet;
13 import java.io.File;
14 import java.awt.event.ActionListener;
15 import java.awt.event.ActionEvent;
16 import java.awt.*;
17
18 import com.jgoodies.forms.layout.FormLayout;
19 import com.jgoodies.forms.builder.DefaultFormBuilder;
20 import com.jgoodies.forms.builder.ButtonBarBuilder;
21
22 /**
23  * This action goes through all selected entries in the BasePanel, and attempts to autoset the
24  * given external file (pdf, ps, ...) based on the same algorithm used for the "Auto" button in
25  * EntryEditor.
26  */
27 public class SynchronizeFileField extends AbstractWorker {
28
29     private String fieldName = GUIGlobals.FILE_FIELD;
30     private BasePanel panel;
31     private BibtexEntry[] sel = null;
32     private SynchronizeFileField.OptionsDialog optDiag = null;
33
34     Object[] brokenLinkOptions =
35             {Globals.lang("Ignore"), Globals.lang("Assign new file"), Globals.lang("Clear field"),
36                     Globals.lang("Quit synchronization")};
37
38     private boolean goOn = true, autoSet = true, overWriteAllowed = true, checkExisting = true;
39
40     private int skipped = 0, brokenLinks = 0, entriesChangedCount = 0;
41
42     public SynchronizeFileField(BasePanel panel) {
43         this.panel = panel;
44     }
45
46     public void init() {
47         Collection col = panel.database().getEntries();
48         sel = new BibtexEntry[col.size()];
49         sel = (BibtexEntry[]) col.toArray(sel);
50
51         // Ask about rules for the operation:
52         if (optDiag == null)
53             optDiag = new SynchronizeFileField.OptionsDialog(panel.frame(), fieldName);
54         Util.placeDialog(optDiag, panel.frame());
55         optDiag.setVisible(true);
56         if (optDiag.canceled()) {
57             goOn = false;
58             return;
59         }
60         autoSet = !optDiag.autoSetNone.isSelected();
61         overWriteAllowed = optDiag.autoSetAll.isSelected();
62         checkExisting = optDiag.checkLinks.isSelected();
63
64         panel.output(Globals.lang("Synchronizing %0 links...", fieldName.toUpperCase()));
65     }
66
67     public void run() {
68         if (!goOn) {
69             panel.output(Globals.lang("No entries selected."));
70             return;
71         }
72         panel.frame().setProgressBarValue(0);
73         panel.frame().setProgressBarVisible(true);
74         int weightAutoSet = 10; // autoSet takes 10 (?) times longer than checkExisting
75         int progressBarMax = (autoSet ? weightAutoSet * sel.length : 0)
76                 + (checkExisting ? sel.length : 0);
77         panel.frame().setProgressBarMaximum(progressBarMax);
78         int progress = 0;
79         skipped = 0;
80         brokenLinks = 0;
81         final NamedCompound ce = new NamedCompound(Globals.lang("Autoset %0 field", fieldName));
82
83         //final OpenFileFilter off = Util.getFileFilterForField(fieldName);
84
85         //ExternalFilePanel extPan = new ExternalFilePanel(fieldName, panel.metaData(), null, null, off);
86         //FieldTextField editor = new FieldTextField(fieldName, "", false);
87
88         // Find the default directory for this field type:
89         String dir = panel.metaData().getFileDirectory(GUIGlobals.FILE_FIELD);
90         Set<BibtexEntry> changedEntries = new HashSet<BibtexEntry>();
91
92         // First we try to autoset fields
93         if (autoSet) {
94             Collection<BibtexEntry> entries = new ArrayList<BibtexEntry>();
95             for (int i = 0; i < sel.length; i++) {
96                 entries.add(sel[i]);
97             }
98             // Start the autosetting process:
99
100             Thread t = FileListEditor.autoSetLinks(entries, ce, changedEntries);
101             // Wait for the autosetting to finish:
102             try {
103                 t.join();
104             } catch (InterruptedException e) {
105                 e.printStackTrace();
106             }
107             /*
108                 progress += weightAutoSet;
109                 panel.frame().setProgressBarValue(progress);
110
111                 Object old = sel[i].getField(fieldName);
112                 FileListTableModel tableModel = new FileListTableModel();
113                 if (old != null)
114                     tableModel.setContent((String)old);
115                 Thread t = FileListEditor.autoSetLinks(sel[i], tableModel, null, null);
116
117                 if (!tableModel.getStringRepresentation().equals(old)) {
118                     String toSet = tableModel.getStringRepresentation();
119                     if (toSet.length() == 0)
120                         toSet = null;
121                     ce.addEdit(new UndoableFieldChange(sel[i], fieldName, old, toSet));
122                     sel[i].setField(fieldName, toSet);
123                     entriesChanged++;
124                 }
125             }    */
126
127         }
128         progress += sel.length*weightAutoSet;
129         panel.frame().setProgressBarValue(progress);
130         //System.out.println("Done setting");
131         // The following loop checks all external links that are already set.
132         if (checkExisting) {
133             mainLoop:
134             for (int i = 0; i < sel.length; i++) {
135                 panel.frame().setProgressBarValue(progress++);
136                 final Object old = sel[i].getField(fieldName);
137                 // Check if a extension is set:
138                 if ((old != null) && !old.equals("")) {
139                     FileListTableModel tableModel = new FileListTableModel();
140                     tableModel.setContent((String)old);
141                     for (int j=0; j<tableModel.getRowCount(); j++) {
142                         FileListEntry flEntry = tableModel.getEntry(j);
143                         // See if the link looks like an URL:
144                         boolean httpLink = flEntry.getLink().toLowerCase().startsWith("http");
145                         if (httpLink)
146                             continue; // Don't check the remote file.
147                         // TODO: should there be an option to check remote links?
148                         
149                         // Get an absolute path representation:
150                         File file = Util.expandFilename(flEntry.getLink(), new String[]{dir, "."});
151                         if ((file == null) || !file.exists()) {
152                             int answer = JOptionPane.showOptionDialog(panel.frame(),
153                                 Globals.lang("<HTML>Could not find file '%0'<BR>linked from entry '%1'</HTML>",
154                                         new String[]{flEntry.getLink(), sel[i].getCiteKey()}),
155                                 Globals.lang("Broken link"),
156                                 JOptionPane.YES_NO_CANCEL_OPTION,
157                                 JOptionPane.QUESTION_MESSAGE, null, brokenLinkOptions, brokenLinkOptions[0]);
158                             switch (answer) {
159                                 case 1:
160                                     // Assign new file.
161                                     FileListEntryEditor flEditor = new FileListEntryEditor
162                                             (panel.frame(), flEntry, false, panel.metaData());
163                                     flEditor.setVisible(true);
164                                     break;
165                                 case 2:
166                                     // Clear field
167                                     tableModel.removeEntry(j);
168                                     j--; // Step back in the iteration, because we removed an entry.
169                                     break;
170                                 case 3:
171                                     // Cancel
172                                     break mainLoop;
173                             }
174                             brokenLinks++;
175                         }
176
177                     }
178                     if (!tableModel.getStringRepresentation().equals(old)) {
179                         // The table has been modified. Store the change:
180                         String toSet = tableModel.getStringRepresentation();
181                         if (toSet.length() == 0)
182                             toSet = null;
183                         ce.addEdit(new UndoableFieldChange(sel[i], fieldName, old,
184                                 toSet));
185                         sel[i].setField(fieldName, toSet);
186                         changedEntries.add(sel[i]);
187                         //System.out.println("Changed to: "+tableModel.getStringRepresentation());
188                     }
189
190
191                 }
192             }
193         }
194
195         entriesChangedCount = changedEntries.size();
196         //for (BibtexEntry entr : changedEntries)
197         //    System.out.println(entr.getCiteKey());
198         if (entriesChangedCount > 0) {
199             // Add the undo edit:
200             ce.end();
201             panel.undoManager.addEdit(ce);
202             
203         }
204     }
205
206
207     public void update() {
208         if (!goOn)
209             return;
210
211         panel.output(Globals.lang("Finished synchronizing %0 links. Entries changed%c %1.",
212                 new String[]{fieldName.toUpperCase(), String.valueOf(entriesChangedCount)}));
213         panel.frame().setProgressBarVisible(false);
214         if (entriesChangedCount > 0) {
215             panel.markBaseChanged();
216         }
217     }
218
219     static class OptionsDialog extends JDialog {
220         JRadioButton autoSetUnset, autoSetAll, autoSetNone;
221         JCheckBox checkLinks;
222         JButton ok = new JButton(Globals.lang("Ok")),
223                 cancel = new JButton(Globals.lang("Cancel"));
224         JLabel description;
225         private boolean canceled = true;
226         private String fieldName;
227
228         public OptionsDialog(JFrame parent, String fieldName) {
229             super(parent, Globals.lang("Synchronize %0 links", fieldName.toUpperCase()), true);
230             final String fn = Globals.lang("file");
231             this.fieldName = fieldName;
232             ok.addActionListener(new ActionListener() {
233                 public void actionPerformed(ActionEvent e) {
234                     canceled = false;
235                     dispose();
236                 }
237             });
238
239             Action closeAction = new AbstractAction() {
240                 public void actionPerformed(ActionEvent e) {
241                     dispose();
242                 }
243             };
244
245
246             cancel.addActionListener(closeAction);
247
248             InputMap im = cancel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
249             ActionMap am = cancel.getActionMap();
250             im.put(Globals.prefs.getKey("Close dialog"), "close");
251             am.put("close", closeAction);
252
253             autoSetUnset = new JRadioButton(Globals.lang("Autoset %0 links. Do not overwrite existing links.", fn), true);
254             autoSetAll = new JRadioButton(Globals.lang("Autoset %0 links. Allow overwriting existing links.", fn), false);
255             autoSetNone = new JRadioButton(Globals.lang("Do not autoset"), false);
256             checkLinks = new JCheckBox(Globals.lang("Check existing %0 links", fn), true);
257             ButtonGroup bg = new ButtonGroup();
258             bg.add(autoSetUnset);
259             bg.add(autoSetNone);
260             bg.add(autoSetAll);
261             FormLayout layout = new FormLayout("fill:pref", "");
262             DefaultFormBuilder builder = new DefaultFormBuilder(layout);
263             description = new JLabel("<HTML>" +
264                     Globals.lang(//"This function helps you keep your external %0 links up-to-date." +
265                             "Attempt to autoset %0 links for your entries. Autoset works if "
266                                     + "a %0 file in your %0 directory or a subdirectory<BR>is named identically to an entry's BibTeX key, plus extension.", fn)
267                     + "</HTML>");
268             //            name.setVerticalAlignment(JLabel.TOP);
269             builder.appendSeparator(Globals.lang("Autoset"));
270             builder.append(description);
271             builder.nextLine();
272             builder.append(autoSetUnset);
273             builder.nextLine();
274             builder.append(autoSetAll);
275             builder.nextLine();
276             builder.append(autoSetNone);
277             builder.nextLine();
278             builder.appendSeparator(Globals.lang("Check links"));
279
280             description = new JLabel("<HTML>" +
281                     Globals.lang("This makes JabRef look up each %0 extension and check if the file exists. If not, you will "
282                             + "be given options<BR>to resolve the problem.", fn)
283                     + "</HTML>");
284             builder.append(description);
285             builder.nextLine();
286             builder.append(checkLinks);
287             builder.nextLine();
288             builder.appendSeparator();
289
290
291             JPanel main = builder.getPanel();
292             main.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
293
294             ButtonBarBuilder bb = new ButtonBarBuilder();
295             bb.addGlue();
296             bb.addGridded(ok);
297             bb.addGridded(cancel);
298             bb.addGlue();
299             getContentPane().add(main, BorderLayout.CENTER);
300             getContentPane().add(bb.getPanel(), BorderLayout.SOUTH);
301
302             pack();
303         }
304
305         public void setVisible(boolean visible) {
306             if (visible)
307                 canceled = true;
308
309             String dir = Globals.prefs.get(fieldName + "Directory");
310             if ((dir == null) || (dir.trim().length() == 0)) {
311
312                 autoSetNone.setSelected(true);
313                 autoSetNone.setEnabled(false);
314                 autoSetAll.setEnabled(false);
315                 autoSetUnset.setEnabled(false);
316             } else {
317                 autoSetNone.setEnabled(true);
318                 autoSetAll.setEnabled(true);
319                 autoSetUnset.setEnabled(true);
320             }
321
322             new FocusRequester(ok);
323             super.setVisible(visible);
324
325         }
326
327         public boolean canceled() {
328             return canceled;
329         }
330     }
331 }