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