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