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