cfa5a9cf1ac4769963fbea7624adfdbfa7749935
[debian/jabref.git] / src / java / net / sf / jabref / JabRefFrame.java
1 /*
2  Copyright (C) 2003 Morten O. Alver, Nizar N. Batada
3
4  All programs in this directory and
5  subdirectories are published under the GNU General Public License as
6  described below.
7
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or (at
11  your option) any later version.
12
13  This program is distributed in the hope that it will be useful, but
14  WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  General Public License for more details.
17
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
21  USA
22
23  Further information about the GNU GPL is available at:
24  http://www.gnu.org/copyleft/gpl.ja.html
25
26  */
27
28 package net.sf.jabref;
29
30 import net.sf.jabref.gui.*;
31 import net.sf.jabref.label.*;
32 import net.sf.jabref.imports.*;
33 import net.sf.jabref.wizard.auximport.gui.*;
34
35 import javax.swing.*;
36
37 import java.awt.*;
38 import java.awt.event.*;
39 import java.util.*;
40 import java.util.List;
41 import java.io.*;
42 import java.net.URL;
43 import net.sf.jabref.undo.NamedCompound;
44 import net.sf.jabref.undo.UndoableInsertEntry;
45 import net.sf.jabref.undo.UndoableRemoveEntry;
46 import net.sf.jabref.export.*;
47
48 import java.lang.reflect.*;
49 import javax.swing.event.*;
50 import net.sf.jabref.wizard.integrity.gui.*;
51 import net.sf.jabref.groups.GroupSelector;
52 import net.sf.jabref.groups.EntryTableTransferHandler;
53 import net.sf.jabref.journals.ManageJournalsAction;
54 import net.sf.jabref.external.*;
55 import net.sf.jabref.util.MassSetFieldAction;
56 import com.jgoodies.uif_lite.component.UIFSplitPane;
57 import com.jgoodies.looks.Options;
58 import com.jgoodies.looks.HeaderStyle;
59
60
61 /**
62  * The main window of the application.
63  */
64 public class JabRefFrame extends JFrame {
65
66    // CO: Code Smells...
67     JabRefFrame ths = this;
68     UIFSplitPane contentPane = new UIFSplitPane();
69
70     JabRefPreferences prefs = Globals.prefs; //new JabRefPreferences();
71     PrefsDialog3 prefsDialog = null;
72     
73     private int lastTabbedPanelSelectionIndex = -1 ;
74
75     // The sidepane manager takes care of populating the sidepane.
76     public SidePaneManager sidePaneManager;
77
78     JTabbedPane tabbedPane = new JTabbedPane();
79     
80     final Insets marg = new Insets(1,0,2,0);
81
82     class ToolBar extends JToolBar {
83       void addAction(Action a) {
84         JButton b = new JButton(a);
85         b.setText(null);
86         if (!Globals.ON_MAC)
87             b.setMargin(marg);
88         add(b);
89       }
90     }
91     ToolBar tlb = new ToolBar();
92
93     JMenuBar mb = new JMenuBar();
94
95     GridBagLayout gbl = new GridBagLayout();
96
97     GridBagConstraints con = new GridBagConstraints();
98
99     JLabel statusLine = new JLabel("", SwingConstants.LEFT), statusLabel = new JLabel(Globals
100         .lang("Status")
101         + ":", SwingConstants.LEFT);
102     JProgressBar progressBar = new JProgressBar();
103
104     // SearchManager searchManager = new SearchManager(ths, prefs);
105
106     private FileHistory fileHistory = new FileHistory(prefs, this);
107
108     LabelMaker labelMaker;
109
110     // The help window.
111     public HelpDialog helpDiag = new HelpDialog(this);
112
113     // Here we instantiate menu/toolbar actions. Actions regarding
114     // the currently open database are defined as a GeneralAction
115     // with a unique command string. This causes the appropriate
116     // BasePanel's runCommand() method to be called with that command.
117     // Note: GeneralAction's constructor automatically gets translations
118     // for the name and message strings.
119
120   // References to the toggle buttons in the toolbar:
121   public JToggleButton groupToggle, searchToggle, previewToggle, highlightAny,
122       highlightAll;
123
124   OpenDatabaseAction
125       open = new OpenDatabaseAction(this, true);
126   AbstractAction
127       close = new CloseDatabaseAction(),
128       quit = new CloseAction(),
129       selectKeys = new SelectKeysAction(),
130       newDatabaseAction = new NewDatabaseAction(),
131       newSubDatabaseAction = new NewSubDatabaseAction(),
132       integrityCheckAction = new IntegrityCheckAction(),
133       help = new HelpAction("JabRef help", helpDiag,
134                             GUIGlobals.baseFrameHelp, Globals.lang("JabRef help"),
135                             prefs.getKey("Help")),
136       contents = new HelpAction("Help contents", helpDiag,
137                                 GUIGlobals.helpContents, Globals.lang("Help contents"),
138                                 GUIGlobals.getIconUrl("helpContents")),
139       about = new HelpAction("About JabRef", helpDiag,
140                              GUIGlobals.aboutPage, Globals.lang("About JabRef"),
141                              GUIGlobals.getIconUrl("about")),
142       editEntry = new GeneralAction("edit", "Edit entry",
143                                Globals.lang("Edit entry"),
144                                prefs.getKey("Edit entry")),
145       save = new GeneralAction("save", "Save database",
146                                Globals.lang("Save database"),
147                                prefs.getKey("Save database")),
148       saveAs = new GeneralAction("saveAs", "Save database as ...",
149                                  Globals.lang("Save database as ..."),
150                                  prefs.getKey("Save database as ...")),
151       saveAll = new SaveAllAction(ths),
152       saveSelectedAs = new GeneralAction("saveSelectedAs",
153                                          "Save selected as ...",
154                                          Globals.lang("Save selected as ..."),
155                                          GUIGlobals.getIconUrl("saveAs")),
156       exportAll = ExportFormats.getExportAction(this, false),
157       exportSelected = ExportFormats.getExportAction(this, true),
158       importCurrent = ImportFormats.getImportAction(this, false),
159       importNew = ImportFormats.getImportAction(this, true),
160       nextTab = new ChangeTabAction(true),
161       prevTab = new ChangeTabAction(false),
162       sortTabs = new SortTabsAction(this),
163       undo = new GeneralAction("undo", "Undo", Globals.lang("Undo"),
164                                prefs.getKey("Undo")),
165       redo = new GeneralAction("redo", "Redo", Globals.lang("Redo"),
166                                prefs.getKey("Redo")),
167       /*cut = new GeneralAction("cut", "Cut", Globals.lang("Cut"),
168          GUIGlobals.cutIconFile,
169          prefs.getKey("Cut")),*/
170       delete = new GeneralAction("delete", "Delete", Globals.lang("Delete"),
171                                  prefs.getKey("Delete")),
172       /*copy = new GeneralAction("copy", "Copy", Globals.lang("Copy"),
173                                GUIGlobals.copyIconFile,
174                                prefs.getKey("Copy")),*/
175       copy = new EditAction("copy", GUIGlobals.getIconUrl("copy")),
176       paste = new EditAction("paste", GUIGlobals.getIconUrl("paste")),
177       cut = new EditAction("cut", GUIGlobals.getIconUrl("cut")),
178       mark = new GeneralAction("markEntries", "Mark entries",
179                                Globals.lang("Mark entries"),
180                                prefs.getKey("Mark entries")),
181        unmark = new GeneralAction("unmarkEntries", "Unmark entries",
182                                   Globals.lang("Unmark entries"),
183                                   prefs.getKey("Unmark entries")),
184        unmarkAll = new GeneralAction("unmarkAll", "Unmark all"),
185       manageSelectors = new GeneralAction("manageSelectors", "Manage content selectors"),
186       saveSessionAction = new SaveSessionAction(),
187       loadSessionAction = new LoadSessionAction(),
188       incrementalSearch = new GeneralAction("incSearch", "Incremental search",
189                                             Globals.lang("Start incremental search"),
190                                             prefs.getKey("Incremental search")),
191       normalSearch = new GeneralAction("search", "Search", Globals.lang("Search"),
192                                        prefs.getKey("Search")),
193       toggleSearch = new GeneralAction("toggleSearch", "Search", Globals.lang("Toggle search panel")),
194
195       fetchCiteSeer = new FetchCiteSeerAction(),
196       importCiteSeer = new ImportCiteSeerAction(),
197       fetchMedline = new FetchMedlineAction(),
198       citeSeerPanelAction = new CiteSeerPanelAction(),
199       //fetchAuthorMedline = new FetchAuthorMedlineAction(),
200       copyKey = new GeneralAction("copyKey", "Copy BibTeX key"),
201       //"Put a BibTeX reference to the selected entries on the clipboard",
202       copyCiteKey = new GeneralAction("copyCiteKey", "Copy \\cite{BibTeX key}",
203                                       //"Put a BibTeX reference to the selected entries on the clipboard",
204                                       prefs.getKey("Copy \\cite{BibTeX key}")),
205       mergeDatabaseAction = new GeneralAction("mergeDatabase",
206                                               "Append database",
207                                               Globals.lang("Append contents from a BibTeX database into the currently viewed database"),
208                                               GUIGlobals.getIconUrl("open")),
209       //prefs.getKey("Open")),
210       /*remove = new GeneralAction("remove", "Remove", "Remove selected entries",
211         GUIGlobals.removeIconFile),*/
212       selectAll = new GeneralAction("selectAll", "Select all",
213                                     prefs.getKey("Select all")),
214       replaceAll = new GeneralAction("replaceAll", "Replace string",
215                                      prefs.getKey("Replace string")),
216
217       editPreamble = new GeneralAction("editPreamble", "Edit preamble",
218                                        Globals.lang("Edit preamble"),
219                                        prefs.getKey("Edit preamble")),
220       editStrings = new GeneralAction("editStrings", "Edit strings",
221                                       Globals.lang("Edit strings"),
222                                       prefs.getKey("Edit strings")),
223       toggleGroups = new GeneralAction("toggleGroups",
224                                        "Toggle groups interface",
225                                        Globals.lang("Toggle groups interface"),
226                                        prefs.getKey("Toggle groups interface")),
227       togglePreview = new GeneralAction("togglePreview",
228                                         "Toggle entry preview",
229                                         Globals.lang("Toggle entry preview"),
230                                         prefs.getKey("Toggle entry preview")),
231       toggleHighlightAny = new GeneralAction("toggleHighlightGroupsMatchingAny",
232                                         "Highlight groups matching any selected entry",
233                                         Globals.lang("Highlight groups matching any selected entry"),
234                                         GUIGlobals.getIconUrl("groupsHighlightAny")),
235       toggleHighlightAll = new GeneralAction("toggleHighlightGroupsMatchingAll",
236                                         "Highlight groups matching all selected entries",
237                                         Globals.lang("Highlight groups matching all selected entries"),
238                                         GUIGlobals.getIconUrl("groupsHighlightAll")),
239       switchPreview = new GeneralAction("switchPreview",
240                                         "Switch preview layout",
241                                         prefs.getKey("Switch preview layout")),
242        makeKeyAction = new GeneralAction("makeKey", "Autogenerate BibTeX keys",
243                                         Globals.lang("Autogenerate BibTeX keys"),
244                                         prefs.getKey("Autogenerate BibTeX keys")),
245
246
247       writeXmpAction = new GeneralAction("writeXMP", "Write XMP-metadata to PDFs",
248                                         Globals.lang("Will write XMP-metadata to the PDFs linked from selected entries."),
249                                         prefs.getKey("Write XMP")),
250       openFile = new GeneralAction("openExternalFile", "Open file",
251                                    Globals.lang("Open file"),
252                                    prefs.getKey("Open file")),
253       openPdf = new GeneralAction("openFile", "Open PDF or PS",
254                                    Globals.lang("Open PDF or PS"),
255                                    prefs.getKey("Open PDF or PS")),
256       openUrl = new GeneralAction("openUrl", "Open URL or DOI",
257                                   Globals.lang("Open URL or DOI"),
258                                   prefs.getKey("Open URL or DOI")),
259       dupliCheck = new GeneralAction("dupliCheck", "Find duplicates"),
260       //strictDupliCheck = new GeneralAction("strictDupliCheck", "Find and remove exact duplicates"),
261       plainTextImport = new GeneralAction("plainTextImport",
262                                           "New entry from plain text",
263                                           prefs.getKey("New from plain text")),
264
265
266       customExpAction = new CustomizeExportsAction(),
267       customImpAction = new CustomizeImportsAction(),
268       customFileTypesAction = ExternalFileTypeEditor.getAction(this),
269       exportToClipboard = new GeneralAction("exportToClipboard", "Export selected entries to clipboard"),
270       expandEndnoteZip = new ExpandEndnoteFilters(this),
271         autoSetPdf = new GeneralAction("autoSetPdf", Globals.lang("Synchronize %0 links", "PDF"), Globals.prefs.getKey("Synchronize PDF")),
272         autoSetPs = new GeneralAction("autoSetPs", Globals.lang("Synchronize %0 links", "PS"), Globals.prefs.getKey("Synchronize PS")),
273         autoSetFile = new GeneralAction("autoSetFile", Globals.lang("Synchronize file links"), Globals.prefs.getKey("Synchronize files")),
274
275     abbreviateMedline = new GeneralAction("abbreviateMedline", "Abbreviate journal names (MEDLINE)",
276                 Globals.lang("Abbreviate journal names of the selected entries (MEDLINE abbreviation)")),
277   abbreviateIso = new GeneralAction("abbreviateIso", "Abbreviate journal names (ISO)",
278                           Globals.lang("Abbreviate journal names of the selected entries (ISO abbreviation)"),
279                           Globals.prefs.getKey("Abbreviate")),
280
281
282     unabbreviate = new GeneralAction("unabbreviate", "Unabbreviate journal names",
283                     Globals.lang("Unabbreviate journal names of the selected entries"),
284             Globals.prefs.getKey("Unabbreviate")),
285     manageJournals = new ManageJournalsAction(this),
286     databaseProperties = new DatabasePropertiesAction(),
287     upgradeExternalLinks = new GeneralAction("upgradeLinks", "Upgrade external links",
288             Globals.lang("Upgrade external PDF/PS links to use the '%0' field.", GUIGlobals.FILE_FIELD)),
289       errorConsole = Globals.errorConsole.getAction(this),
290     test = new GeneralAction("test", "Test");
291
292     PushToApplicationButton pushExternalButton;
293   /*setupSelector = new GeneralAction("setupSelector", "", "",
294           GUIGlobals.pasteIconFile,
295           prefs.getKey(")),*/
296
297
298     MedlineFetcher medlineFetcher;
299     CiteSeerFetcher citeSeerFetcher;
300     CiteSeerFetcherPanel citeSeerFetcherPanel;
301     IEEEXploreFetcher ieeexplorerFetcher;
302     GeneralFetcher ieex;
303     OAI2Fetcher arxivFetcher;
304     GeneralFetcher arxiv;
305     SearchManager2 searchManager;
306     public GroupSelector groupSelector;
307
308   // The menus for importing/appending other formats
309   JMenu importMenu = subMenu("Import into current database"),
310       importNewMenu = subMenu("Import into new database"),
311       exportMenu = subMenu("Export"),
312       customExportMenu = subMenu("Custom export"),
313       newDatabaseMenu = subMenu("New database" );
314
315   // Other submenus
316   JMenu checkAndFix = subMenu("Scan database...");
317
318
319   // The action for adding a new entry of unspecified type.
320   NewEntryAction newEntryAction = new NewEntryAction(prefs.getKey("New entry"));
321   NewEntryAction[] newSpecificEntryAction = new NewEntryAction[]
322   {
323       new NewEntryAction("article", prefs.getKey("New article")),
324       new NewEntryAction("book", prefs.getKey("New book")),
325       new NewEntryAction("phdthesis", prefs.getKey("New phdthesis")),
326       new NewEntryAction("inbook", prefs.getKey("New inbook")),
327       new NewEntryAction("mastersthesis", prefs.getKey("New mastersthesis")),
328       new NewEntryAction("proceedings", prefs.getKey("New proceedings")),
329       new NewEntryAction("inproceedings"),
330       new NewEntryAction("conference"),
331       new NewEntryAction("incollection"),
332       new NewEntryAction("booklet"),
333       new NewEntryAction("manual"),
334       new NewEntryAction("techreport"),
335       new NewEntryAction("unpublished",
336                          prefs.getKey("New unpublished")),
337       new NewEntryAction("misc"),
338       new NewEntryAction("other")
339   };
340
341   public JabRefFrame() {
342     init();
343     updateEnabledState();
344   }
345
346   private void init() {
347
348         macOSXRegistration();
349         MyGlassPane glassPane = new MyGlassPane();
350         setGlassPane(glassPane);
351         // glassPane.setVisible(true);
352
353         setTitle(GUIGlobals.frameTitle);
354         setIconImage(GUIGlobals.getImage("jabrefIcon").getImage());
355         setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
356         addWindowListener(new WindowAdapter() {
357             public void windowClosing(WindowEvent e) {
358                 (new CloseAction()).actionPerformed(null);
359             }
360         });
361
362         initLabelMaker();
363
364         initSidePane();
365         
366         initLayout();
367         
368         initActions();
369         
370         if (Globals.prefs.getBoolean("rememberWindowLocation")) {
371             setSize(new Dimension(prefs.getInt("sizeX"), prefs.getInt("sizeY")));
372             setLocation(new Point(prefs.getInt("posX"), prefs.getInt("posY")));
373         }
374         tabbedPane.setBorder(null);
375         tabbedPane.setForeground(GUIGlobals.inActiveTabbed);
376
377         /*
378          * The following state listener makes sure focus is registered with the
379          * correct database when the user switches tabs. Without this,
380          * cut/paste/copy operations would some times occur in the wrong tab.
381          */
382         tabbedPane.addChangeListener(new ChangeListener() {
383             public void stateChanged(ChangeEvent e) {
384                 markActiveBasePanel();
385
386                 BasePanel bp = basePanel();
387                 if (bp != null) {
388                     groupToggle.setSelected(sidePaneManager.isComponentVisible("groups"));
389                     searchToggle.setSelected(sidePaneManager.isComponentVisible("search"));
390                     previewToggle.setSelected(Globals.prefs.getBoolean("previewEnabled"));
391                     highlightAny
392                         .setSelected(Globals.prefs.getBoolean("highlightGroupsMatchingAny"));
393                     highlightAll
394                         .setSelected(Globals.prefs.getBoolean("highlightGroupsMatchingAll"));
395                     Globals.focusListener.setFocused(bp.mainTable);
396
397                     new FocusRequester(bp.mainTable);
398                 }
399             }
400         });
401     }
402
403
404
405     private void initSidePane() {
406         sidePaneManager = new SidePaneManager(this);
407
408         Globals.sidePaneManager = this.sidePaneManager;
409         Globals.helpDiag = this.helpDiag;
410
411         ieeexplorerFetcher = new IEEEXploreFetcher();
412         arxivFetcher = new OAI2Fetcher();
413         medlineFetcher = new MedlineFetcher(sidePaneManager);
414         citeSeerFetcher = new CiteSeerFetcher(sidePaneManager);
415         citeSeerFetcherPanel = new CiteSeerFetcherPanel(sidePaneManager,
416             (CiteSeerFetcher) citeSeerFetcher);
417         groupSelector = new GroupSelector(this, sidePaneManager);
418         searchManager = new SearchManager2(this, sidePaneManager);
419
420         sidePaneManager.register("fetchMedline", medlineFetcher);
421         sidePaneManager.register("CiteSeerProgress", citeSeerFetcher);
422         sidePaneManager.register("CiteSeerPanel", citeSeerFetcherPanel);
423         sidePaneManager.register("groups", groupSelector);
424         sidePaneManager.register("search", searchManager);
425
426         // Show the search panel if it was visible at last shutdown:
427         if (Globals.prefs.getBoolean("searchPanelVisible"))
428             sidePaneManager.show("search");
429     }
430
431
432 AboutAction aboutAction = new AboutAction();
433   class AboutAction
434       extends AbstractAction {
435     public AboutAction() {
436       super(Globals.lang("About JabRef"));
437
438     }
439
440     public void actionPerformed(ActionEvent e) {
441       about();
442     }
443   }
444
445
446   // General info dialog.  The OSXAdapter calls this method when "About OSXAdapter"
447   // is selected from the application menu.
448   public void about() {
449     JDialog about = new JDialog(ths, Globals.lang("About JabRef"),
450                                 true);
451     JEditorPane jp = new JEditorPane();
452     JScrollPane sp = new JScrollPane
453         (jp, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
454          JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
455     jp.setEditable(false);
456     try {
457       jp.setPage(GUIGlobals.class.getResource("/help/About.html"));//GUIGlobals.aboutPage);
458       // We need a hyperlink listener to be able to switch to the license
459       // terms and back.
460       jp.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
461         public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent e) {
462           if (e.getEventType()
463               == javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) {
464             try {
465               ( (JEditorPane) e.getSource()).setPage(e.getURL());
466             }
467             catch (IOException ex) {}
468           }
469         }
470       });
471       about.getContentPane().add(sp);
472       about.setSize(GUIGlobals.aboutSize);
473       Util.placeDialog(about, ths);
474       about.setVisible(true);
475     }
476     catch (IOException ex) {
477       ex.printStackTrace();
478       JOptionPane.showMessageDialog(ths, "Could not load file 'About.html'",
479                                     "Error", JOptionPane.ERROR_MESSAGE);
480     }
481
482   }
483
484   // General preferences dialog.  The OSXAdapter calls this method when "Preferences..."
485   // is selected from the application menu.
486   public void preferences() {
487     //PrefsDialog.showPrefsDialog(ths, prefs);
488       AbstractWorker worker = new AbstractWorker() {
489               public void run() {
490                   output(Globals.lang("Opening preferences..."));
491                   if (prefsDialog == null) {
492                       prefsDialog = new PrefsDialog3(JabRefFrame.this);
493                       Util.placeDialog(prefsDialog, JabRefFrame.this);
494                   }
495                   else
496                       prefsDialog.setValues();
497
498               }
499               public void update() {
500                   prefsDialog.setVisible(true);
501                   output("");
502               }
503           };
504       worker.getWorker().run();
505       worker.getCallBack().update();
506   }
507
508 public JabRefPreferences prefs() {
509   return prefs;
510 }
511
512   // General info dialog.  The OSXAdapter calls this method when "Quit OSXAdapter"
513   // is selected from the application menu, Cmd-Q is pressed, or "Quit" is selected from the Dock.
514   public void quit() {
515     // Ask here if the user really wants to close, if the base
516     // has not been saved since last save.
517     boolean close = true;
518     Vector filenames = new Vector();
519     if (tabbedPane.getTabCount() > 0) {
520       loop: for (int i = 0; i < tabbedPane.getTabCount(); i++) {
521         if (baseAt(i).baseChanged) {
522           tabbedPane.setSelectedIndex(i);
523           int answer = JOptionPane.showConfirmDialog
524               (ths, Globals.lang
525                ("Database has changed. Do you "
526                 + "want to save before closing?"),
527                Globals.lang("Save before closing"),
528                JOptionPane.YES_NO_CANCEL_OPTION);
529
530           if ( (answer == JOptionPane.CANCEL_OPTION) ||
531               (answer == JOptionPane.CLOSED_OPTION)) {
532             close = false; // The user has cancelled.
533               return;
534           }
535           if (answer == JOptionPane.YES_OPTION) {
536             // The user wants to save.
537             try {
538               //basePanel().runCommand("save");
539                 SaveDatabaseAction saveAction = new SaveDatabaseAction(basePanel());
540                 saveAction.runCommand();
541                 if (saveAction.isCancelled() || !saveAction.isSuccess())
542                     // The action was either cancelled or unsuccessful.
543                     // Break!
544                     close = false;
545             }
546             catch (Throwable ex) {
547               // Something prevented the file
548               // from being saved. Break!!!
549               close = false;
550               break;
551             }
552           }
553         }
554         if (baseAt(i).getFile() != null) {
555           filenames.add(baseAt(i).getFile().getAbsolutePath());
556         }
557       }
558     }
559     if (close) {
560       dispose();
561
562       prefs.putInt("posX", ths.getLocation().x);
563       prefs.putInt("posY", ths.getLocation().y);
564       prefs.putInt("sizeX", ths.getSize().width);
565       prefs.putInt("sizeY", ths.getSize().height);
566       prefs.putBoolean("searchPanelVisible", sidePaneManager.isComponentVisible("search"));
567       // Store divider location for side pane:
568       int width = contentPane.getDividerLocation();
569       if (width > 0) 
570           prefs.putInt("sidePaneWidth", width);
571       if (prefs.getBoolean("openLastEdited")) {
572         // Here we store the names of allcurrent filea. If
573         // there is no current file, we remove any
574         // previously stored file name.
575         if (filenames.size() == 0) {
576           prefs.remove("lastEdited");
577         }
578         else {
579           String[] names = new String[filenames.size()];
580           for (int i = 0; i < filenames.size(); i++) {
581             names[i] = (String) filenames.elementAt(i);
582
583           }
584           prefs.putStringArray("lastEdited", names);
585         }
586
587       }
588
589       fileHistory.storeHistory();
590       prefs.customExports.store();
591       prefs.customImports.store();
592       BibtexEntryType.saveCustomEntryTypes(prefs);
593
594       // Let the search interface store changes to prefs.
595       // But which one? Let's use the one that is visible.
596       if (basePanel() != null) {
597         ((SearchManager2)searchManager).updatePrefs();
598
599       }
600       System.exit(0); // End program.
601     }
602   }
603
604     
605
606   private void macOSXRegistration() {
607     if (Globals.osName.equals(Globals.MAC)) {
608       try {
609         Class osxAdapter = Class.forName("osxadapter.OSXAdapter");
610
611         Class[] defArgs = {
612             JabRefFrame.class};
613         Method registerMethod = osxAdapter.getDeclaredMethod(
614             "registerMacOSXApplication", defArgs);
615         if (registerMethod != null) {
616           Object[] args = {
617               this};
618           registerMethod.invoke(osxAdapter, args);
619         }
620         // This is slightly gross.  to reflectively access methods with boolean args,
621         // use "boolean.class", then pass a Boolean object in as the arg, which apparently
622
623         defArgs[0] = boolean.class;
624         Method prefsEnableMethod = osxAdapter.getDeclaredMethod("enablePrefs",
625             defArgs);
626         if (prefsEnableMethod != null) {
627           Object args[] = {
628               Boolean.TRUE};
629           prefsEnableMethod.invoke(osxAdapter, args);
630         }
631       }
632       catch (NoClassDefFoundError e) {
633         // This will be thrown first if the OSXAdapter is loaded on a system without the EAWT
634         // because OSXAdapter extends ApplicationAdapter in its def
635         System.err.println("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled (" +
636                            e + ")");
637       }
638       catch (ClassNotFoundException e) {
639         // This shouldn't be reached; if there's a problem with the OSXAdapter we should get the
640         // above NoClassDefFoundError first.
641         System.err.println("This version of Mac OS X does not support the Apple EAWT.  Application Menu handling has been disabled (" +
642                            e + ")");
643       }
644       catch (Exception e) {
645         System.err.println("Exception while loading the OSXAdapter:");
646         e.printStackTrace();
647       }
648     }
649   }
650
651
652   private void initLayout() {
653     tabbedPane.putClientProperty(Options.NO_CONTENT_BORDER_KEY, Boolean.TRUE);
654
655     setProgressBarVisible(false);
656
657       pushExternalButton = new PushToApplicationButton(this,
658               PushToApplicationButton.applications);
659     fillMenu();
660     createToolBar();
661     getContentPane().setLayout(gbl);
662       contentPane.setDividerSize(2);
663       contentPane.setBorder(null);
664     //getContentPane().setBackground(GUIGlobals.lightGray);
665     con.fill = GridBagConstraints.HORIZONTAL;
666     con.anchor = GridBagConstraints.WEST;
667     con.weightx = 1;
668     con.weighty = 0;
669     con.gridwidth = GridBagConstraints.REMAINDER;
670
671     //gbl.setConstraints(mb, con);
672     //getContentPane().add(mb);
673     setJMenuBar(mb);
674     con.anchor = GridBagConstraints.NORTH;
675     //con.gridwidth = 1;//GridBagConstraints.REMAINDER;;
676     gbl.setConstraints(tlb, con);
677     getContentPane().add(tlb);
678
679     Component lim = Box.createGlue();
680     gbl.setConstraints(lim, con);
681     //getContentPane().add(lim);
682     /*
683       JPanel empt = new JPanel();
684       empt.setBackground(GUIGlobals.lightGray);
685       gbl.setConstraints(empt, con);
686            getContentPane().add(empt);
687
688       con.insets = new Insets(1,0,1,1);
689       con.anchor = GridBagConstraints.EAST;
690       con.weightx = 0;
691       gbl.setConstraints(searchManager, con);
692       getContentPane().add(searchManager);*/
693     con.gridwidth = GridBagConstraints.REMAINDER;
694     con.weightx = 1;
695     con.weighty = 0;
696     con.fill = GridBagConstraints.BOTH;
697     con.anchor = GridBagConstraints.WEST;
698     con.insets = new Insets(0, 0, 0, 0);
699     lim = Box.createGlue();
700     gbl.setConstraints(lim, con);
701     getContentPane().add(lim);
702     //tabbedPane.setVisible(false);
703     //tabbedPane.setForeground(GUIGlobals.lightGray);
704     con.weighty = 1;
705     gbl.setConstraints(contentPane, con);
706     getContentPane().add(contentPane);
707     contentPane.setRightComponent(tabbedPane);
708     contentPane.setLeftComponent(sidePaneManager.getPanel());
709     sidePaneManager.updateView();
710
711     JPanel status = new JPanel();
712     status.setLayout(gbl);
713     con.weighty = 0;
714     con.weightx = 0;
715     con.gridwidth = 1;
716     con.insets = new Insets(0, 2, 0, 0);
717     gbl.setConstraints(statusLabel, con);
718     status.add(statusLabel);
719     con.weightx = 1;
720     con.insets = new Insets(0, 4, 0, 0);
721     con.gridwidth = 1;
722     gbl.setConstraints(statusLine, con);
723     status.add(statusLine);
724     con.weightx = 0;
725     con.gridwidth = GridBagConstraints.REMAINDER;
726     gbl.setConstraints(progressBar, con);
727     status.add(progressBar);
728     con.weightx = 1;
729     con.gridwidth = GridBagConstraints.REMAINDER;
730     statusLabel.setForeground(GUIGlobals.validFieldColor.darker());
731     con.insets = new Insets(0, 0, 0, 0);
732     gbl.setConstraints(status, con);
733     getContentPane().add(status);
734
735
736       // Drag and drop for tabbedPane:
737       TransferHandler xfer = new EntryTableTransferHandler(null, this, null);
738       tabbedPane.setTransferHandler(xfer);
739       tlb.setTransferHandler(xfer);
740       mb.setTransferHandler(xfer);
741       sidePaneManager.getPanel().setTransferHandler(xfer);
742   }
743
744   private void initLabelMaker() {
745     // initialize the labelMaker
746     labelMaker = new LabelMaker();
747     labelMaker.addRule(new ArticleLabelRule(),
748                        BibtexEntryType.ARTICLE);
749     labelMaker.addRule(new BookLabelRule(),
750                        BibtexEntryType.BOOK);
751     labelMaker.addRule(new IncollectionLabelRule(),
752                        BibtexEntryType.INCOLLECTION);
753     labelMaker.addRule(new InproceedingsLabelRule(),
754                        BibtexEntryType.INPROCEEDINGS);
755   }
756
757   /**
758    * Returns the indexed BasePanel.
759    * @param i Index of base
760    */
761   public BasePanel baseAt(int i) {
762     return (BasePanel) tabbedPane.getComponentAt(i);
763   }
764
765   public void showBaseAt(int i) {
766       tabbedPane.setSelectedIndex(i);
767   }
768
769   /**
770    * Returns the currently viewed BasePanel.
771    */
772   public BasePanel basePanel() {
773     return (BasePanel) tabbedPane.getSelectedComponent();
774   }
775
776   /**
777    * handle the color of active and inactive JTabbedPane tabs
778    */
779   private void markActiveBasePanel()
780   {
781     int now = tabbedPane.getSelectedIndex() ;
782     int len = tabbedPane.getTabCount() ;
783     if ((lastTabbedPanelSelectionIndex > -1) && (lastTabbedPanelSelectionIndex < len))
784       tabbedPane.setForegroundAt(lastTabbedPanelSelectionIndex, GUIGlobals.inActiveTabbed);
785     if ( (now > -1) &&  (now < len))
786       tabbedPane.setForegroundAt(now, GUIGlobals.activeTabbed);
787     lastTabbedPanelSelectionIndex = now ;
788   }
789
790   private int getTabIndex(JComponent comp) {
791     for (int i = 0; i < tabbedPane.getTabCount(); i++) {
792       if (tabbedPane.getComponentAt(i) == comp) {
793         return i;
794       }
795     }
796     return -1;
797   }
798
799   public JTabbedPane getTabbedPane() { return tabbedPane; }
800
801   public String getTabTitle(JComponent comp) {
802     return tabbedPane.getTitleAt(getTabIndex(comp));
803   }
804
805     public String getTabTooltip(JComponent comp) {
806         return tabbedPane.getToolTipTextAt(getTabIndex(comp));
807     }
808
809   public void setTabTitle(JComponent comp, String title, String toolTip) {
810       int index = getTabIndex(comp);
811       tabbedPane.setTitleAt(index, title);
812       tabbedPane.setToolTipTextAt(index, toolTip);
813   }
814
815   class GeneralAction
816       extends MnemonicAwareAction {
817     private String command;
818     public GeneralAction(String command, String text,
819                          String description, URL icon) {
820       super(new ImageIcon(icon));
821       this.command = command;
822       putValue(NAME, text);
823       putValue(SHORT_DESCRIPTION, Globals.lang(description));
824     }
825
826     public GeneralAction(String command, String text,
827                          String description, String imageName,
828                          KeyStroke key) {
829       super(GUIGlobals.getImage(imageName));
830       this.command = command;
831       putValue(NAME, text);
832       putValue(ACCELERATOR_KEY, key);
833       putValue(SHORT_DESCRIPTION, Globals.lang(description));
834     }
835
836       public GeneralAction(String command, String text) {
837           putValue(NAME, text);
838           this.command = command;
839       }
840
841       public GeneralAction(String command, String text, KeyStroke key) {
842           this.command = command;
843           putValue(NAME, text);
844           putValue(ACCELERATOR_KEY, key);
845       }
846
847       public GeneralAction(String command, String text, String description) {
848           this.command = command;
849           ImageIcon icon = GUIGlobals.getImage(command);
850           if (icon != null)
851               putValue(SMALL_ICON, icon);
852           putValue(NAME, text);
853           putValue(SHORT_DESCRIPTION, Globals.lang(description));
854       }
855
856       public GeneralAction(String command, String text, String description, KeyStroke key) {
857           this.command = command;
858           ImageIcon icon = GUIGlobals.getImage(command);
859           if (icon != null)
860               putValue(SMALL_ICON, icon);
861           putValue(NAME, text);
862           putValue(SHORT_DESCRIPTION, Globals.lang(description));
863           putValue(ACCELERATOR_KEY, key);
864       }
865
866   /*    public GeneralAction(String command, String text, String description,
867                            URL imageUrl, KeyStroke key) {
868       this.command = command;
869         ImageIcon icon = GUIGlobals.getImage(command);
870         if (icon != null)
871             putValue(SMALL_ICON, icon);
872       putValue(NAME, text);
873       putValue(SHORT_DESCRIPTION, Globals.lang(description));
874         putValue(ACCELERATOR_KEY, key);
875     }*/
876
877     public void actionPerformed(ActionEvent e) {
878       if (tabbedPane.getTabCount() > 0) {
879         try {
880           ( (BasePanel) (tabbedPane.getSelectedComponent ()))
881               .runCommand(command);
882         }
883         catch (Throwable ex) {
884           ex.printStackTrace();
885         }
886       }
887       else {
888         Util.pr("Action '" + command + "' must be disabled when no "
889                 + "database is open.");
890       }
891     }
892   }
893
894   /** This got removed when we introduced SearchManager2.
895        class IncrementalSearchAction extends AbstractAction {
896     public IncrementalSearchAction() {
897    super("Incremental search", new ImageIcon(GUIGlobals.searchIconFile));
898    putValue(SHORT_DESCRIPTION, Globals.lang("Start incremental search"));
899    putValue(ACCELERATOR_KEY, prefs.getKey("Incremental search"));
900     }
901     public void actionPerformed(ActionEvent e) {
902    if (tabbedPane.getTabCount() > 0)
903      searchManager.startIncrementalSearch();
904     }
905        }
906
907        class SearchAction extends AbstractAction {
908     public SearchAction() {
909    super("Search", new ImageIcon(GUIGlobals.searchIconFile));
910    putValue(SHORT_DESCRIPTION, Globals.lang("Start search"));
911    putValue(ACCELERATOR_KEY, prefs.getKey("Search"));
912     }
913     public void actionPerformed(ActionEvent e) {
914    if (tabbedPane.getTabCount() > 0)
915      searchManager.startSearch();
916     }
917        }
918    */
919
920   class NewEntryAction
921       extends MnemonicAwareAction {
922
923     String type = null; // The type of item to create.
924     KeyStroke keyStroke = null; // Used for the specific instances.
925
926     public NewEntryAction(KeyStroke key) {
927       // This action leads to a dialog asking for entry type.
928       super(GUIGlobals.getImage("add"));
929       putValue(NAME, "New entry");
930       putValue(ACCELERATOR_KEY, key);
931       putValue(SHORT_DESCRIPTION, Globals.lang("New BibTeX entry"));
932     }
933
934     public NewEntryAction(String type_) {
935       // This action leads to the creation of a specific entry.
936       putValue(NAME, Util.nCase(type_));
937       type = type_;
938     }
939
940     public NewEntryAction(String type_, KeyStroke key) {
941         // This action leads to the creation of a specific entry.
942         putValue(NAME, Util.nCase(type_));
943         putValue(ACCELERATOR_KEY, key);
944         type = type_;
945     }
946
947     public void actionPerformed(ActionEvent e) {
948       String thisType = type;
949       if (thisType == null) {
950         EntryTypeDialog etd = new EntryTypeDialog(ths);
951         Util.placeDialog(etd, ths);
952         etd.setVisible(true);
953         BibtexEntryType tp = etd.getChoice();
954         if (tp == null) {
955           return;
956         }
957         thisType = tp.getName();
958       }
959
960       if (tabbedPane.getTabCount() > 0) {
961         ( (BasePanel) (tabbedPane.getSelectedComponent()))
962             .newEntry(BibtexEntryType.getType(thisType));
963       }
964       else {
965         Util.pr("Action 'New entry' must be disabled when no "
966                 + "database is open.");
967       }
968     }
969   }
970
971   /*
972        private void setupDatabaseLayout() {
973     // This method is called whenever this frame has been provided
974     // with a database, and completes the layout.
975
976
977     if (file != null)
978    setTitle(GUIGlobals.baseTitle+file.getName());
979     else
980     setTitle(GUIGlobals.untitledTitle);
981
982     //DragNDropManager dndm = new DragNDropManager(this);
983
984     //setNonEmptyState();
985     Util.pr("JabRefFrame: Must set non-empty state.");
986     }*/
987
988   /**
989    * Refresh import menus.
990    */
991   public void setUpImportMenus() {
992     setUpImportMenu(importMenu, false);
993     setUpImportMenu(importNewMenu, true);
994   }
995
996   private void fillMenu() {
997       //mb.putClientProperty(Options.HEADER_STYLE_KEY, HeaderStyle.BOTH);
998       mb.setBorder(null);
999       JMenu file = subMenu("File"),
1000               sessions = subMenu("Sessions"),
1001               edit = subMenu("Edit"),
1002               bibtex = subMenu("BibTeX"),
1003               view = subMenu("View"),
1004               tools = subMenu("Tools"),
1005               web = subMenu("Web search"),
1006               options = subMenu("Options"),
1007               newSpec = subMenu("New entry..."),
1008               helpMenu = subMenu("Help");
1009
1010       setUpImportMenus();
1011
1012       newDatabaseMenu.add(newDatabaseAction);
1013       newDatabaseMenu.add(newSubDatabaseAction);
1014
1015       file.add(newDatabaseAction);
1016       file.add(open); //opendatabaseaction
1017       file.add(mergeDatabaseAction);
1018       file.add(save);
1019       file.add(saveAs);
1020       file.add(saveAll);
1021       file.add(saveSelectedAs);
1022       file.addSeparator();
1023       //file.add(importMenu);
1024       //file.add(importNewMenu);
1025       file.add(importNew);
1026       file.add(importCurrent);
1027       file.add(exportAll);
1028       file.add(exportSelected);
1029
1030       file.addSeparator();
1031       file.add(databaseProperties);
1032       file.addSeparator();
1033
1034       sessions.add(loadSessionAction);
1035       sessions.add(saveSessionAction);
1036       file.add(sessions);
1037       file.add(fileHistory);
1038       //file.addSeparator();
1039
1040       file.addSeparator();
1041       file.add(close);
1042       file.add(quit);
1043       mb.add(file);
1044       //edit.add(test);
1045       edit.add(undo);
1046       edit.add(redo);
1047       edit.addSeparator();
1048
1049       edit.add(cut);
1050       edit.add(copy);
1051       edit.add(paste);
1052       //edit.add(remove);
1053       edit.add(delete);
1054       edit.add(copyKey);
1055       edit.add(copyCiteKey);
1056       //edit.add(exportToClipboard);
1057       edit.addSeparator();
1058       edit.add(mark);
1059       edit.add(unmark);
1060       edit.add(unmarkAll);
1061       edit.addSeparator();
1062       edit.add(selectAll);
1063       mb.add(edit);
1064       view.add(nextTab);
1065       view.add(prevTab);
1066       view.add(sortTabs);
1067       view.addSeparator();
1068       view.add(toggleGroups);
1069       view.add(togglePreview);
1070       view.add(switchPreview);
1071       view.addSeparator();
1072       view.add(toggleHighlightAny);
1073       view.add(toggleHighlightAll);
1074       mb.add(view);
1075
1076       bibtex.add(newEntryAction);
1077       for (int i = 0; i < newSpecificEntryAction.length; i++) {
1078           newSpec.add(newSpecificEntryAction[i]);
1079       }
1080       bibtex.add(newSpec);
1081       bibtex.add(plainTextImport);
1082       bibtex.addSeparator();
1083       bibtex.add(editEntry);
1084       bibtex.add(importCiteSeer);
1085       bibtex.add(editPreamble);
1086       bibtex.add(editStrings);
1087       mb.add(bibtex);
1088
1089       tools.add(normalSearch);
1090       tools.add(incrementalSearch);
1091       tools.add(replaceAll);
1092       tools.add(new MassSetFieldAction(this));
1093       tools.add(makeKeyAction);
1094
1095       // [kiar] I think we should group these festures
1096       tools.add(checkAndFix);
1097       checkAndFix.add(dupliCheck);
1098       //checkAndFix.add(strictDupliCheck);
1099       checkAndFix.add(autoSetFile);
1100       checkAndFix.add(autoSetPdf);
1101       checkAndFix.add(autoSetPs);
1102       checkAndFix.add(integrityCheckAction);
1103       checkAndFix.addSeparator();
1104       checkAndFix.add(upgradeExternalLinks);
1105
1106       tools.addSeparator();
1107       tools.add(manageSelectors);
1108
1109       tools.add(pushExternalButton.getMenuAction());
1110       tools.add(writeXmpAction);
1111
1112       //tools.add(emacsPushAction);
1113       //tools.add(lyxPushAction);
1114       //tools.add(winEdtPushAction);
1115       //tools.add(latexEditorPushAction);
1116       //tools.add(fetchAuthorMedline);
1117       tools.addSeparator();
1118       tools.add(openFile);
1119       tools.add(openPdf);
1120       tools.add(openUrl);
1121       tools.addSeparator();
1122       tools.add(newSubDatabaseAction);
1123
1124       tools.addSeparator();
1125       tools.add(abbreviateIso);
1126       tools.add(abbreviateMedline);
1127       tools.add(unabbreviate);
1128
1129       // TODO: Temporary for 2.2 release: we should perhaps find a better solution:
1130       tools.addSeparator();
1131       tools.add(new ExpandEndnoteFilters(ths));
1132       
1133       mb.add(tools);
1134
1135       web.add(fetchMedline);
1136       web.add(citeSeerPanelAction);
1137       web.add(fetchCiteSeer);
1138       ieex = new GeneralFetcher(sidePaneManager, this, ieeexplorerFetcher);
1139       arxiv = new GeneralFetcher(sidePaneManager, this, arxivFetcher);
1140       web.add(ieex.getAction());
1141       web.add(arxiv.getAction());
1142
1143       mb.add(web);
1144
1145       options.add(showPrefs);
1146       AbstractAction customizeAction = new CustomizeEntryTypeAction();
1147       AbstractAction genFieldsCustomization = new GenFieldsCustomizationAction();
1148       options.add(customizeAction);
1149       options.add(genFieldsCustomization);
1150       options.add(customExpAction);
1151       options.add(customImpAction);
1152       options.add(customFileTypesAction);
1153       options.add(manageJournals);
1154
1155       /*options.add(new AbstractAction("Font") {
1156       public void actionPerformed(ActionEvent e) {
1157           // JDialog dl = new EntryCustomizationDialog(ths);
1158           Font f=new FontSelectorDialog
1159         (ths, GUIGlobals.CURRENTFONT).getSelectedFont();
1160        if(f==null)
1161         return;
1162        else
1163         GUIGlobals.CURRENTFONT=f;
1164        // updatefont
1165        prefs.put("fontFamily", GUIGlobals.CURRENTFONT.getFamily());
1166        prefs.putInt("fontStyle", GUIGlobals.CURRENTFONT.getStyle());
1167        prefs.putInt("fontSize", GUIGlobals.CURRENTFONT.getSize());
1168        if (tabbedPane.getTabCount() > 0) {
1169         for (int i=0; i<tabbedPane.getTabCount(); i++) {
1170          baseAt(i).entryTable.updateFont();
1171          baseAt(i).refreshTable();
1172         }
1173        }
1174       }
1175       });*/
1176
1177       //options.add(selectKeys);
1178       mb.add(options);
1179
1180       helpMenu.add(help);
1181       helpMenu.add(contents);
1182       helpMenu.addSeparator();
1183 //old about    helpMenu.add(about);
1184       helpMenu.add(about);
1185       mb.add(helpMenu);
1186       helpMenu.addSeparator();
1187       helpMenu.add(errorConsole);
1188   }
1189
1190     private JMenu subMenu(String name) {
1191         name = Globals.menuTitle(name);
1192         int i = name.indexOf('&');
1193         JMenu res;
1194         if (i >= 0) {
1195             res = new JMenu(name.substring(0, i)+name.substring(i+1));
1196             char mnemonic = Character.toUpperCase(name.charAt(i+1));
1197             res.setMnemonic((int)mnemonic);
1198         }
1199         else res = new JMenu(name);
1200
1201         return res;
1202     }
1203
1204   private void createToolBar() {
1205     tlb.putClientProperty(Options.HEADER_STYLE_KEY, HeaderStyle.BOTH);
1206     tlb.setBorder(null);
1207     tlb.setRollover(true);
1208
1209     //tlb.setBorderPainted(true);
1210     //tlb.setBackground(GUIGlobals.lightGray);
1211     //tlb.setForeground(GUIGlobals.lightGray);
1212     tlb.setFloatable(false);
1213     tlb.addAction(newDatabaseAction);
1214     tlb.addAction(open);
1215     tlb.addAction(save);
1216     tlb.addAction(saveAll);
1217     
1218     tlb.addSeparator();
1219     tlb.addAction(cut);
1220     tlb.addAction(copy);
1221     tlb.addAction(paste);
1222     tlb.addAction(undo);
1223     tlb.addAction(redo);
1224
1225     tlb.addSeparator();
1226     tlb.addAction(newEntryAction);
1227     tlb.addAction(editEntry);
1228     tlb.addAction(editPreamble);
1229     tlb.addAction(editStrings);
1230     tlb.addAction(makeKeyAction);
1231
1232
1233     tlb.addSeparator();
1234     tlb.addAction(mark);
1235     tlb.addAction(unmark);
1236
1237     tlb.addSeparator();
1238     searchToggle = new JToggleButton(toggleSearch);
1239     searchToggle.setText(null);
1240     if (!Globals.ON_MAC)
1241         searchToggle.setMargin(marg);
1242     tlb.add(searchToggle);
1243
1244     previewToggle = new JToggleButton(togglePreview);
1245     previewToggle.setText(null);
1246     if (!Globals.ON_MAC)
1247         previewToggle.setMargin(marg);
1248     tlb.add(previewToggle);
1249     tlb.addSeparator();
1250
1251     groupToggle = new JToggleButton(toggleGroups);
1252     groupToggle.setText(null);
1253     if (!Globals.ON_MAC)
1254         groupToggle.setMargin(marg);
1255     tlb.add(groupToggle);
1256
1257
1258     highlightAny = new JToggleButton(toggleHighlightAny);
1259     highlightAny.setText(null);
1260     if (!Globals.ON_MAC)
1261         highlightAny.setMargin(marg);
1262     tlb.add(highlightAny);
1263     highlightAll = new JToggleButton(toggleHighlightAll);
1264     highlightAll.setText(null);
1265     if (!Globals.ON_MAC)
1266         highlightAll.setMargin(marg);
1267     tlb.add(highlightAll);
1268
1269     tlb.addSeparator();
1270
1271       // Removing the separate push-to buttons, replacing them by the
1272       // multipurpose button:
1273       //tlb.addAction(emacsPushAction);
1274       //tlb.addAction(lyxPushAction);
1275       //tlb.addAction(winEdtPushAction);
1276       tlb.add(pushExternalButton.getComponent());
1277
1278       tlb.addAction(openFile);
1279     //tlb.addAction(openPdf);
1280     //tlb.addAction(openUrl);
1281
1282
1283     //tlb.addSeparator();
1284     //tlb.addAction(showPrefs);
1285     tlb.add(Box.createHorizontalGlue());
1286     //tlb.add(new JabRefLabel(GUIGlobals.frameTitle+" "+GUIGlobals.version));
1287
1288     tlb.addAction(closeDatabaseAction);
1289     //Insets margin = new Insets(0, 0, 0, 0);
1290     //for (int i=0; i<tlb.getComponentCount(); i++)
1291     //  ((JButton)tlb.getComponentAtIndex(i)).setMargin(margin);
1292
1293   }
1294
1295   private class JabRefLabel
1296       extends JPanel {
1297     private String label;
1298     public JabRefLabel(String name) {
1299       label = name;
1300     }
1301
1302     public void paint(Graphics g) {
1303       Graphics2D g2 = (Graphics2D) g;
1304       g2.setColor(GUIGlobals.nullFieldColor);
1305       g2.setFont(GUIGlobals.jabRefFont);
1306       FontMetrics fm = g2.getFontMetrics();
1307       int width = fm.stringWidth(label);
1308       g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
1309                           RenderingHints.VALUE_ANTIALIAS_ON);
1310       g2.drawString(label, getWidth() - width - 7, getHeight() - 10);
1311
1312
1313     }
1314   }
1315
1316   private JMenuItem mItem(AbstractAction a, KeyStroke ks) {
1317     // Set up a menu item with action and accelerator key.
1318     JMenuItem mi = new JMenuItem();
1319     mi.setAction(a);
1320     if (ks != null) {
1321       mi.setAccelerator(ks);
1322     }
1323     return mi;
1324   }
1325
1326   //private void setupMainPanel() {
1327
1328
1329   /*public Completer getAutoCompleter(String field) {
1330     return (Completer)autoCompleters.get(field);
1331     }
1332
1333
1334        public void assignAutoCompleters() {
1335     // Set up which fields should have autocompletion. This should
1336     // probably be made customizable. Existing Completer objects are
1337     // forgotten. The completers must be updated towards the database.
1338     byte[] fields = prefs.getByteArray("autoCompFields");
1339     autoCompleters = new Hashtable();
1340     for (int i=0; i<fields.length; i++) {
1341    autoCompleters.put(GUIGlobals.ALL_FIELDS[fields[i]], new Completer());
1342     }
1343
1344        }
1345
1346        public void updateAutoCompleters() {
1347     if (database != null)
1348    database.setCompleters(autoCompleters);
1349    }*/
1350
1351  public void output(final String s) {
1352
1353       SwingUtilities.invokeLater(new Runnable() {
1354           public void run() {
1355               statusLine.setText(s);
1356               statusLine.repaint();
1357           }
1358       });
1359   }
1360
1361   public void stopShowingSearchResults() {
1362     for (int i = 0; i < tabbedPane.getTabCount(); i++) {
1363       baseAt(i).stopShowingSearchResults();
1364     }
1365   }
1366
1367   protected List openDatabaseOnlyActions = new LinkedList();
1368   protected List severalDatabasesOnlyActions = new LinkedList();
1369   
1370     protected void initActions() {
1371         openDatabaseOnlyActions = new LinkedList();
1372         openDatabaseOnlyActions.addAll(Arrays.asList(new Object[] { manageSelectors,
1373             mergeDatabaseAction, newSubDatabaseAction, close, save, saveAs, saveSelectedAs, undo,
1374             redo, cut, delete, copy, paste, mark, unmark, unmarkAll, editEntry, importCiteSeer,
1375             selectAll, copyKey, copyCiteKey, editPreamble, editStrings, toggleGroups, toggleSearch,
1376             makeKeyAction, normalSearch,
1377             incrementalSearch, replaceAll, importMenu, exportMenu, fetchMedline, fetchCiteSeer,
1378                 openPdf, openUrl, togglePreview, dupliCheck, /*strictDupliCheck,*/ highlightAll,
1379             highlightAny, citeSeerPanelAction, newEntryAction, plainTextImport,
1380             closeDatabaseAction, switchPreview, integrityCheckAction, autoSetPdf, autoSetPs,
1381             toggleHighlightAny, toggleHighlightAll, databaseProperties, abbreviateIso,
1382             abbreviateMedline, unabbreviate, ieex.getAction(), arxiv.getAction(), exportAll, exportSelected,
1383             importCurrent, saveAll}));
1384
1385         openDatabaseOnlyActions.addAll(Arrays.asList(newSpecificEntryAction));
1386
1387         severalDatabasesOnlyActions = new LinkedList();
1388         severalDatabasesOnlyActions.addAll(Arrays
1389             .asList(new Object[] { nextTab, prevTab, sortTabs }));
1390
1391         tabbedPane.addChangeListener(new ChangeListener() {
1392             public void stateChanged(ChangeEvent event) {
1393                 updateEnabledState();
1394             }
1395         });
1396         
1397         
1398
1399     }
1400
1401     public static void setEnabled(List l, boolean enabled) {
1402         Iterator i = l.iterator();
1403         while (i.hasNext()) {
1404             Object o = i.next();
1405             if (o instanceof Action)
1406                 ((Action)o).setEnabled(enabled);
1407             if (o instanceof Component)
1408                 ((Component)o).setEnabled(enabled);
1409         }
1410     }
1411
1412     protected int previousTabCount = -1;
1413     
1414     /**
1415      * Enable or Disable all actions based on the number of open tabs.
1416      * 
1417      * The action that are affected are set in initActions.
1418      */
1419     protected void updateEnabledState() {
1420         int tabCount = tabbedPane.getTabCount();
1421         if (tabCount != previousTabCount){
1422             previousTabCount = tabCount;
1423             setEnabled(openDatabaseOnlyActions, tabCount > 0);
1424             setEnabled(severalDatabasesOnlyActions, tabCount > 1);
1425         }
1426     }
1427
1428   /**
1429    * This method causes all open BasePanels to set up their tables
1430    * anew. When called from PrefsDialog2, this updates to the new
1431    * settings.
1432    */
1433   public void setupAllTables() {
1434     // This action can be invoked without an open database, so
1435     // we have to check if we have one before trying to invoke
1436     // methods to execute changes in the preferences.
1437
1438     // We want to notify all tabs about the changes to
1439     // avoid problems when changing the column set.
1440     for (int i = 0; i < tabbedPane.getTabCount(); i++) {
1441       BasePanel bf = baseAt(i);
1442
1443       // Update tables:
1444       if (bf.database != null) {
1445         bf.setupMainPanel();
1446
1447       }
1448
1449     }
1450   }
1451
1452   public BasePanel addTab(BibtexDatabase db, File file, HashMap meta, String encoding, boolean raisePanel) {
1453       BasePanel bp = new BasePanel(ths, db, file, meta, encoding);
1454       addTab(bp, file, raisePanel);
1455       return bp;
1456   }
1457
1458     public void addTab(BasePanel bp, File file, boolean raisePanel) {
1459         tabbedPane.add((file != null ? file.getName(): Globals.lang(GUIGlobals.untitledTitle)),
1460                        bp);
1461         tabbedPane.setToolTipTextAt(tabbedPane.getTabCount()-1,
1462                 file != null ? file.getAbsolutePath() : null);
1463         if (raisePanel) {
1464             tabbedPane.setSelectedComponent(bp);
1465         }
1466     }
1467
1468   class SelectKeysAction
1469       extends AbstractAction {
1470     public SelectKeysAction() {
1471       super(Globals.lang("Customize key bindings"));
1472     }
1473
1474     public void actionPerformed(ActionEvent e) {
1475       KeyBindingsDialog d = new KeyBindingsDialog
1476           ( (HashMap) prefs.getKeyBindings().clone(),
1477            prefs.getDefaultKeys());
1478       d.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
1479       d.pack(); //setSize(300,500);
1480       Util.placeDialog(d, ths);
1481       d.setVisible(true);
1482       if (d.getAction()) {
1483         prefs.setNewKeyBindings(d.getNewKeyBindings());
1484         JOptionPane.showMessageDialog
1485             (ths,
1486              Globals.lang("Your new key bindings have been stored.") + "\n"
1487              + Globals.lang("You must restart JabRef for the new key "
1488                             + "bindings to work properly."),
1489              Globals.lang("Key bindings changed"),
1490              JOptionPane.INFORMATION_MESSAGE);
1491       }
1492     }
1493   }
1494
1495   /**
1496    * The action concerned with closing the window.
1497    */
1498   class CloseAction
1499       extends MnemonicAwareAction {
1500     public CloseAction() {
1501       putValue(NAME, "Quit");
1502       putValue(SHORT_DESCRIPTION, Globals.lang("Quit JabRef"));
1503       putValue(ACCELERATOR_KEY, prefs.getKey("Quit JabRef"));
1504       //putValue(ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_Q,
1505       //    Toolkit.getDefaultToolkit().getMenuShortcutKeyMask()));
1506
1507     }
1508
1509     public void actionPerformed(ActionEvent e) {
1510       quit();
1511     }
1512   }
1513
1514   // The action for closing the current database and leaving the window open.
1515     CloseDatabaseAction closeDatabaseAction = new CloseDatabaseAction();
1516
1517     class CloseDatabaseAction extends MnemonicAwareAction {
1518         public CloseDatabaseAction() {
1519             super(GUIGlobals.getImage("close"));
1520             putValue(NAME, "Close database");
1521             putValue(SHORT_DESCRIPTION, Globals.lang("Close the current database"));
1522             putValue(ACCELERATOR_KEY, prefs.getKey("Close database"));
1523         }
1524
1525         public void actionPerformed(ActionEvent e) {
1526             // Ask here if the user really wants to close, if the base
1527             // has not been saved since last save.
1528             boolean close = true;
1529             if (basePanel() == null) { // when it is initially empty
1530                 return; // nbatada nov 7
1531             }
1532
1533             if (basePanel().baseChanged) {
1534                 int answer = JOptionPane.showConfirmDialog(ths, Globals
1535                     .lang("Database has changed. Do you want to save " + "before closing?"),
1536                     Globals.lang("Save before closing"), JOptionPane.YES_NO_CANCEL_OPTION);
1537                 if ((answer == JOptionPane.CANCEL_OPTION) || (answer == JOptionPane.CLOSED_OPTION)) {
1538                     close = false; // The user has cancelled.
1539                 }
1540                 if (answer == JOptionPane.YES_OPTION) {
1541                     // The user wants to save.
1542                     try {
1543                         SaveDatabaseAction saveAction = new SaveDatabaseAction(basePanel());
1544                         saveAction.runCommand();
1545                         if (saveAction.isCancelled() || !saveAction.isSuccess())
1546                             // The action either not cancelled or unsuccessful.
1547                             // Break! 
1548                             close = false;
1549                     } catch (Throwable ex) {
1550                         // Something prevented the file
1551                         // from being saved. Break!!!
1552                         close = false;
1553                     }
1554
1555                 }
1556             }
1557
1558             if (close) {
1559                 basePanel().cleanUp();
1560                 tabbedPane.remove(basePanel());
1561                 if (tabbedPane.getTabCount() > 0) {
1562                     markActiveBasePanel();
1563                 }
1564                 updateEnabledState(); // Man, this is what I call a bug that this is not called.
1565                 output(Globals.lang("Closed database") + ".");
1566                 System.gc(); // Test
1567             }
1568         }
1569     }
1570
1571
1572   // The action concerned with opening a new database.
1573   class NewDatabaseAction
1574       extends MnemonicAwareAction {
1575     public NewDatabaseAction() {
1576         super(GUIGlobals.getImage("new"));
1577         putValue(NAME, "New database");
1578         putValue(SHORT_DESCRIPTION, Globals.lang("New BibTeX database"));
1579         //putValue(MNEMONIC_KEY, GUIGlobals.newKeyCode);
1580     }
1581
1582     public void actionPerformed(ActionEvent e) {
1583         // Create a new, empty, database.
1584         BibtexDatabase database = new BibtexDatabase();
1585         addTab(database, null, null, Globals.prefs.get("defaultEncoding"), true);
1586         output(Globals.lang("New database created."));
1587     }
1588   }
1589
1590 class ImportCiteSeerAction
1591         extends MnemonicAwareAction {
1592
1593     public ImportCiteSeerAction() {
1594         super(GUIGlobals.getImage("citeseer"));
1595         putValue(NAME, "Import Fields from CiteSeer");
1596         putValue(SHORT_DESCRIPTION, Globals.lang("Import Fields from CiteSeer Database"));
1597         putValue(ACCELERATOR_KEY, prefs.getKey("Import Fields from CiteSeer")); // Key defined in MenuTitles!
1598         }
1599
1600         public void actionPerformed(ActionEvent e) {
1601
1602                 if(citeSeerFetcher.activateImportFetcher()) {
1603
1604
1605                         (new Thread() {
1606
1607                                 BasePanel currentBp;
1608                                 BibtexEntry toShow;
1609                                 //String id;
1610                                 int[] clickedOn = null;
1611
1612                                 class UpdateComponent implements Runnable {
1613                                         boolean changesMade;
1614
1615                                         UpdateComponent(boolean changesMade) {
1616                                                 this.changesMade = changesMade;
1617                                         }
1618
1619                                         public void run() {
1620                                             citeSeerFetcher.endImportCiteSeerProgress();
1621                                             if (changesMade)
1622                                                     currentBp.markBaseChanged();
1623                                                 //for(int i=0; i < clickedOn.length; i++)
1624                                                 //        currentBp.entryTable.addRowSelectionInterval(i,i);
1625                                                 //currentBp.showEntry(toShow);
1626                                                 output(Globals.lang("Completed Import Fields from CiteSeer."));
1627                                         }
1628                                 }
1629
1630                             public void run() {
1631                                 currentBp = (BasePanel) tabbedPane.getSelectedComponent();
1632                                         // We demand that at least one row is selected.
1633
1634                                         int rowCount = currentBp.mainTable.getSelectedRowCount();
1635                                         if (rowCount >= 1) {
1636                                                 clickedOn = currentBp.mainTable.getSelectedRows();
1637                                         } else {
1638                                                 JOptionPane.showMessageDialog(currentBp.frame(),
1639                                                 Globals.lang("You must select at least one row to perform this operation."),
1640                                                 Globals.lang("CiteSeer Import Error"),
1641                                                 JOptionPane.WARNING_MESSAGE);
1642                                         }
1643                                         toShow = (BibtexEntry)currentBp.mainTable.getSelected().get(0);
1644                                         if (clickedOn != null) {
1645                                                 citeSeerFetcher.beginImportCiteSeerProgress();
1646                                                 NamedCompound citeseerNamedCompound =
1647                                                         new NamedCompound(Globals.lang("CiteSeer Import Fields"));
1648                                                 boolean newValues = citeSeerFetcher.importCiteSeerEntries(clickedOn, citeseerNamedCompound);
1649                                                 if (newValues) {
1650                                                         citeseerNamedCompound.end();
1651                                                         currentBp.undoManager.addEdit(citeseerNamedCompound);
1652                                                 }
1653                                                 UpdateComponent updateComponent = new UpdateComponent(newValues);
1654                                                 SwingUtilities.invokeLater(updateComponent);
1655                                         }
1656                                         citeSeerFetcher.deactivateImportFetcher();
1657                             }
1658                         }).start();
1659                 } else {
1660                         JOptionPane.showMessageDialog(tabbedPane.getSelectedComponent(),
1661                                         Globals.lang("A CiteSeer import operation is currently in progress.") + "  " +
1662                                         Globals.lang("Please wait until it has finished."),
1663                                         Globals.lang("CiteSeer Import Error"),
1664                                         JOptionPane.WARNING_MESSAGE);
1665                 }
1666         }
1667 }
1668
1669 class FetchCiteSeerAction
1670         extends MnemonicAwareAction {
1671
1672                 public FetchCiteSeerAction() {
1673                     super(GUIGlobals.getImage("citeseer"));
1674                     putValue(NAME, "Fetch citations from CiteSeer");
1675
1676                     putValue(SHORT_DESCRIPTION, Globals.lang("Fetch Articles Citing your Database"));
1677                     putValue(ACCELERATOR_KEY, prefs.getKey("Fetch citations from CiteSeer"));
1678                 }
1679
1680                 public void actionPerformed(ActionEvent e) {
1681
1682                         if(citeSeerFetcher.activateCitationFetcher()) {
1683                                 sidePaneManager.show("CiteSeerProgress");
1684                                 (new Thread() {
1685                                         BasePanel newBp;
1686                                         BasePanel targetBp;
1687                                         BibtexDatabase newDatabase;
1688                                         BibtexDatabase targetDatabase;
1689
1690                                         Runnable updateComponent = new Runnable() {
1691
1692                                                 /* TODO: This should probably be selectable on/off
1693                                                  * in the preferences window, but for now all
1694                                                  * Citation fetcher operations will sort by citation count.
1695                                                  */
1696                                                 private void setSortingByCitationCount() {
1697                                                         newBp.sortingByCiteSeerResults = true;
1698                                                 }
1699
1700                                                 public void run() {
1701                                                         setSortingByCitationCount();
1702                                                         tabbedPane.add(Globals.lang(GUIGlobals.untitledTitle), newBp);
1703                                                         tabbedPane.setSelectedComponent(newBp);
1704                                                         output(Globals.lang("Fetched all citations from target database."));
1705                                                         citeSeerFetcher.deactivateCitationFetcher();
1706                                                 }
1707                                         };
1708
1709                                   public void run() {
1710                                         try {
1711                                                 newBp = new BasePanel(ths);
1712                                                 int errorCode;
1713                                                 targetBp = (BasePanel) tabbedPane.getSelectedComponent();
1714                                                 newDatabase = newBp.getDatabase();
1715                                                 targetDatabase = targetBp.getDatabase();
1716                                                 errorCode = citeSeerFetcher.populate(newDatabase, targetDatabase);
1717                                                 if (newDatabase.getEntryCount() > 0) {
1718                                                         SwingUtilities.invokeLater(updateComponent);
1719                                                 } else if(errorCode == 0) {
1720                                                         SwingUtilities.invokeLater(citeSeerFetcher.getEmptyFetchSetDialog());
1721                                             } else {
1722                                                     citeSeerFetcher.deactivateCitationFetcher();
1723                                             }
1724                                         }
1725                                         catch (Exception ex) {
1726                                           ex.printStackTrace();
1727                                         }
1728                                   }
1729                                 }).start();
1730                         } else {
1731                             JOptionPane.showMessageDialog(tabbedPane.getSelectedComponent(),
1732                                                 Globals.lang("A CiteSeer fetch operation is currently in progress.") + "  " +
1733                                                 Globals.lang("Please wait until it has finished."),
1734                                                 Globals.lang("CiteSeer Fetch Error"),
1735                                                 JOptionPane.WARNING_MESSAGE);
1736                         }
1737                 }
1738         }
1739
1740
1741
1742     // The action concerned with generate a new (sub-)database from latex aux file.
1743     class NewSubDatabaseAction extends MnemonicAwareAction
1744     {
1745       public NewSubDatabaseAction()
1746       {
1747         super(GUIGlobals.getImage("new"));
1748         putValue(NAME, "New subdatabase based on AUX file" );
1749         putValue( SHORT_DESCRIPTION, Globals.lang( "New BibTeX subdatabase" ) ) ;
1750             //putValue(MNEMONIC_KEY, GUIGlobals.newKeyCode);
1751       }
1752
1753       public void actionPerformed( ActionEvent e )
1754       {
1755         // Create a new, empty, database.
1756
1757         FromAuxDialog dialog = new FromAuxDialog(ths, "", true, ths.tabbedPane) ;
1758
1759         Util.placeDialog(dialog, ths);
1760         dialog.setVisible(true) ;
1761
1762         if (dialog.okPressed())
1763         {
1764           BasePanel bp = new BasePanel( ths,
1765                                         dialog.getGenerateDB(),   // database
1766                                         null,                     // file
1767                                         null, Globals.prefs.get("defaultEncoding"));                     // meta data
1768           tabbedPane.add( Globals.lang( GUIGlobals.untitledTitle ), bp ) ;
1769           tabbedPane.setSelectedComponent( bp ) ;
1770           output( Globals.lang( "New database created." ) ) ;
1771         }
1772       }
1773     }
1774
1775
1776     // The action should test the database and report errors/warnings
1777     class IntegrityCheckAction extends AbstractAction
1778     {
1779       public IntegrityCheckAction()
1780       {
1781         super(Globals.menuTitle("Integrity check"),
1782                GUIGlobals.getImage("integrityCheck")) ;
1783                //putValue( SHORT_DESCRIPTION, "integrity" ) ;  //Globals.lang( "integrity" ) ) ;
1784             //putValue(MNEMONIC_KEY, GUIGlobals.newKeyCode);
1785       }
1786
1787       public void actionPerformed( ActionEvent e )
1788       {
1789        Object selComp = tabbedPane.getSelectedComponent() ;
1790        if (selComp != null)
1791        {
1792          BasePanel bp = ( BasePanel ) selComp ;
1793          BibtexDatabase refBase = bp.getDatabase() ;
1794          if (refBase != null)
1795          {
1796              IntegrityWizard wizard = new IntegrityWizard(ths, basePanel()) ;
1797              Util.placeDialog(wizard, ths);
1798              wizard.setVisible(true) ;
1799
1800          }
1801        }
1802       }
1803     }
1804
1805   class FetchMedlineAction extends MnemonicAwareAction {
1806         public FetchMedlineAction() {
1807             super(GUIGlobals.getImage("medline"));
1808             putValue(NAME, "Fetch Medline");
1809             putValue(ACCELERATOR_KEY, prefs.getKey("Fetch Medline"));
1810             putValue(SHORT_DESCRIPTION, Globals.lang("Fetch Medline by ID"));
1811         }
1812
1813         public void actionPerformed(ActionEvent e) {
1814             if (tabbedPane.getTabCount() > 0) {
1815                 sidePaneManager.toggle("fetchMedline");
1816                 if (sidePaneManager.isComponentVisible("fetchMedline")) {
1817                     new FocusRequester(medlineFetcher.getTextField());
1818                 }
1819             }
1820         }
1821     }
1822
1823   class CiteSeerPanelAction extends MnemonicAwareAction {
1824         public CiteSeerPanelAction() {
1825             super(GUIGlobals.getImage("medline"));
1826             putValue(NAME, "Fetch CiteSeer");
1827             putValue(ACCELERATOR_KEY, prefs.getKey("Fetch CiteSeer"));
1828             putValue(SHORT_DESCRIPTION, Globals.lang("Fetch CiteSeer by ID"));
1829         }
1830
1831         public void actionPerformed(ActionEvent e) {
1832             if (tabbedPane.getTabCount() > 0) {
1833                 sidePaneManager.toggle("CiteSeerPanel");
1834                 if (sidePaneManager.isComponentVisible("CiteSeerPanel")) {
1835                     new FocusRequester(citeSeerFetcherPanel.getTextField());
1836                 }
1837             }
1838         }
1839     }
1840
1841   // The action for opening the preferences dialog.
1842   AbstractAction showPrefs = new ShowPrefsAction();
1843
1844   class ShowPrefsAction
1845       extends MnemonicAwareAction {
1846     public ShowPrefsAction() {
1847       super(GUIGlobals.getImage("preferences"));
1848       putValue(NAME, "Preferences");
1849       putValue(SHORT_DESCRIPTION, Globals.lang("Preferences"));
1850     }
1851
1852     public void actionPerformed(ActionEvent e) {
1853       preferences();
1854     }
1855   }
1856
1857   /**
1858    * This method does the job of adding imported entries into the active database, or into a new one.
1859    * It shows the ImportInspectionDialog if preferences indicate it should be used. Otherwise it imports
1860    * directly.
1861    * @param panel The BasePanel to add to.
1862    * @param entries The entries to add.
1863    * @param filename Name of the file where the import came from.
1864    * @param openInNew Should the entries be imported into a new database?
1865    * @param callBack The callback for the ImportInspectionDialog to use.
1866    */
1867   public void addImportedEntries(final BasePanel panel, final List entries, String filename, boolean openInNew,
1868                                  ImportInspectionDialog.CallBack callBack) {
1869       // Use the import inspection dialog if it is enabled in preferences, and (there are more than
1870       // one entry or the inspection dialog is also enabled for single entries):
1871       if (Globals.prefs.getBoolean("useImportInspectionDialog") &&
1872               (Globals.prefs.getBoolean("useImportInspectionDialogForSingle") || (entries.size() > 1))) {
1873                 ImportInspectionDialog diag = new ImportInspectionDialog(ths, panel,
1874                         BibtexFields.DEFAULT_INSPECTION_FIELDS,
1875                         Globals.lang("Import"), openInNew);
1876                 diag.addEntries(entries);
1877                 diag.addCallBack(callBack);
1878                 diag.entryListComplete();
1879                 Util.placeDialog(diag, ths);
1880                 diag.setVisible(true);
1881         diag.toFront();
1882         } else {
1883             ths.addBibEntries(entries, filename, openInNew);
1884           if ((panel != null) && (entries.size() == 1)) {
1885               SwingUtilities.invokeLater(new Runnable() {
1886                   public void run() {
1887                       panel.highlightEntry((BibtexEntry)entries.get(0));
1888                   }
1889               });
1890
1891
1892           }
1893        }
1894   }
1895
1896     /**
1897      * Adds the entries to the database, possibly checking for duplicates first.
1898      * @param filename If non-null, a message is printed to the status line describing
1899      * how many entries were imported, and from which file. If null, the message will not
1900      * be printed.
1901      * @param intoNew Determines if the entries will be put in a new database or in the current
1902      * one.
1903      */
1904   public int addBibEntries(java.util.List bibentries, String filename,
1905                            boolean intoNew) {
1906           if (bibentries == null || bibentries.size() == 0) {
1907
1908       // No entries found. We need a message for this.
1909       JOptionPane.showMessageDialog(ths, Globals.lang("No entries found. Please make sure you are "
1910                                                       +"using the correct import filter."), Globals.lang("Import failed"),
1911                                     JOptionPane.ERROR_MESSAGE);
1912       return 0;
1913     }
1914
1915       int addedEntries = 0;
1916
1917     // Set owner and timestamp fields:
1918     Util.setAutomaticFields(bibentries, Globals.prefs.getBoolean("overwriteOwner"),
1919             Globals.prefs.getBoolean("overwriteTimeStamp"));
1920
1921     if (intoNew || (tabbedPane.getTabCount() == 0)) {
1922       // Import into new database.
1923       BibtexDatabase database = new BibtexDatabase();
1924       Iterator it = bibentries.iterator();
1925       while (it.hasNext()) {
1926         BibtexEntry entry = (BibtexEntry) it.next();
1927
1928         try {
1929           entry.setId(Util.createNeutralId());
1930           database.insertEntry(entry);
1931         }
1932         catch (KeyCollisionException ex) {
1933           //ignore
1934           System.err.println("KeyCollisionException [ addBibEntries(...) ]");
1935         }
1936       }
1937       HashMap meta = new HashMap();
1938       // Metadata are only put in bibtex files, so we will not find it
1939       // in imported files. Instead we pass an empty HashMap.
1940       BasePanel bp = new BasePanel(ths, database, null, meta, Globals.prefs.get("defaultEncoding"));
1941       /*
1942             if (prefs.getBoolean("autoComplete")) {
1943             db.setCompleters(autoCompleters);
1944             }
1945        */
1946       addedEntries = database.getEntryCount();
1947       tabbedPane.add(GUIGlobals.untitledTitle, bp);
1948       bp.markBaseChanged();
1949       tabbedPane.setSelectedComponent(bp);
1950       if (filename != null)
1951           output(Globals.lang("Imported database") + " '" + filename + "' " +
1952                  Globals.lang("with") + " " +
1953                  database.getEntryCount() + " " +
1954                  Globals.lang("entries into new database") + ".");
1955     }
1956     else {
1957       // Import into current database.
1958       boolean checkForDuplicates = true;
1959       BasePanel basePanel = basePanel();
1960       BibtexDatabase database = basePanel.database;
1961       int oldCount = database.getEntryCount();
1962       NamedCompound ce = new NamedCompound(Globals.lang("Import entries"));
1963       Iterator it = bibentries.iterator();
1964
1965       mainLoop: while (it.hasNext()) {
1966         BibtexEntry entry = (BibtexEntry) it.next();
1967         boolean dupli = false;
1968         // Check for duplicates among the current entries:
1969         if (checkForDuplicates) {
1970             loop: for (Iterator i2=database.getKeySet().iterator();
1971                        i2.hasNext();) {
1972                 BibtexEntry existingEntry = database.getEntryById((String)i2.next());
1973                 if (Util.isDuplicate(entry, existingEntry,
1974                                      Globals.duplicateThreshold)) {
1975                     DuplicateResolverDialog drd = new DuplicateResolverDialog
1976                         (ths, existingEntry, entry, DuplicateResolverDialog.IMPORT_CHECK);
1977                     drd.setVisible(true);
1978                     int res = drd.getSelected();
1979                     if (res == DuplicateResolverDialog.KEEP_LOWER)   {
1980                         dupli = true;
1981                     }
1982                     else if (res == DuplicateResolverDialog.KEEP_UPPER) {
1983                         database.removeEntry(existingEntry.getId());
1984                         ce.addEdit(new UndoableRemoveEntry
1985                                    (database, existingEntry, basePanel));
1986                     } else if (res == DuplicateResolverDialog.BREAK) {
1987                         break mainLoop;
1988                     }
1989                     break loop;
1990                 }
1991             }
1992         }
1993
1994         if (!dupli) {
1995             try {
1996                 entry.setId(Util.createNeutralId());
1997                 database.insertEntry(entry);
1998                 ce.addEdit(new UndoableInsertEntry
1999                            (database, entry, basePanel));
2000                 addedEntries++;
2001             }
2002             catch (KeyCollisionException ex) {
2003                 //ignore
2004                 System.err.println("KeyCollisionException [ addBibEntries(...) ]");
2005             }
2006         }
2007       }
2008         if (addedEntries > 0) {
2009             ce.end();
2010             basePanel.undoManager.addEdit(ce);
2011             basePanel.markBaseChanged();
2012             if (filename != null)
2013                 output(Globals.lang("Imported database") + " '" + filename + "' " +
2014                      Globals.lang("with") + " " +
2015                      (database.getEntryCount() - oldCount) + " " +
2016                      Globals.lang("entries into new database") + ".");
2017         }
2018
2019     }
2020
2021     return addedEntries;
2022   }
2023
2024   private void setUpImportMenu(JMenu importMenu, boolean intoNew_) {
2025       final boolean intoNew = intoNew_;
2026       importMenu.removeAll();
2027
2028       // Add a menu item for autodetecting import format:
2029       importMenu.add(new ImportMenuItem(ths, intoNew));
2030
2031       // Add custom importers
2032       importMenu.addSeparator();
2033
2034       SortedSet customImporters = Globals.importFormatReader.getCustomImportFormats();
2035       JMenu submenu = new JMenu(Globals.lang("Custom importers"));
2036       submenu.setMnemonic(KeyEvent.VK_S);
2037       /*if (customImporters.size() == 0) {
2038         submenu.setEnabled(false);
2039         submenu.setToolTipText(Globals.lang("No custom imports registered yet."));
2040       } else {*/
2041         // Put in all formatters registered in ImportFormatReader:
2042         for (Iterator i=customImporters.iterator(); i.hasNext();) {
2043             ImportFormat imFo = (ImportFormat)i.next();
2044             submenu.add(new ImportMenuItem(ths, intoNew, imFo));
2045         }
2046       //}
2047       if (customImporters.size() > 0)
2048           submenu.addSeparator();
2049       submenu.add(customImpAction);
2050
2051       importMenu.add(submenu);
2052       importMenu.addSeparator();
2053
2054       // Put in all formatters registered in ImportFormatReader:
2055       for (Iterator i=Globals.importFormatReader.getBuiltInInputFormats().iterator(); i.hasNext();) {
2056           ImportFormat imFo = (ImportFormat)i.next();
2057           importMenu.add(new ImportMenuItem(ths, intoNew, imFo));
2058       }
2059   }
2060
2061
2062     public FileHistory getFileHistory() {
2063         return fileHistory;
2064     }
2065
2066
2067     /**
2068      * Set the preview active state for all BasePanel instances.
2069      * @param enabled
2070      */
2071     public void setPreviewActive(boolean enabled) {
2072         for (int i=0; i<tabbedPane.getTabCount(); i++) {
2073             baseAt(i).setPreviewActive(enabled);
2074         }
2075     }
2076
2077
2078    public void removeCachedEntryEditors() {
2079        for (int j=0; j<tabbedPane.getTabCount(); j++) {
2080             BasePanel bp = (BasePanel)tabbedPane.getComponentAt(j);
2081             bp.entryEditors.clear();
2082        }
2083    }
2084
2085     /**
2086      * This method shows a wait cursor and blocks all input to the JFrame's contents.
2087      */
2088     public void block() {
2089         getGlassPane().setVisible(true);
2090         //getGlassPane().setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2091     }
2092
2093     /**
2094      * This method reverts the cursor to normal, and stops blocking input to the JFrame's contents.
2095      * There are no adverse effects of calling this method redundantly.
2096      */
2097     public void unblock() {
2098         getGlassPane().setVisible(false);
2099         //  getGlassPane().setCursor(Cursor.WAIT_CURSOR);
2100     }
2101
2102
2103     /** Set the visibility of the progress bar in the right end of the
2104       * status line at the bottom of the frame.
2105       *
2106       * If not called on the event dispatch thread, this method uses
2107       * SwingUtilities.invokeLater() to do the actual operation on the EDT.
2108       */
2109     public void setProgressBarVisible(final boolean visible) {
2110     if (SwingUtilities.isEventDispatchThread())
2111         progressBar.setVisible(visible);
2112     else SwingUtilities.invokeLater(new Runnable() {
2113         public void run() {
2114             progressBar.setVisible(visible);
2115         }
2116         });
2117     }
2118
2119
2120     /**
2121      * Sets the current value of the progress bar.
2122       *
2123       * If not called on the event dispatch thread, this method uses
2124       * SwingUtilities.invokeLater() to do the actual operation on the EDT.
2125      */
2126     public void setProgressBarValue(final int value) {
2127     if (SwingUtilities.isEventDispatchThread())
2128         progressBar.setValue(value);
2129     else SwingUtilities.invokeLater(new Runnable() {
2130         public void run() {
2131             progressBar.setValue(value);
2132         }
2133         });
2134
2135     }
2136
2137     /**
2138      * Sets the maximum value of the progress bar. Always call this method
2139      * before using the progress bar, to set a maximum value appropriate to
2140      * the task at hand.
2141       *
2142       * If not called on the event dispatch thread, this method uses
2143       * SwingUtilities.invokeLater() to do the actual operation on the EDT.
2144      */
2145     public void setProgressBarMaximum(final int value) {
2146     if (SwingUtilities.isEventDispatchThread())
2147         progressBar.setMaximum(value);
2148     else SwingUtilities.invokeLater(new Runnable() {
2149         public void run() {
2150             progressBar.setMaximum(value);
2151         }
2152         });
2153
2154
2155     }
2156
2157 class SaveSessionAction
2158       extends MnemonicAwareAction {
2159     public SaveSessionAction() {
2160       super(GUIGlobals.getImage("save"));
2161       putValue(NAME, "Save session");
2162       putValue(ACCELERATOR_KEY, prefs.getKey("Save session"));
2163     }
2164
2165     public void actionPerformed(ActionEvent e) {
2166       // Here we store the names of allcurrent filea. If
2167       // there is no current file, we remove any
2168       // previously stored file name.
2169       Vector filenames = new Vector();
2170       if (tabbedPane.getTabCount() > 0) {
2171         for (int i = 0; i < tabbedPane.getTabCount(); i++) {
2172           if (tabbedPane.getTitleAt(i).equals(GUIGlobals.untitledTitle)) {
2173             tabbedPane.setSelectedIndex(i);
2174             int answer = JOptionPane.showConfirmDialog
2175                 (ths, Globals.lang
2176                  ("This untitled database must be saved first to be "
2177                   + "included in the saved session. Save now?"),
2178                  Globals.lang("Save database"),
2179                  JOptionPane.YES_NO_OPTION);
2180             if (answer == JOptionPane.YES_OPTION) {
2181               // The user wants to save.
2182               try {
2183                 basePanel().runCommand("save");
2184               }
2185               catch (Throwable ex) {}
2186             }
2187           }
2188           if (baseAt(i).getFile() != null) {
2189             filenames.add(baseAt(i).getFile().getPath());
2190           }
2191         }
2192       }
2193
2194       if (filenames.size() == 0) {
2195         output(Globals.lang("Not saved (empty session)") + ".");
2196         return;
2197       }
2198       else {
2199         String[] names = new String[filenames.size()];
2200         for (int i = 0; i < filenames.size(); i++) {
2201           names[i] = (String) filenames.elementAt(i);
2202         }
2203         prefs.putStringArray("savedSession", names);
2204         output(Globals.lang("Saved session") + ".");
2205       }
2206
2207     }
2208   }
2209
2210   class LoadSessionAction
2211       extends MnemonicAwareAction {
2212       boolean running = false;
2213     public LoadSessionAction() {
2214       super(GUIGlobals.getImage("loadSession"));
2215       putValue(NAME, "Load session");
2216       putValue(ACCELERATOR_KEY, prefs.getKey("Load session"));
2217     }
2218
2219     public void actionPerformed(ActionEvent e) {
2220       if (prefs.get("savedSession") == null) {
2221         output(Globals.lang("No saved session found."));
2222         return;
2223       }
2224       if (running)
2225           return;
2226       else running = true;
2227       output(Globals.lang("Loading session..."));
2228       (new Thread() {
2229         public void run() {
2230           HashSet currentFiles = new HashSet();
2231           if (tabbedPane.getTabCount() > 0) {
2232             for (int i = 0; i < tabbedPane.getTabCount(); i++) {
2233                 if (baseAt(i).getFile() != null)
2234                     currentFiles.add(baseAt(i).getFile().getPath());
2235             }
2236           }
2237           int i0 = tabbedPane.getTabCount();
2238           String[] names = prefs.getStringArray("savedSession");
2239           for (int i = 0; i < names.length; i++) {
2240             if (!currentFiles.contains(names[i])) {
2241               File file = new File(names[i]);
2242               if (file.exists()) {
2243                 //Util.pr("Opening last edited file:"
2244                 //+fileToOpen.getName());
2245                 open.openIt(file, i == 0);
2246               }
2247             }
2248           }
2249           output(Globals.lang("Files opened") + ": " +
2250                  (tabbedPane.getTabCount() - i0));
2251           running = false;
2252         }
2253       }).start();
2254
2255     }
2256   }
2257
2258   class ChangeTabAction
2259       extends MnemonicAwareAction {
2260     private boolean next;
2261     public ChangeTabAction(boolean next) {
2262       putValue(NAME, next ? "Next tab" : "Previous tab");
2263       this.next = next;
2264       //Util.pr(""+prefs.getKey("Next tab"));
2265       putValue(ACCELERATOR_KEY,
2266                (next ? prefs.getKey("Next tab") : prefs.getKey("Previous tab")));
2267     }
2268
2269     public void actionPerformed(ActionEvent e) {
2270       int i = tabbedPane.getSelectedIndex();
2271       int newI = (next ? i + 1 : i - 1);
2272       if (newI < 0) {
2273         newI = tabbedPane.getTabCount() - 1;
2274       }
2275       if (newI == tabbedPane.getTabCount()) {
2276         newI = 0;
2277       }
2278       tabbedPane.setSelectedIndex(newI);
2279     }
2280   }
2281
2282   /**
2283    * Class for handling general actions; cut, copy and paste. The focused component is
2284    * kept track of by Globals.focusListener, and we call the action stored under the
2285    * relevant name in its action map.
2286    */
2287   class EditAction
2288       extends MnemonicAwareAction {
2289     private String command;
2290     public EditAction(String command, URL icon) {
2291       super(new ImageIcon(icon));
2292       this.command = command;
2293       String nName = Util.nCase(command);
2294       putValue(NAME, nName);
2295       putValue(ACCELERATOR_KEY, prefs.getKey(nName));
2296       putValue(SHORT_DESCRIPTION, Globals.lang(nName));
2297       //putValue(ACCELERATOR_KEY,
2298       //         (next?prefs.getKey("Next tab"):prefs.getKey("Previous tab")));
2299     }
2300
2301     public void actionPerformed(ActionEvent e) {
2302
2303       //Util.pr(Globals.focusListener.getFocused().toString());
2304       JComponent source = Globals.focusListener.getFocused();
2305       try {
2306         source.getActionMap().get(command).actionPerformed
2307             (new ActionEvent(source, 0, command));
2308       } catch (NullPointerException ex) {
2309         // No component is focused, so we do nothing.
2310       }
2311     }
2312   }
2313
2314   class CustomizeExportsAction extends MnemonicAwareAction {
2315     public CustomizeExportsAction() {
2316       putValue(NAME, "Manage custom exports");
2317     }
2318
2319     public void actionPerformed(ActionEvent e) {
2320       ExportCustomizationDialog ecd = new ExportCustomizationDialog(ths);
2321       ecd.setVisible(true);
2322     }
2323   }
2324
2325   class CustomizeImportsAction extends MnemonicAwareAction {
2326     public CustomizeImportsAction() {
2327       putValue(NAME, "Manage custom imports");
2328     }
2329
2330     public void actionPerformed(ActionEvent e) {
2331       ImportCustomizationDialog ecd = new ImportCustomizationDialog(ths);
2332       ecd.setVisible(true);
2333     }
2334   }
2335
2336  
2337     class CustomizeEntryTypeAction extends MnemonicAwareAction {
2338         public CustomizeEntryTypeAction() {
2339             putValue(NAME, "Customize entry types");
2340         }
2341         public void actionPerformed(ActionEvent e) {
2342             JDialog dl = new EntryCustomizationDialog2(ths);
2343             Util.placeDialog(dl, ths);
2344             dl.setVisible(true);
2345         }
2346     }
2347
2348     class GenFieldsCustomizationAction extends MnemonicAwareAction {
2349         public GenFieldsCustomizationAction() {
2350             putValue(NAME, "Set up general fields");
2351         }
2352         public void actionPerformed(ActionEvent e) {
2353             GenFieldsCustomizer gf = new GenFieldsCustomizer(ths);
2354             Util.placeDialog(gf, ths);
2355             gf.setVisible(true);
2356
2357         }
2358     }
2359
2360     class DatabasePropertiesAction extends MnemonicAwareAction {
2361         DatabasePropertiesDialog propertiesDialog = null;
2362         public DatabasePropertiesAction() {
2363             putValue(NAME, "Database properties");
2364         }
2365
2366         public void actionPerformed(ActionEvent e) {
2367             if (propertiesDialog == null)
2368                 propertiesDialog = new DatabasePropertiesDialog(JabRefFrame.this);
2369             propertiesDialog.setPanel(basePanel());
2370             Util.placeDialog(propertiesDialog, JabRefFrame.this);
2371             propertiesDialog.setVisible(true);
2372         }
2373     }
2374
2375     /*private class ForegroundLabel extends JLabel {
2376          public ForegroundLabel(String s) {
2377              super(s);
2378              setFont(new Font("plain", Font.BOLD, 70));
2379              setHorizontalAlignment(JLabel.CENTER);
2380          }
2381
2382         public void paint(Graphics g) {
2383             Graphics2D g2 = (Graphics2D)g;
2384             g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
2385             super.paint(g2);    //To change body of overridden methods use File | Settings | File Templates.
2386         }
2387     }       */
2388
2389   private class MyGlassPane extends JPanel {
2390     //ForegroundLabel infoLabel = new ForegroundLabel("Showing search");
2391     public MyGlassPane() {
2392       addKeyListener(new KeyAdapter() { });
2393       addMouseListener(new MouseAdapter() { });
2394       /*  infoLabel.setForeground(new Color(255, 100, 100, 124));
2395
2396         setLayout(new BorderLayout());
2397         add(infoLabel, BorderLayout.CENTER);*/
2398       super.setCursor(
2399         Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
2400     }
2401       // Override isOpaque() to prevent the glasspane from hiding the window contents:
2402       public boolean isOpaque() { return false; }
2403   }
2404 }