2 Copyright (C) 2003 Morten O. Alver and Nizar N. Batada
4 All programs in this directory and
5 subdirectories are published under the GNU General Public License as
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.
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.
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
23 Further information about the GNU GPL is available at:
24 http://www.gnu.org/copyleft/gpl.ja.html
29 package net.sf.jabref;
32 import java.awt.datatransfer.*;
33 import java.awt.event.*;
36 import java.util.List;
38 import javax.swing.tree.TreePath;
39 import javax.swing.undo.*;
40 import net.sf.jabref.collab.*;
41 import net.sf.jabref.export.*;
42 import net.sf.jabref.external.PushToLyx;
43 import net.sf.jabref.external.AutoSetExternalFileForEntries;
44 import net.sf.jabref.external.PushToEmacs;
45 import net.sf.jabref.groups.*;
46 import net.sf.jabref.imports.*;
47 import net.sf.jabref.labelPattern.LabelPatternUtil;
48 import net.sf.jabref.undo.*;
49 import net.sf.jabref.wizard.text.gui.TextInputDialog;
50 import net.sf.jabref.journals.AbbreviateAction;
51 import net.sf.jabref.journals.UnabbreviateAction;
52 import net.sf.jabref.gui.*;
53 import net.sf.jabref.search.NoSearchMatcher;
54 import net.sf.jabref.search.SearchMatcher;
55 import com.jgoodies.uif_lite.component.UIFSplitPane;
56 import ca.odell.glazedlists.FilterList;
57 import ca.odell.glazedlists.matchers.Matcher;
58 import ca.odell.glazedlists.event.ListEventListener;
59 import ca.odell.glazedlists.event.ListEvent;
61 public class BasePanel extends JPanel implements ClipboardOwner, FileUpdateListener {
63 public final static int SHOWING_NOTHING=0, SHOWING_PREVIEW=1, SHOWING_EDITOR=2, WILL_SHOW_EDITOR=3;
65 private EntryEditor currentEditor = null;
66 private PreviewPanel currentPreview = null;
70 private MainTableSelectionListener selectionListener = null;
71 private ListEventListener groupsHighlightListener;
72 UIFSplitPane contentPane = new UIFSplitPane();
75 //BibtexEntry testE = new BibtexEntry("tt");
76 //boolean previewActive = true;
79 BibtexDatabase database;
80 // The database shown in this panel.
82 fileToOpen = null; // The filename of the database.
83 String fileMonitorHandle = null;
84 boolean saving = false, updatedExternally = false;
85 private String encoding;
87 GridBagLayout gbl = new GridBagLayout();
88 GridBagConstraints con = new GridBagConstraints();
90 //Hashtable autoCompleters = new Hashtable();
91 // Hashtable that holds as keys the names of the fields where
92 // autocomplete is active, and references to the autocompleter objects.
95 public CountingUndoManager undoManager = new CountingUndoManager(this);
96 UndoAction undoAction = new UndoAction();
97 RedoAction redoAction = new RedoAction();
99 //ExampleFileFilter fileFilter;
100 // File filter for .bib files.
102 boolean baseChanged = false, nonUndoableChange = false;
103 // Used to track whether the base has changed since last save.
105 //EntryTableModel tableModel = null;
106 //public EntryTable entryTable = null;
107 public MainTable mainTable = null;
108 public FilterList searchFilterList = null, groupFilterList = null;
110 public RightClickMenu rcm;
112 BibtexEntry showing = null;
113 // To indicate which entry is currently shown.
114 public HashMap entryEditors = new HashMap();
115 // To contain instantiated entry editors. This is to save time
116 // in switching between entries.
118 //HashMap entryTypeForms = new HashMap();
119 // Hashmap to keep track of which entries currently have open
120 // EntryTypeForm dialogs.
122 PreambleEditor preambleEditor = null;
123 // Keeps track of the preamble dialog if it is open.
125 StringDialog stringDialog = null;
126 // Keeps track of the string dialog if it is open.
129 * The group selector component for this database. Instantiated by the
130 * SidePaneManager if necessary, or from this class if merging groups from a
131 * different database.
133 //GroupSelector groupSelector;
135 public boolean sortingBySearchResults = false,
136 coloringBySearchResults = false,
137 hidingNonHits = false,
138 sortingByGroup = false,
139 sortingByCiteSeerResults = false,
140 coloringByGroup = false;
141 //previewEnabled = Globals.prefs.getBoolean("previewEnabled");
142 int lastSearchHits = -1; // The number of hits in the latest search.
143 // Potential use in hiding non-hits completely.
145 // MetaData parses, keeps and writes meta data.
147 HashMap fieldExtras = new HashMap();
148 //## keep track of all keys for duplicate key warning and unique key generation
149 //private HashMap allKeys = new HashMap(); // use a map instead of a set since i need to know how many of each key is inthere
151 private boolean suppressOutput = false;
153 private HashMap actions = new HashMap();
154 private SidePaneManager sidePaneManager;
157 * Create a new BasePanel with an empty database.
158 * @param frame The application window.
160 public BasePanel(JabRefFrame frame) {
161 this.sidePaneManager = Globals.sidePaneManager;
162 database = new BibtexDatabase();
163 metaData = new MetaData();
167 encoding = Globals.prefs.get("defaultEncoding");
168 //System.out.println("Default: "+encoding);
171 public BasePanel(JabRefFrame frame, BibtexDatabase db, File file,
172 HashMap meta, String encoding) {
174 this.encoding = encoding;
175 // System.out.println(encoding);
176 //super(JSplitPane.HORIZONTAL_SPLIT, true);
177 this.sidePaneManager = Globals.sidePaneManager;
183 metaData = new MetaData();
186 /*if (Globals.prefs.getBoolean("autoComplete")) {
187 db.setCompleters(autoCompleters);
192 // Register so we get notifications about outside changes to the file.
195 fileMonitorHandle = Globals.fileUpdateMonitor.addUpdateListener(this,
197 } catch (IOException ex) {
201 public int getMode() {
205 public BibtexDatabase database() { return database; }
206 public MetaData metaData() { return metaData; }
207 public File file() { return file; }
208 public JabRefFrame frame() { return frame; }
209 public JabRefPreferences prefs() { return Globals.prefs; }
211 public String getEncoding() { return encoding; }
212 public void setEncoding(String encoding) {
213 this.encoding = encoding;
216 public void output(String s) {
217 //Util.pr("\""+s+"\""+(SwingUtilities.isEventDispatchThread()));
222 private void setupActions() {
224 actions.put("undo", undoAction);
225 actions.put("redo", redoAction);
227 // The action for opening an entry editor.
228 actions.put("edit", new BaseAction() {
229 public void action() {
230 selectionListener.editSignalled();
233 if (isShowingEditor()) {
234 new FocusRequester(splitPane.getBottomComponent());
240 //public void run() {
242 // We demand that one and only one row is selected.
243 if (entryTable.getSelectedRowCount() == 1) {
244 clickedOn = entryTable.getSelectedRow();
246 if (clickedOn >= 0) {
247 String id = tableModel.getIdForRow(clickedOn);
248 BibtexEntry be = database.getEntryById(id);
251 if (splitPane.getBottomComponent() != null) {
252 new FocusRequester(splitPane.getBottomComponent());
261 // The action for saving a database.
262 actions.put("save", new AbstractWorker() {
263 private boolean success = false;
264 public void init() throws Throwable {
267 runCommand("saveAs");
270 if (updatedExternally || Globals.fileUpdateMonitor.hasBeenModified(fileMonitorHandle)) {
271 String[] opts = new String[]{Globals.lang("Review changes"), Globals.lang("Save"),
272 Globals.lang("Cancel")};
273 int answer = JOptionPane.showOptionDialog(frame, Globals.lang("File has been updated externally. "
274 + "What do you want to do?"), Globals.lang("File updated externally"),
275 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE,
276 null, opts, opts[0]);
277 /* int choice = JOptionPane.showConfirmDialog(frame, Globals.lang("File has been updated externally. "
278 +"Are you sure you want to save?"), Globals.lang("File updated externally"),
279 JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);*/
281 if (answer == JOptionPane.CANCEL_OPTION)
283 else if (answer == JOptionPane.YES_OPTION) {
284 ChangeScanner scanner = new ChangeScanner(frame, BasePanel.this); //, panel.database(), panel.metaData());
286 scanner.changeScan(file());
287 setUpdatedExternally(false);
288 SwingUtilities.invokeLater(new Runnable() {
290 sidePaneManager.hideAway("fileUpdate");
294 //} catch (IOException ex) {
295 // ex.printStackTrace();
302 frame.output(Globals.lang("Saving database") + "...");
307 public void update() {
309 frame.setTabTitle(BasePanel.this, file.getName());
310 frame.output(Globals.lang("Saved database")+" '"
311 +file.getPath()+"'.");
313 frame.output(Globals.lang("Save failed"));
319 success = saveDatabase(file, false, encoding);
321 //Util.pr("Testing resolve string... BasePanel line 237");
322 //Util.pr("Resolve aq: "+database.resolveString("aq"));
323 //Util.pr("Resolve text: "+database.resolveForStrings("A text which refers to the string #aq# and #billball#, hurra."));
326 Globals.fileUpdateMonitor.updateTimeStamp(fileMonitorHandle);
327 } catch (IllegalArgumentException ex) {
328 // This means the file has not yet been registered, which is the case
329 // when doing a "Save as". Maybe we should change the monitor so no
330 // exception is cast.
334 undoManager.markUnchanged();
335 // (Only) after a successful save the following
336 // statement marks that the base is unchanged
338 nonUndoableChange = false;
340 updatedExternally = false;
342 } catch (SaveException ex2) {
343 ex2.printStackTrace();
348 actions.put("saveAs", new BaseAction () {
349 public void action() throws Throwable {
351 String chosenFile = Globals.getNewFile(frame, Globals.prefs, new File(Globals.prefs.get("workingDirectory")), ".bib",
352 JFileChooser.SAVE_DIALOG, false);
354 if (chosenFile != null) {
355 file = new File(chosenFile);
356 if (!file.exists() ||
357 (JOptionPane.showConfirmDialog
358 (frame, "'"+file.getName()+"' "+Globals.lang("exists. Overwrite file?"),
359 Globals.lang("Save database"), JOptionPane.OK_CANCEL_OPTION)
360 == JOptionPane.OK_OPTION)) {
364 // Register so we get notifications about outside changes to the file.
366 fileMonitorHandle = Globals.fileUpdateMonitor.addUpdateListener(BasePanel.this,file);
367 } catch (IOException ex) {
368 ex.printStackTrace();
371 Globals.prefs.put("workingDirectory", file.getParent());
372 frame.getFileHistory().newFile(file.getPath());
380 actions.put("saveSelectedAs", new BaseAction () {
381 public void action() throws Throwable {
383 String chosenFile = Globals.getNewFile(frame, Globals.prefs, new File(Globals.prefs.get("workingDirectory")), ".bib",
384 JFileChooser.SAVE_DIALOG, false);
385 if (chosenFile != null) {
386 File expFile = new File(chosenFile);
387 if (!expFile.exists() ||
388 (JOptionPane.showConfirmDialog
389 (frame, "'"+expFile.getName()+"' "+
390 Globals.lang("exists. Overwrite file?"),
391 Globals.lang("Save database"), JOptionPane.OK_CANCEL_OPTION)
392 == JOptionPane.OK_OPTION)) {
393 saveDatabase(expFile, true, Globals.prefs.get("defaultEncoding"));
394 //runCommand("save");
395 frame.getFileHistory().newFile(expFile.getPath());
396 frame.output(Globals.lang("Saved selected to")+" '"
397 +expFile.getPath()+"'.");
403 // The action for copying selected entries.
404 actions.put("copy", new BaseAction() {
405 public void action() {
406 BibtexEntry[] bes = mainTable.getSelectedEntries();
408 if ((bes != null) && (bes.length > 0)) {
409 TransferableBibtexEntry trbe
410 = new TransferableBibtexEntry(bes);
411 // ! look at ClipBoardManager
412 Toolkit.getDefaultToolkit().getSystemClipboard()
413 .setContents(trbe, BasePanel.this);
414 output(Globals.lang("Copied")+" "+(bes.length>1 ? bes.length+" "
415 +Globals.lang("entries")
416 : "1 "+Globals.lang("entry")+"."));
418 // The user maybe selected a single cell.
419 int[] rows = mainTable.getSelectedRows(),
420 cols = mainTable.getSelectedColumns();
421 if ((cols.length == 1) && (rows.length == 1)) {
422 // Copy single value.
423 Object o = mainTable.getValueAt(rows[0], cols[0]);
425 StringSelection ss = new StringSelection(o.toString());
426 Toolkit.getDefaultToolkit().getSystemClipboard()
427 .setContents(ss, BasePanel.this);
429 output(Globals.lang("Copied cell contents")+".");
436 actions.put("cut", new BaseAction() {
437 public void action() throws Throwable {
439 BibtexEntry[] bes = mainTable.getSelectedEntries();
440 //int row0 = mainTable.getSelectedRow();
441 if ((bes != null) && (bes.length > 0)) {
442 // Create a CompoundEdit to make the action undoable.
443 NamedCompound ce = new NamedCompound
444 (Globals.lang(bes.length > 1 ? "cut entries" : "cut entry"));
445 // Loop through the array of entries, and delete them.
446 for (int i=0; i<bes.length; i++) {
447 database.removeEntry(bes[i].getId());
448 ensureNotShowing(bes[i]);
449 ce.addEdit(new UndoableRemoveEntry
450 (database, bes[i], BasePanel.this));
452 //entryTable.clearSelection();
453 frame.output(Globals.lang("Cut_pr")+" "+
454 (bes.length>1 ? bes.length
455 +" "+ Globals.lang("entries")
456 : Globals.lang("entry"))+".");
458 undoManager.addEdit(ce);
461 // Reselect the entry in the first prev. selected position:
462 /*if (row0 >= entryTable.getRowCount())
463 row0 = entryTable.getRowCount()-1;
465 entryTable.addRowSelectionInterval(row0, row0);*/
470 actions.put("delete", new BaseAction() {
471 public void action() {
472 boolean cancelled = false;
473 BibtexEntry[] bes = mainTable.getSelectedEntries();
474 int row0 = mainTable.getSelectedRow();
475 if ((bes != null) && (bes.length > 0)) {
477 boolean goOn = showDeleteConfirmationDialog(bes.length);
482 // Create a CompoundEdit to make the action undoable.
483 NamedCompound ce = new NamedCompound
484 (Globals.lang(bes.length > 1 ? "delete entries" : "delete entry"));
485 // Loop through the array of entries, and delete them.
486 for (int i = 0; i < bes.length; i++) {
487 database.removeEntry(bes[i].getId());
488 ensureNotShowing(bes[i]);
489 ce.addEdit(new UndoableRemoveEntry(database, bes[i], BasePanel.this));
492 frame.output(Globals.lang("Deleted") + " " +
493 (bes.length > 1 ? bes.length
494 + " " + Globals.lang("entries")
495 : Globals.lang("entry")) + ".");
497 undoManager.addEdit(ce);
498 //entryTable.clearSelection();
502 // Reselect the entry in the first prev. selected position:
503 /*if (row0 >= entryTable.getRowCount())
504 row0 = entryTable.getRowCount()-1;
506 final int toSel = row0;
508 SwingUtilities.invokeLater(new Runnable() {
510 entryTable.addRowSelectionInterval(toSel, toSel);
511 //entryTable.ensureVisible(toSel);
521 // The action for pasting entries or cell contents.
522 // Edited by Seb Wills <saw27@mrao.cam.ac.uk> on 14-Apr-04:
523 // - more robust detection of available content flavors (doesn't only look at first one offered)
524 // - support for parsing string-flavor clipboard contents which are bibtex entries.
525 // This allows you to (a) paste entire bibtex entries from a text editor, web browser, etc
526 // (b) copy and paste entries between multiple instances of JabRef (since
527 // only the text representation seems to get as far as the X clipboard, at least on my system)
528 actions.put("paste", new BaseAction() {
529 public void action() {
530 // Get clipboard contents, and see if TransferableBibtexEntry is among the content flavors offered
531 Transferable content = Toolkit.getDefaultToolkit()
532 .getSystemClipboard().getContents(null);
533 if (content != null) {
534 BibtexEntry[] bes = null;
535 if (content.isDataFlavorSupported(TransferableBibtexEntry.entryFlavor)) {
536 // We have determined that the clipboard data is a set of entries.
538 bes = (BibtexEntry[])(content.getTransferData(TransferableBibtexEntry.entryFlavor));
540 } catch (UnsupportedFlavorException ex) {
541 ex.printStackTrace();
542 } catch (IOException ex) {
543 ex.printStackTrace();
545 } else if (content.isDataFlavorSupported(DataFlavor.stringFlavor)) {
546 // We have determined that no TransferableBibtexEntry is available, but
547 // there is a string, which we will handle according to context:
548 int[] rows = mainTable.getSelectedRows();
549 //cols = entryTable.getSelectedColumns();
550 //Util.pr(rows.length+" x "+cols.length);
551 /*if ((cols != null) && (cols.length == 1) && (cols[0] != 0)
552 && (rows != null) && (rows.length == 1)) {
553 // A single cell is highlighted, so paste the string straight into it without parsing
555 tableModel.setValueAt((String)(content.getTransferData(DataFlavor.stringFlavor)), rows[0], cols[0]);
558 output("Pasted cell contents");
559 } catch (UnsupportedFlavorException ex) {
560 ex.printStackTrace();
561 } catch (IOException ex) {
562 ex.printStackTrace();
563 } catch (IllegalArgumentException ex) {
564 output("Can't paste.");
567 // no single cell is selected, so try parsing the clipboard contents as bibtex entries instead
569 BibtexParser bp = new BibtexParser
570 (new java.io.StringReader( (String) (content.getTransferData(
571 DataFlavor.stringFlavor))));
572 BibtexDatabase db = bp.parse().getDatabase();
573 Util.pr("Parsed " + db.getEntryCount() + " entries from clipboard text");
574 if(db.getEntryCount()>0) {
575 Set keySet = db.getKeySet();
576 if (keySet != null) {
577 // Copy references to the entries into a BibtexEntry array.
578 // Could import directly from db, but going via bes allows re-use
579 // of the same pasting code as used for TransferableBibtexEntries
580 bes = new BibtexEntry[db.getEntryCount()];
581 Iterator it = keySet.iterator();
582 for (int i=0; it.hasNext();i++) {
583 bes[i]=db.getEntryById((String) (it.next()));
587 String cont = (String)(content.getTransferData(DataFlavor.stringFlavor));
588 Util.pr("----------------\n"+cont+"\n---------------------");
589 TextAnalyzer ta = new TextAnalyzer(cont);
590 output(Globals.lang("Unable to parse clipboard text as Bibtex entries."));
592 } catch (UnsupportedFlavorException ex) {
593 ex.printStackTrace();
594 } catch (Throwable ex) {
595 ex.printStackTrace();
600 // finally we paste in the entries (if any), which either came from TransferableBibtexEntries
601 // or were parsed from a string
602 if ((bes != null) && (bes.length > 0)) {
603 NamedCompound ce = new NamedCompound
604 (Globals.lang(bes.length > 1 ? "paste entries" : "paste entry"));
605 for (int i=0; i<bes.length; i++) {
607 BibtexEntry be = (BibtexEntry)(bes[i].clone());
608 // We have to clone the
609 // entries, since the pasted
610 // entries must exist
611 // independently of the copied
613 be.setId(Util.createNeutralId());
614 database.insertEntry(be);
615 ce.addEdit(new UndoableInsertEntry
616 (database, be, BasePanel.this));
617 } catch (KeyCollisionException ex) {
618 Util.pr("KeyCollisionException... this shouldn't happen.");
622 undoManager.addEdit(ce);
623 //entryTable.clearSelection();
624 //entryTable.revalidate();
625 output(Globals.lang("Pasted")+" "+
626 (bes.length>1 ? bes.length+" "+
627 Globals.lang("entries") : "1 "+Globals.lang("entry"))
637 actions.put("selectAll", new BaseAction() {
638 public void action() {
639 mainTable.selectAll();
643 // The action for opening the preamble editor
644 actions.put("editPreamble", new BaseAction() {
645 public void action() {
646 if (preambleEditor == null) {
647 PreambleEditor form = new PreambleEditor
648 (frame, BasePanel.this, database, Globals.prefs);
649 Util.placeDialog(form, frame);
650 form.setVisible(true);
651 preambleEditor = form;
653 preambleEditor.setVisible(true);
659 // The action for opening the string editor
660 actions.put("editStrings", new BaseAction() {
661 public void action() {
662 if (stringDialog == null) {
663 StringDialog form = new StringDialog
664 (frame, BasePanel.this, database, Globals.prefs);
665 Util.placeDialog(form, frame);
666 form.setVisible(true);
669 stringDialog.setVisible(true);
675 // The action for toggling the groups interface
676 actions.put("toggleGroups", new BaseAction() {
677 public void action() {
678 sidePaneManager.togglePanel("groups");
679 frame.groupToggle.setSelected(sidePaneManager.isPanelVisible("groups"));
683 // The action for pushing citations to an open Lyx/Kile instance:
684 actions.put("pushToLyX", new PushToLyx(BasePanel.this));
686 actions.put("pushToWinEdt",new BaseAction(){
687 public void action(){
688 final List entries = mainTable.getSelected();
689 final int numSelected = entries.size();
690 // Globals.logger("Pushing " +numSelected+(numSelected>1? " entries" : "entry") + " to WinEdt");
692 if( numSelected > 0){
693 Thread pushThread = new Thread() {
695 String winEdt = Globals.prefs.get("winEdtPath");
696 //winEdt = "osascript";
698 StringBuffer toSend = new StringBuffer("\"[InsText('\\")
699 .append(Globals.prefs.get("citeCommand")).append("{");
700 String citeKey = "";//, message = "";
701 boolean first = true;
702 for (Iterator i=entries.iterator(); i.hasNext();) {
703 BibtexEntry bes = (BibtexEntry)i.next();
704 citeKey = (String) bes.getField(GUIGlobals.KEY_FIELD);
705 // if the key is empty we give a warning and ignore this entry
706 if (citeKey == null || citeKey.equals(""))
709 toSend.append(citeKey);
713 toSend.append(",").append(citeKey);
716 //message += (1 + rows[i]);
720 output(Globals.lang("Please define BibTeX key first"));
722 toSend.append("}');]\"");
723 //System.out.println(toSend.toString());
724 Runtime.getRuntime().exec(winEdt + " " + toSend.toString());
725 Globals.lang("Pushed citations to WinEdt");
727 Globals.lang("Pushed the citations for the following rows to")+"WinEdt: " +
732 catch (IOException excep) {
733 output(Globals.lang("Error")+": "+Globals.lang("Could not call executable")+" '"
735 excep.printStackTrace();
745 actions.put("pushToEmacs", new PushToEmacs(BasePanel.this));
747 // The action for auto-generating keys.
748 actions.put("makeKey", new AbstractWorker() {
752 boolean cancelled = false;
754 // Run first, in EDT:
757 entries = new ArrayList(Arrays.asList(getSelectedEntries()));
758 //rows = entryTable.getSelectedRows() ;
759 numSelected = entries.size();
761 if (entries.size() == 0) { // None selected. Inform the user to select entries first.
762 JOptionPane.showMessageDialog(frame, Globals.lang("First select the entries you want keys to be generated for."),
763 Globals.lang("Autogenerate BibTeX key"), JOptionPane.INFORMATION_MESSAGE);
767 output(Globals.lang("Generating BibTeX key for")+" "+
768 numSelected+" "+(numSelected>1 ? Globals.lang("entries")
769 : Globals.lang("entry"))+"...");
772 // Run second, on a different thread:
774 BibtexEntry bes = null ;
775 NamedCompound ce = new NamedCompound(Globals.lang("autogenerate keys"));
778 boolean hasShownWarning = false;
779 // First check if any entries have keys set already. If so, possibly remove
780 // them from consideration, or warn about overwriting keys.
781 loop: for (Iterator i=entries.iterator(); i.hasNext();) {
782 bes = (BibtexEntry)i.next();
783 if (bes.getField(GUIGlobals.KEY_FIELD) != null) {
784 if (Globals.prefs.getBoolean("avoidOverwritingKey"))
785 // Rmove the entry, because its key is already set:
787 else if (Globals.prefs.getBoolean("warnBeforeOverwritingKey")) {
788 // Ask if the user wants to cancel the operation:
789 CheckBoxMessage cbm = new CheckBoxMessage(Globals.lang("One or more keys will be overwritten. Continue?"),
790 Globals.lang("Disable this confirmation dialog"), false);
791 int answer = JOptionPane.showConfirmDialog(frame, cbm, Globals.lang("Overwrite keys"),
792 JOptionPane.YES_NO_OPTION);
793 if (cbm.isSelected())
794 Globals.prefs.putBoolean("warnBeforeOverwritingKey", false);
795 if (answer == JOptionPane.NO_OPTION) {
796 // Ok, break off the operation.
800 // No need to check more entries, because the user has already confirmed
801 // that it's ok to overwrite keys:
807 HashMap oldvals = new HashMap();
808 // Iterate again, removing already set keys. This is skipped if overwriting
809 // is disabled, since all entries with keys set will have been removed.
810 if (!Globals.prefs.getBoolean("avoidOverwritingKey")) for (Iterator i=entries.iterator(); i.hasNext();) {
811 bes = (BibtexEntry)i.next();
812 // Store the old value:
813 oldvals.put(bes, bes.getField(GUIGlobals.KEY_FIELD));
814 database.setCiteKeyForEntry(bes.getId(), null);
817 // Finally, set the new keys:
818 for (Iterator i=entries.iterator(); i.hasNext();) {
819 bes = (BibtexEntry)i.next();
820 bes = LabelPatternUtil.makeLabel(Globals.prefs.getKeyPattern(), database, bes);
821 ce.addEdit(new UndoableKeyChange
822 (database, bes.getId(), (String)oldvals.get(bes),
823 (String)bes.getField(GUIGlobals.KEY_FIELD)));
826 undoManager.addEdit(ce);
829 // Run third, on EDT:
830 public void update() {
836 numSelected = entries.size();
837 output(Globals.lang("Generated BibTeX key for")+" "+
838 numSelected+" "+(numSelected!=1 ? Globals.lang("entries")
839 : Globals.lang("entry")));
844 actions.put("search", new BaseAction() {
845 public void action() {
846 //sidePaneManager.togglePanel("search");
847 sidePaneManager.ensureVisible("search");
848 //boolean on = sidePaneManager.isPanelVisible("search");
849 frame.searchToggle.setSelected(true);
851 frame.searchManager.startSearch();
855 actions.put("toggleSearch", new BaseAction() {
856 public void action() {
857 //sidePaneManager.togglePanel("search");
858 sidePaneManager.togglePanel("search");
859 boolean on = sidePaneManager.isPanelVisible("search");
860 frame.searchToggle.setSelected(on);
862 frame.searchManager.startSearch();
866 actions.put("incSearch", new BaseAction() {
867 public void action() {
868 sidePaneManager.ensureVisible("search");
869 frame.searchToggle.setSelected(true);
870 frame.searchManager.startIncrementalSearch();
874 // The action for copying the selected entry's key.
875 actions.put("copyKey", new BaseAction() {
876 public void action() {
877 BibtexEntry[] bes = mainTable.getSelectedEntries();
878 if ((bes != null) && (bes.length > 0)) {
880 //String[] keys = new String[bes.length];
881 Vector keys = new Vector();
882 // Collect all non-null keys.
883 for (int i=0; i<bes.length; i++)
884 if (bes[i].getField(Globals.KEY_FIELD) != null)
885 keys.add(bes[i].getField(Globals.KEY_FIELD));
886 if (keys.size() == 0) {
887 output("None of the selected entries have BibTeX keys.");
890 StringBuffer sb = new StringBuffer((String)keys.elementAt(0));
891 for (int i=1; i<keys.size(); i++) {
893 sb.append((String)keys.elementAt(i));
896 StringSelection ss = new StringSelection(sb.toString());
897 Toolkit.getDefaultToolkit().getSystemClipboard()
898 .setContents(ss, BasePanel.this);
900 if (keys.size() == bes.length)
901 // All entries had keys.
902 output(Globals.lang((bes.length > 1) ? "Copied keys"
903 : "Copied key")+".");
905 output(Globals.lang("Warning")+": "+(bes.length-keys.size())
906 +" "+Globals.lang("out of")+" "+bes.length+" "+
907 Globals.lang("entries have undefined BibTeX key")+".");
912 // The action for copying a cite for the selected entry.
913 actions.put("copyCiteKey", new BaseAction() {
914 public void action() {
915 BibtexEntry[] bes = mainTable.getSelectedEntries();
916 if ((bes != null) && (bes.length > 0)) {
918 //String[] keys = new String[bes.length];
919 Vector keys = new Vector();
920 // Collect all non-null keys.
921 for (int i=0; i<bes.length; i++)
922 if (bes[i].getField(Globals.KEY_FIELD) != null)
923 keys.add(bes[i].getField(Globals.KEY_FIELD));
924 if (keys.size() == 0) {
925 output("None of the selected entries have BibTeX keys.");
928 StringBuffer sb = new StringBuffer((String)keys.elementAt(0));
929 for (int i=1; i<keys.size(); i++) {
931 sb.append((String)keys.elementAt(i));
934 StringSelection ss = new StringSelection
935 ("\\cite{"+sb.toString()+"}");
936 Toolkit.getDefaultToolkit().getSystemClipboard()
937 .setContents(ss, BasePanel.this);
939 if (keys.size() == bes.length)
940 // All entries had keys.
941 output(Globals.lang((bes.length > 1) ? "Copied keys"
942 : "Copied key")+".");
944 output(Globals.lang("Warning")+": "+(bes.length-keys.size())
945 +" "+Globals.lang("out of")+" "+bes.length+" "+
946 Globals.lang("entries have undefined BibTeX key")+".");
951 actions.put("mergeDatabase", new BaseAction() {
952 public void action() {
954 final MergeDialog md = new MergeDialog(frame, Globals.lang("Append database"), true);
955 Util.placeDialog(md, BasePanel.this);
958 String chosenFile = Globals.getNewFile(frame, Globals.prefs, new File(Globals.prefs.get("workingDirectory")),
959 null, JFileChooser.OPEN_DIALOG, false);
960 /*JFileChooser chooser = (Globals.prefs.get("workingDirectory") == null) ?
961 new JabRefFileChooser((File)null) :
962 new JabRefFileChooser(new File(Globals.prefs.get("workingDirectory")));
963 chooser.addChoosableFileFilter( new OpenFileFilter() );//nb nov2
964 int returnVal = chooser.showOpenDialog(BasePanel.this);*/
965 if(chosenFile == null)
967 fileToOpen = new File(chosenFile);
969 // Run the actual open in a thread to prevent the program
970 // locking until the file is loaded.
973 openIt(md.importEntries(), md.importStrings(),
974 md.importGroups(), md.importSelectorWords());
977 frame.getFileHistory().newFile(fileToOpen.getPath());
982 void openIt(boolean importEntries, boolean importStrings,
983 boolean importGroups, boolean importSelectorWords) {
984 if ((fileToOpen != null) && (fileToOpen.exists())) {
986 Globals.prefs.put("workingDirectory", fileToOpen.getPath());
987 // Should this be done _after_ we know it was successfully opened?
988 String encoding = Globals.prefs.get("defaultEncoding");
989 ParserResult pr = OpenDatabaseAction.loadDatabase(fileToOpen, encoding);
990 mergeFromBibtex(pr, importEntries, importStrings, importGroups, importSelectorWords);
991 output(Globals.lang("Imported from database")+" '"+fileToOpen.getPath()+"'");
993 } catch (Throwable ex) {
994 ex.printStackTrace();
995 JOptionPane.showMessageDialog
996 (BasePanel.this, ex.getMessage(),
997 "Open database", JOptionPane.ERROR_MESSAGE);
1003 actions.put("openFile", new BaseAction() {
1004 public void action() {
1007 BibtexEntry[] bes = mainTable.getSelectedEntries();
1008 String field = "ps";
1009 if ( (bes != null) && (bes.length == 1)) {
1010 Object link = bes[0].getField("ps");
1011 if (bes[0].getField("pdf") != null) {
1012 link = bes[0].getField("pdf");
1015 String filepath = null;
1017 filepath = link.toString();
1021 // see if we can fall back to a filename based on the bibtex key
1023 Object key = bes[0].getField(Globals.KEY_FIELD);
1025 basefile = key.toString();
1026 final String[] types = new String[] {"pdf", "ps"};
1027 final String sep = System.getProperty("file.separator");
1028 for (int i = 0; i < types.length; i++) {
1029 String dir = Globals.prefs.get(types[i]+"Directory");
1030 if (dir.endsWith(sep)) {
1031 dir = dir.substring(0, dir.length()-sep.length());
1033 String found = Util.findPdf(basefile, types[i], dir, new OpenFileFilter("."+types[i]));
1034 if (found != null) {
1035 filepath = dir+sep+found;
1043 if (filepath != null) {
1044 //output(Globals.lang("Calling external viewer..."));
1046 Util.openExternalViewer(filepath, field, Globals.prefs);
1047 output(Globals.lang("External viewer called") + ".");
1049 catch (IOException ex) {
1050 output(Globals.lang("Error") + ": " + ex.getMessage());
1054 output(Globals.lang(
1055 "No pdf or ps defined, and no file matching Bibtex key found") +
1059 output(Globals.lang("No entries or multiple entries selected."));
1066 actions.put("openUrl", new BaseAction() {
1067 public void action() {
1068 BibtexEntry[] bes = mainTable.getSelectedEntries();
1069 String field = "doi";
1070 if ((bes != null) && (bes.length == 1)) {
1071 Object link = bes[0].getField("doi");
1072 if (bes[0].getField("url") != null) {
1073 link = bes[0].getField("url");
1077 //output(Globals.lang("Calling external viewer..."));
1079 Util.openExternalViewer(link.toString(), field, Globals.prefs);
1080 output(Globals.lang("External viewer called")+".");
1081 } catch (IOException ex) {
1082 output(Globals.lang("Error") + ": " + ex.getMessage());
1086 output(Globals.lang("No url defined")+".");
1088 output(Globals.lang("No entries or multiple entries selected."));
1092 actions.put("replaceAll", new BaseAction() {
1093 public void action() {
1094 ReplaceStringDialog rsd = new ReplaceStringDialog(frame);
1095 rsd.setVisible(true);
1096 if (!rsd.okPressed())
1099 NamedCompound ce = new NamedCompound(Globals.lang("Replace string"));
1100 if (!rsd.selOnly()) {
1101 for (Iterator i=database.getKeySet().iterator();
1103 counter += rsd.replace(database.getEntryById((String)i.next()), ce);
1105 BibtexEntry[] bes = mainTable.getSelectedEntries();
1106 for (int i=0; i<bes.length; i++)
1107 counter += rsd.replace(bes[i], ce);
1110 output(Globals.lang("Replaced")+" "+counter+" "+
1111 Globals.lang(counter==1?"occurence":"occurences")+".");
1114 undoManager.addEdit(ce);
1120 actions.put("dupliCheck", new BaseAction() {
1121 public void action() {
1122 DuplicateSearch ds = new DuplicateSearch(BasePanel.this);
1127 actions.put("strictDupliCheck", new BaseAction() {
1128 public void action() {
1129 StrictDuplicateSearch ds = new StrictDuplicateSearch(BasePanel.this);
1134 actions.put("plainTextImport", new BaseAction() {
1135 public void action()
1137 // get Type of new entry
1138 EntryTypeDialog etd = new EntryTypeDialog(frame);
1139 Util.placeDialog(etd, BasePanel.this);
1140 etd.setVisible(true);
1141 BibtexEntryType tp = etd.getChoice();
1145 String id = Util.createNeutralId();
1146 BibtexEntry bibEntry = new BibtexEntry(id, tp) ;
1147 TextInputDialog tidialog = new TextInputDialog(frame, BasePanel.this,
1150 Util.placeDialog(tidialog, BasePanel.this);
1151 tidialog.setVisible(true);
1153 if (tidialog.okPressed())
1155 Util.setAutomaticFields(Arrays.asList(new BibtexEntry[] {bibEntry}));
1156 insertEntry(bibEntry) ;
1161 // The action starts the "import from plain text" dialog
1162 /*actions.put("importPlainText", new BaseAction() {
1163 public void action()
1165 BibtexEntry bibEntry = null ;
1166 // try to get the first marked entry
1167 BibtexEntry[] bes = entryTable.getSelectedEntries();
1168 if ((bes != null) && (bes.length > 0))
1171 if (bibEntry != null)
1173 // Create an UndoableInsertEntry object.
1174 undoManager.addEdit(new UndoableInsertEntry(database, bibEntry, BasePanel.this));
1176 TextInputDialog tidialog = new TextInputDialog(frame, BasePanel.this,
1179 Util.placeDialog(tidialog, BasePanel.this);
1180 tidialog.setVisible(true);
1182 if (tidialog.okPressed())
1184 output(Globals.lang("changed ")+" '"
1185 +bibEntry.getType().getName().toLowerCase()+"' "
1186 +Globals.lang("entry")+".");
1188 int row = tableModel.getNumberFromName(bibEntry.getId());
1190 entryTable.clearSelection();
1191 entryTable.scrollTo(row);
1192 markBaseChanged(); // The database just changed.
1193 if (Globals.prefs.getBoolean("autoOpenForm"))
1195 showEntry(bibEntry);
1202 actions.put("markEntries", new AbstractWorker() {
1203 private int besLength = -1;
1206 NamedCompound ce = new NamedCompound(Globals.lang("Mark entries"));
1207 BibtexEntry[] bes = mainTable.getSelectedEntries();
1208 besLength = bes.length;
1211 for (int i=0; i<bes.length; i++) {
1212 Util.markEntry(bes[i], ce);
1215 undoManager.addEdit(ce);
1218 public void update() {
1220 output(Globals.lang("Marked selected")+" "+Globals.lang(besLength>0?"entry":"entries"));
1225 actions.put("unmarkEntries", new BaseAction() {
1226 public void action() {
1228 NamedCompound ce = new NamedCompound(Globals.lang("Unmark entries"));
1229 BibtexEntry[] bes = mainTable.getSelectedEntries();
1232 for (int i=0; i<bes.length; i++) {
1233 Util.unmarkEntry(bes[i], database, ce);
1236 undoManager.addEdit(ce);
1238 output(Globals.lang("Unmarked selected")+" "+Globals.lang(bes.length>0?"entry":"entries"));
1239 } catch (Throwable ex) { ex.printStackTrace(); }
1243 actions.put("unmarkAll", new BaseAction() {
1244 public void action() {
1245 NamedCompound ce = new NamedCompound(Globals.lang("Unmark all"));
1246 Set keySet = database.getKeySet();
1247 for (Iterator i = keySet.iterator(); i.hasNext(); ) {
1248 BibtexEntry be = database.getEntryById( (String) i.next());
1249 Util.unmarkEntry(be, database, ce);
1253 undoManager.addEdit(ce);
1258 actions.put("togglePreview", new BaseAction() {
1259 public void action() {
1260 boolean enabled = !Globals.prefs.getBoolean("previewEnabled");
1261 Globals.prefs.putBoolean("previewEnabled", enabled);
1262 frame.setPreviewActive(enabled);
1263 frame.previewToggle.setSelected(enabled);
1267 actions.put("toggleHighlightGroupsMatchingAny", new BaseAction() {
1268 public void action() {
1269 boolean enabled = !Globals.prefs.getBoolean("highlightGroupsMatchingAny");
1270 Globals.prefs.putBoolean("highlightGroupsMatchingAny", enabled);
1271 frame.highlightAny.setSelected(enabled);
1273 frame.highlightAll.setSelected(false);
1274 Globals.prefs.putBoolean("highlightGroupsMatchingAll", false);
1276 // ping the listener so it updates:
1277 groupsHighlightListener.listChanged(null);
1281 actions.put("toggleHighlightGroupsMatchingAll", new BaseAction() {
1282 public void action() {
1283 boolean enabled = !Globals.prefs.getBoolean("highlightGroupsMatchingAll");
1284 Globals.prefs.putBoolean("highlightGroupsMatchingAll", enabled);
1285 frame.highlightAll.setSelected(enabled);
1287 frame.highlightAny.setSelected(false);
1288 Globals.prefs.putBoolean("highlightGroupsMatchingAny", false);
1290 // ping the listener so it updates:
1291 groupsHighlightListener.listChanged(null);
1295 actions.put("switchPreview", new BaseAction() {
1296 public void action() {
1297 selectionListener.switchPreview();
1301 actions.put("manageSelectors", new BaseAction() {
1302 public void action() {
1303 ContentSelectorDialog2 csd = new ContentSelectorDialog2
1304 (frame, frame, BasePanel.this, false, metaData, null);
1305 Util.placeDialog(csd, frame);
1306 csd.setVisible(true);
1311 actions.put("exportToClipboard", new AbstractWorker() {
1312 String message = null;
1314 if (mainTable.getSelected().size() == 0) {
1315 message = Globals.lang("No entries selected")+".";
1316 getCallBack().update();
1320 // Make a list of possible formats:
1321 Map formats = new HashMap();
1322 formats.put("BibTeXML", "bibtexml");
1323 formats.put("DocBook", "docbook");
1324 formats.put("HTML", "html");
1325 formats.put("RTF (Harvard)", "harvard/harvard");
1326 formats.put("Simple HTML", "simplehtml");
1327 for (int i = 0; i < Globals.prefs.customExports.size(); i++) {
1328 Object o = (Globals.prefs.customExports.getElementAt(i))[0];
1331 Object[] array = formats.keySet().toArray();
1333 JList list = new JList(array);
1334 list.setBorder(BorderFactory.createEtchedBorder());
1335 list.setSelectionInterval(0,0);
1336 list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
1338 JOptionPane.showOptionDialog(frame, list, Globals.lang("Select format"),
1339 JOptionPane.YES_NO_OPTION,
1340 JOptionPane.QUESTION_MESSAGE, null,
1341 new String[] {Globals.lang("Ok"), Globals.lang("Cancel")},
1342 Globals.lang("Ok"));
1344 if (answer == JOptionPane.NO_OPTION)
1347 String lfName = (String)(formats.get(list.getSelectedValue()));
1348 final boolean custom = (list.getSelectedIndex() >= Globals.STANDARD_EXPORT_COUNT);
1351 int index = list.getSelectedIndex()-Globals.STANDARD_EXPORT_COUNT;
1352 dir = (String)(Globals.prefs.customExports.getElementAt(index)[1]);
1353 File f = new File(dir);
1354 lfName = f.getName();
1355 lfName = lfName.substring(0, lfName.indexOf("."));
1356 // Remove file name - we want the directory only.
1357 dir = f.getParent()+System.getProperty("file.separator");
1359 final String format = lfName,
1363 BibtexEntry[] bes = mainTable.getSelectedEntries();
1364 StringWriter sw = new StringWriter();
1365 FileActions.exportEntries(database, bes, format, custom, directory, sw);
1366 ClipboardOwner owner = new ClipboardOwner() {
1367 public void lostOwnership(Clipboard clipboard, Transferable content) {}
1369 //StringSelection ss = new StringSelection(sw.toString());
1370 RtfSelection rs = new RtfSelection(sw.toString());
1371 Toolkit.getDefaultToolkit().getSystemClipboard()
1372 .setContents(rs, owner);
1373 message = Globals.lang("Entries exported to clipboard")+": "+bes.length;
1374 } catch (Exception ex) {
1375 ex.printStackTrace();
1379 public void update() {
1387 actions.put("abbreviateIso", new AbbreviateAction(this, true));
1388 actions.put("abbreviateMedline", new AbbreviateAction(this, false));
1389 actions.put("unabbreviate", new UnabbreviateAction(this));
1390 actions.put("autoSetPdf", new AutoSetExternalFileForEntries(this, "pdf"));
1391 actions.put("autoSetPs", new AutoSetExternalFileForEntries(this, "ps"));
1396 * This method is called from JabRefFrame is a database specific
1397 * action is requested by the user. Runs the command if it is
1398 * defined, or prints an error message to the standard error
1401 * @param _command The name of the command to run.
1403 public void runCommand(String _command) {
1404 final String command = _command;
1406 // public void run() {
1407 if (actions.get(command) == null)
1408 Util.pr("No action defined for'" + command + "'");
1410 Object o = actions.get(command);
1412 if (o instanceof BaseAction)
1413 ((BaseAction)o).action();
1415 // This part uses Spin's features:
1416 Worker wrk = ((AbstractWorker)o).getWorker();
1417 // The Worker returned by getWorker() has been wrapped
1418 // by Spin.off(), which makes its methods be run in
1419 // a different thread from the EDT.
1420 CallBack clb = ((AbstractWorker)o).getCallBack();
1422 ((AbstractWorker)o).init(); // This method runs in this same thread, the EDT.
1423 // Useful for initial GUI actions, like printing a message.
1425 // The CallBack returned by getCallBack() has been wrapped
1426 // by Spin.over(), which makes its methods be run on
1428 wrk.run(); // Runs the potentially time-consuming action
1429 // without freezing the GUI. The magic is that THIS line
1430 // of execution will not continue until run() is finished.
1431 clb.update(); // Runs the update() method on the EDT.
1433 } catch (Throwable ex) {
1434 // If the action has blocked the JabRefFrame before crashing, we need to unblock it.
1435 // The call to unblock will simply hide the glasspane, so there is no harm in calling
1436 // it even if the frame hasn't been blocked.
1438 ex.printStackTrace();
1445 private boolean saveDatabase(File file, boolean selectedOnly, String encoding) throws SaveException {
1446 SaveSession session;
1450 session = FileActions.saveDatabase(database, metaData, file,
1451 Globals.prefs, false, false, encoding);
1453 session = FileActions.savePartOfDatabase(database, metaData, file,
1454 Globals.prefs, mainTable.getSelectedEntries(), encoding);
1456 } catch (SaveException ex) {
1457 if (ex.specificEntry()) {
1458 // Error occured during processing of
1459 // be. Highlight it:
1460 int row = mainTable.findEntry(ex.getEntry()),
1461 topShow = Math.max(0, row-3);
1462 mainTable.setRowSelectionInterval(row, row);
1463 mainTable.scrollTo(topShow);
1464 showEntry(ex.getEntry());
1466 else ex.printStackTrace();
1468 JOptionPane.showMessageDialog
1469 (frame, Globals.lang("Could not save file")
1470 +".\n"+ex.getMessage(),
1471 Globals.lang("Save database"),
1472 JOptionPane.ERROR_MESSAGE);
1473 throw new SaveException("rt");
1479 boolean commit = true;
1480 if (!session.getWriter().couldEncodeAll()) {
1481 String warning = Globals.lang("The chosen encoding '%0' could not encode the following characters: ",
1482 session.getEncoding())+session.getWriter().getProblemCharacters()
1483 +"\nDo you want to continue?";
1484 //int answer = JOptionPane.showConfirmDialog(frame, warning, Globals.lang("Save database"),
1485 // JOptionPane.YES_NO_OPTION);
1486 String message = Globals.lang("The chosen encoding '%0' could not encode the following characters: ",
1487 session.getEncoding())+session.getWriter().getProblemCharacters()
1488 +"\nWhat do you want to do?";
1489 String tryDiff = Globals.lang("Try different encoding");
1490 int answer = JOptionPane.showOptionDialog(frame, message, Globals.lang("Save database"),
1491 JOptionPane.YES_NO_CANCEL_OPTION, JOptionPane.WARNING_MESSAGE, null,
1492 new String[] {Globals.lang("Save"), tryDiff, Globals.lang("Cancel")}, tryDiff);
1494 if (answer == JOptionPane.NO_OPTION) {
1495 // The user wants to use another encoding.
1496 Object choice = JOptionPane.showInputDialog(frame, Globals.lang("Select encoding"), Globals.lang("Save database"),
1497 JOptionPane.QUESTION_MESSAGE, null, Globals.ENCODINGS, encoding);
1498 if (choice != null) {
1499 String newEncoding = (String)choice;
1500 return saveDatabase(file, selectedOnly, newEncoding);
1503 } else if (answer == JOptionPane.CANCEL_OPTION)
1512 this.encoding = encoding; // Make sure to remember which encoding we used.
1516 } catch (IOException e) {
1517 e.printStackTrace();
1525 * This method is called from JabRefFrame when the user wants to
1526 * create a new entry. If the argument is null, the user is
1527 * prompted for an entry type.
1529 * @param type The type of the entry to create.
1531 public void newEntry(BibtexEntryType type) {
1533 // Find out what type is wanted.
1534 EntryTypeDialog etd = new EntryTypeDialog(frame);
1535 // We want to center the dialog, to make it look nicer.
1536 Util.placeDialog(etd, frame);
1537 etd.setVisible(true);
1538 type = etd.getChoice();
1540 if (type != null) { // Only if the dialog was not cancelled.
1541 String id = Util.createNeutralId();
1542 final BibtexEntry be = new BibtexEntry(id, type);
1544 database.insertEntry(be);
1546 // Set owner/timestamp if options are enabled:
1547 ArrayList list = new ArrayList();
1549 Util.setAutomaticFields(list);
1551 // Create an UndoableInsertEntry object.
1552 undoManager.addEdit(new UndoableInsertEntry(database, be, BasePanel.this));
1553 output(Globals.lang("Added new")+" '"+type.getName().toLowerCase()+"' "
1554 +Globals.lang("entry")+".");
1555 final int row = mainTable.findEntry(be);
1557 // We are going to select the new entry. Before that, make sure that we are in
1558 // show-entry mode. If we aren't already in that mode, enter the WILL_SHOW_EDITOR
1559 // mode which makes sure the selection will trigger display of the entry editor
1560 // and adjustment of the splitter.
1561 if (mode != SHOWING_EDITOR) {
1562 mode = WILL_SHOW_EDITOR;
1565 highlightEntry(be); // Selects the entry. The selection listener will open the editor.
1567 markBaseChanged(); // The database just changed.
1568 new FocusRequester(getEntryEditor(be));
1569 } catch (KeyCollisionException ex) {
1570 Util.pr(ex.getMessage());
1575 public void mergeFromBibtex(ParserResult pr,
1576 boolean importEntries, boolean importStrings,
1577 boolean importGroups, boolean importSelectorWords)
1578 throws KeyCollisionException {
1580 BibtexDatabase fromDatabase = pr.getDatabase();
1581 ArrayList appendedEntries = new ArrayList();
1582 ArrayList originalEntries = new ArrayList();
1583 BibtexEntry originalEntry;
1584 NamedCompound ce = new NamedCompound(Globals.lang("Append database"));
1585 MetaData meta = new MetaData(pr.getMetaData(), pr.getDatabase());
1587 if (importEntries) { // Add entries
1588 Iterator i = fromDatabase.getKeySet().iterator();
1589 while (i.hasNext()) {
1590 originalEntry = fromDatabase.getEntryById((String) i.next());
1591 BibtexEntry be = (BibtexEntry) (originalEntry.clone());
1592 be.setId(Util.createNeutralId());
1593 database.insertEntry(be);
1594 appendedEntries.add(be);
1595 originalEntries.add(originalEntry);
1596 ce.addEdit(new UndoableInsertEntry(database, be, this));
1600 if (importStrings) {
1603 Iterator i = fromDatabase.getStringKeySet().iterator();
1604 for (; i.hasNext();) {
1605 bs = (BibtexString) (fromDatabase.getString(i.next()).clone());
1606 if (!database.hasStringLabel(bs.getName())) {
1607 //pos = toDatabase.getStringCount();
1608 database.addString(bs);
1609 ce.addEdit(new UndoableInsertString(this, database, bs));
1615 GroupTreeNode newGroups = meta.getGroups();
1616 if (newGroups != null) {
1618 // ensure that there is always only one AllEntriesGroup
1619 if (newGroups.getGroup() instanceof AllEntriesGroup) {
1620 // create a dummy group
1621 ExplicitGroup group = new ExplicitGroup("Imported",
1622 AbstractGroup.INDEPENDENT); // JZTODO lyrics
1623 newGroups.setGroup(group);
1624 for (int i = 0; i < appendedEntries.size(); ++i)
1625 group.addEntry((BibtexEntry) appendedEntries.get(i));
1628 // groupsSelector is always created, even when no groups
1629 // have been defined. therefore, no check for null is
1631 frame.groupSelector.addGroups(newGroups, ce);
1632 // for explicit groups, the entries copied to the mother fromDatabase have to
1633 // be "reassigned", i.e. the old reference is removed and the reference
1634 // to the new fromDatabase is added.
1636 ExplicitGroup group;
1638 for (Enumeration e = newGroups.preorderEnumeration(); e.hasMoreElements();) {
1639 node = (GroupTreeNode) e.nextElement();
1640 if (!(node.getGroup() instanceof ExplicitGroup))
1642 group = (ExplicitGroup) node.getGroup();
1643 for (int i = 0; i < originalEntries.size(); ++i) {
1644 entry = (BibtexEntry) originalEntries.get(i);
1645 if (group.contains(entry)) {
1646 group.removeEntry(entry);
1647 group.addEntry((BibtexEntry) appendedEntries.get(i));
1651 frame.groupSelector.revalidateGroups();
1655 if (importSelectorWords) {
1656 Iterator i = meta.iterator();
1657 while (i.hasNext()) {
1658 String s = (String) i.next();
1659 if (s.startsWith(Globals.SELECTOR_META_PREFIX)) {
1660 metaData().putData(s, meta.getData(s));
1666 undoManager.addEdit(ce);
1672 * This method is called from JabRefFrame when the user wants to
1673 * create a new entry.
1674 * @param bibEntry The new entry.
1676 public void insertEntry(BibtexEntry bibEntry)
1678 if (bibEntry != null)
1682 database.insertEntry(bibEntry) ;
1683 if (Globals.prefs.getBoolean("useOwner"))
1684 // Set owner field to default value
1685 bibEntry.setField(Globals.OWNER, Globals.prefs.get("defaultOwner") );
1686 // Create an UndoableInsertEntry object.
1687 undoManager.addEdit(new UndoableInsertEntry(database, bibEntry, BasePanel.this));
1688 output(Globals.lang("Added new")+" '"
1689 +bibEntry.getType().getName().toLowerCase()+"' "
1690 +Globals.lang("entry")+".");
1691 int row = mainTable.findEntry(bibEntry);
1693 mainTable.clearSelection();
1694 mainTable.scrollTo(row);
1695 markBaseChanged(); // The database just changed.
1696 if (Globals.prefs.getBoolean("autoOpenForm"))
1698 showEntry(bibEntry);
1700 } catch (KeyCollisionException ex) { Util.pr(ex.getMessage()); }
1704 public void createMainTable() {
1705 //Comparator comp = new FieldComparator("author");
1707 GlazedEntrySorter eventList = new GlazedEntrySorter(database.getEntryMap(), null);
1708 // Must initialize sort columns somehow:
1710 database.addDatabaseChangeListener(eventList);
1711 groupFilterList = new FilterList(eventList.getTheList(), NoSearchMatcher.INSTANCE);
1712 searchFilterList = new FilterList(groupFilterList, NoSearchMatcher.INSTANCE);
1713 //final SortedList sortedList = new SortedList(searchFilterList, null);
1714 MainTableFormat tableFormat = new MainTableFormat(this);
1715 tableFormat.updateTableFormat();
1716 //EventTableModel tableModel = new EventTableModel(sortedList, tableFormat);
1717 mainTable = new MainTable(/*tableModel, */tableFormat, searchFilterList);
1718 selectionListener = new MainTableSelectionListener(this, mainTable);
1719 mainTable.updateFont();
1720 mainTable.addSelectionListener(selectionListener);
1721 mainTable.addMouseListener(selectionListener);
1723 // Add the listener that will take care of highlighting groups as the selection changes:
1724 groupsHighlightListener = new ListEventListener() {
1725 public void listChanged(ListEvent listEvent) {
1726 if (Globals.prefs.getBoolean("highlightGroupsMatchingAny"))
1727 getGroupSelector().showMatchingGroups(
1728 mainTable.getSelectedEntries(), false);
1729 else if (Globals.prefs.getBoolean("highlightGroupsMatchingAll"))
1730 getGroupSelector().showMatchingGroups(
1731 mainTable.getSelectedEntries(), true);
1732 else // no highlight
1733 getGroupSelector().showMatchingGroups(null, true);
1736 mainTable.addSelectionListener(groupsHighlightListener);
1738 mainTable.getActionMap().put("cut", new AbstractAction() {
1739 public void actionPerformed(ActionEvent e) {
1740 try { runCommand("cut");
1741 } catch (Throwable ex) {
1742 ex.printStackTrace();
1746 mainTable.getActionMap().put("copy", new AbstractAction() {
1747 public void actionPerformed(ActionEvent e) {
1748 try { runCommand("copy");
1749 } catch (Throwable ex) {
1750 ex.printStackTrace();
1754 mainTable.getActionMap().put("paste", new AbstractAction() {
1755 public void actionPerformed(ActionEvent e) {
1756 try { runCommand("paste");
1757 } catch (Throwable ex) {
1758 ex.printStackTrace();
1763 mainTable.addKeyListener(new KeyAdapter() {
1765 public void keyPressed(KeyEvent e) {
1766 final int keyCode = e.getKeyCode();
1767 final TreePath path = frame.groupSelector.getSelectionPath();
1768 final GroupTreeNode node = path == null ? null : (GroupTreeNode) path.getLastPathComponent();
1770 if (e.isControlDown()) {
1772 // The up/down/left/rightkeystrokes are displayed in the
1773 // GroupSelector's popup menu, so if they are to be changed,
1774 // edit GroupSelector.java accordingly!
1775 case KeyEvent.VK_UP:
1778 frame.groupSelector.moveNodeUp(node, true);
1780 case KeyEvent.VK_DOWN:
1783 frame.groupSelector.moveNodeDown(node, true);
1785 case KeyEvent.VK_LEFT:
1788 frame.groupSelector.moveNodeLeft(node, true);
1790 case KeyEvent.VK_RIGHT:
1793 frame.groupSelector.moveNodeRight(node, true);
1795 case KeyEvent.VK_PAGE_DOWN:
1796 frame.nextTab.actionPerformed(null);
1799 case KeyEvent.VK_PAGE_UP:
1800 frame.prevTab.actionPerformed(null);
1804 } else if (keyCode == KeyEvent.VK_ENTER){
1806 try { runCommand("edit");
1807 } catch (Throwable ex) {
1808 ex.printStackTrace();
1815 public void setupMainPanel() {
1816 //System.out.println("setupMainPanel");
1817 //splitPane = new com.jgoodies.uif_lite.component.UIFSplitPane(JSplitPane.VERTICAL_SPLIT);
1818 splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT);
1819 splitPane.setDividerSize(GUIGlobals.SPLIT_PANE_DIVIDER_SIZE);
1820 // We replace the default FocusTraversalPolicy with a subclass
1821 // that only allows FieldEditor components to gain keyboard focus,
1822 // if there is an entry editor open.
1823 /*splitPane.setFocusTraversalPolicy(new LayoutFocusTraversalPolicy() {
1824 protected boolean accept(Component c) {
1826 if (showing == null)
1827 return super.accept(c);
1829 return (super.accept(c) &&
1830 (c instanceof FieldEditor));
1836 splitPane.setTopComponent(mainTable.getPane());
1839 // If an entry is currently being shown, make sure it stays shown,
1840 // otherwise set the bottom component to null.
1841 if (mode == SHOWING_PREVIEW) {
1842 mode = SHOWING_NOTHING;
1843 int row = mainTable.findEntry(currentPreview.entry);
1845 mainTable.setRowSelectionInterval(row, row);
1848 else if (mode == SHOWING_EDITOR) {
1849 mode = SHOWING_NOTHING;
1850 /*int row = mainTable.findEntry(currentEditor.entry);
1852 mainTable.setRowSelectionInterval(row, row);
1854 //showEntryEditor(currentEditor);
1856 splitPane.setBottomComponent(null);
1859 setLayout(new BorderLayout());
1861 add(splitPane, BorderLayout.CENTER);
1862 //add(contentPane, BorderLayout.CENTER);
1864 //add(sidePaneManager.getPanel(), BorderLayout.WEST);
1865 //add(splitPane, BorderLayout.CENTER);
1868 //con.fill = GridBagConstraints.BOTH;
1871 //gbl.setConstraints(sidePaneManager.getPanel(), con);
1873 //gbl.setConstraints(splitPane, con);
1874 //mainPanel.setDividerLocation(GUIGlobals.SPLIT_PANE_DIVIDER_LOCATION);
1875 //setDividerSize(GUIGlobals.SPLIT_PANE_DIVIDER_SIZE);
1876 //setResizeWeight(0);
1877 splitPane.revalidate();
1884 * This method is called after a database has been parsed. The
1885 * hashmap contains the contents of all comments in the .bib file
1886 * that started with the meta flag (GUIGlobals.META_FLAG).
1887 * In this method, the meta data are input to their respective
1890 * @param meta Metadata to input.
1892 public void parseMetaData(HashMap meta) {
1893 metaData = new MetaData(meta,database());
1898 public void refreshTable() {
1899 //System.out.println("hiding="+hidingNonHits+"\tlastHits="+lastSearchHits);
1900 // This method is called by EntryTypeForm when a field value is
1901 // stored. The table is scheduled for repaint.
1902 entryTable.assureNotEditing();
1903 //entryTable.invalidate();
1904 BibtexEntry[] bes = entryTable.getSelectedEntries();
1906 tableModel.update(lastSearchHits);
1908 tableModel.update();
1909 //tableModel.remap();
1910 if ((bes != null) && (bes.length > 0))
1911 selectEntries(bes, 0);
1913 //long toc = System.currentTimeMillis();
1914 // Util.pr("Refresh took: "+(toc-tic)+" ms");
1917 public void updatePreamble() {
1918 if (preambleEditor != null)
1919 preambleEditor.updatePreamble();
1922 public void assureStringDialogNotEditing() {
1923 if (stringDialog != null)
1924 stringDialog.assureNotEditing();
1927 public void updateStringDialog() {
1928 if (stringDialog != null)
1929 stringDialog.refreshTable();
1932 public void updateEntryPreviewToRow(BibtexEntry e) {
1936 public void adjustSplitter() {
1937 int mode = getMode();
1938 if (mode == SHOWING_PREVIEW) {
1939 splitPane.setDividerLocation(splitPane.getHeight()-GUIGlobals.PREVIEW_PANEL_HEIGHT);
1941 splitPane.setDividerLocation(GUIGlobals.VERTICAL_DIVIDER_LOCATION);
1949 * Stores the source view in the entry editor, if one is open, has the source view
1950 * selected and the source has been edited.
1951 * @return boolean false if there is a validation error in the source panel, true otherwise.
1953 public boolean entryEditorAllowsChange() {
1954 Component c = splitPane.getBottomComponent();
1955 if ((c != null) && (c instanceof EntryEditor)) {
1956 return ((EntryEditor)c).lastSourceAccepted();
1962 public void moveFocusToEntryEditor() {
1963 Component c = splitPane.getBottomComponent();
1964 if ((c != null) && (c instanceof EntryEditor)) {
1965 new FocusRequester(c);
1970 * Ensure that no preview is shown. Called when preview is turned off. Must chech if
1971 * a preview is in fact visible before doing anything rash.
1973 public void hidePreview() {
1974 Globals.prefs.putBoolean("previewEnabled", false);
1976 Component c = splitPane.getBottomComponent();
1977 if ((c != null) && !(c instanceof EntryEditor))
1978 splitPane.setBottomComponent(null);
1981 public boolean isShowingEditor() {
1982 return ((splitPane.getBottomComponent() != null)
1983 && (splitPane.getBottomComponent() instanceof EntryEditor));
1986 public void showEntry(final BibtexEntry be) {
1987 if (showing == be) {
1988 if (splitPane.getBottomComponent() == null) {
1989 // This is the special occasion when showing is set to an
1990 // entry, but no entry editor is in fact shown. This happens
1991 // after Preferences dialog is closed, and it means that we
1992 // must make sure the same entry is shown again. We do this by
1993 // setting showing to null, and recursively calling this method.
1997 // The correct entry is already being shown. Make sure the editor
1999 ((EntryEditor)splitPane.getBottomComponent()).updateAllFields();
2008 String visName = null;
2009 if (showing != null) {
2010 visName = ((EntryEditor)splitPane.getBottomComponent()).
2011 getVisiblePanelName();
2013 if (showing != null)
2014 divLoc = splitPane.getDividerLocation();
2016 if (entryEditors.containsKey(be.getType().getName())) {
2017 // We already have an editor for this entry type.
2018 form = (EntryEditor)entryEditors.get
2019 ((be.getType().getName()));
2021 if (visName != null)
2022 form.setVisiblePanel(visName);
2023 splitPane.setBottomComponent(form);
2024 //highlightEntry(be);
2026 // We must instantiate a new editor for this type.
2027 form = new EntryEditor(frame, BasePanel.this, be);
2028 if (visName != null)
2029 form.setVisiblePanel(visName);
2030 splitPane.setBottomComponent(form);
2032 //highlightEntry(be);
2033 entryEditors.put(be.getType().getName(), form);
2037 splitPane.setDividerLocation(divLoc);
2040 splitPane.setDividerLocation
2041 (GUIGlobals.VERTICAL_DIVIDER_LOCATION);
2042 //new FocusRequester(form);
2043 //form.requestFocus();
2046 setEntryEditorEnabled(true); // Make sure it is enabled.
2050 * Get an entry editor ready to edit the given entry. If an appropriate editor is already
2051 * cached, it will be updated and returned.
2052 * @param entry The entry to be edited.
2053 * @return A suitable entry editor.
2055 public EntryEditor getEntryEditor(BibtexEntry entry) {
2057 if (entryEditors.containsKey(entry.getType().getName())) {
2058 EntryEditor visibleNow = currentEditor;
2059 // We already have an editor for this entry type.
2060 form = (EntryEditor)entryEditors.get
2061 ((entry.getType().getName()));
2063 form.switchTo(entry);
2064 //if (visName != null)
2065 // form.setVisiblePanel(visName);
2067 // We must instantiate a new editor for this type.
2068 form = new EntryEditor(frame, BasePanel.this, entry);
2069 //if (visName != null)
2070 // form.setVisiblePanel(visName);
2072 entryEditors.put(entry.getType().getName(), form);
2077 public EntryEditor getCurrentEditor() {
2078 return currentEditor;
2082 * Sets the given entry editor as the bottom component in the split pane. If an entry editor already
2083 * was shown, makes sure that the divider doesn't move.
2084 * Updates the mode to SHOWING_EDITOR.
2085 * @param editor The entry editor to add.
2087 public void showEntryEditor(EntryEditor editor) {
2088 int oldSplitterLocation = -1;
2089 if (mode == SHOWING_EDITOR)
2090 oldSplitterLocation = splitPane.getDividerLocation();
2091 boolean adjustSplitter = (mode == WILL_SHOW_EDITOR);
2092 mode = SHOWING_EDITOR;
2093 currentEditor = editor;
2094 splitPane.setBottomComponent(editor);
2095 if (oldSplitterLocation > 0)
2096 splitPane.setDividerLocation(oldSplitterLocation);
2097 if (adjustSplitter) {
2099 //new FocusRequester(editor);
2104 * Sets the given preview panel as the bottom component in the split panel.
2105 * Updates the mode to SHOWING_PREVIEW.
2106 * @param preview The preview to show.
2108 public void showPreview(PreviewPanel preview) {
2109 mode = SHOWING_PREVIEW;
2110 currentPreview = preview;
2111 splitPane.setBottomComponent(preview.getPane());
2115 * Removes the bottom component.
2117 public void hideBottomComponent() {
2118 mode = SHOWING_NOTHING;
2119 splitPane.setBottomComponent(null);
2123 * This method selects the given entry, and scrolls it into view in the table.
2124 * If an entryEditor is shown, it is given focus afterwards.
2126 public void highlightEntry(final BibtexEntry be) {
2127 //SwingUtilities.invokeLater(new Thread() {
2128 // public void run() {
2129 final int row = mainTable.findEntry(be);
2131 mainTable.setRowSelectionInterval(row, row);
2132 //entryTable.setActiveRow(row);
2133 mainTable.ensureVisible(row);
2141 * This method is called from an EntryEditor when it should be closed. We relay
2142 * to the selection listener, which takes care of the rest.
2143 * @param editor The entry editor to close.
2145 public void entryEditorClosing(EntryEditor editor) {
2146 selectionListener.entryEditorClosing(editor);
2150 * This method selects the given enties.
2151 * If an entryEditor is shown, it is given focus afterwards.
2153 /*public void selectEntries(final BibtexEntry[] bes, final int toScrollTo) {
2155 SwingUtilities.invokeLater(new Thread() {
2157 int rowToScrollTo = 0;
2158 entryTable.revalidate();
2159 entryTable.clearSelection();
2160 loop: for (int i=0; i<bes.length; i++) {
2163 int row = tableModel.getNumberFromName(bes[i].getId());
2165 rowToScrollTo = row;
2167 entryTable.addRowSelectionIntervalQuietly(row, row);
2169 entryTable.ensureVisible(rowToScrollTo);
2170 Component comp = splitPane.getBottomComponent();
2171 //if (comp instanceof EntryEditor)
2172 // comp.requestFocus();
2178 * Closes the entry editor if it is showing the given entry.
2180 * @param be a <code>BibtexEntry</code> value
2182 public void ensureNotShowing(BibtexEntry be) {
2183 if ((mode == SHOWING_EDITOR) && (currentEditor.getEntry() == be)) {
2184 selectionListener.entryEditorClosing(currentEditor);
2188 public void updateEntryEditorIfShowing() {
2189 if (mode == SHOWING_EDITOR) {
2190 if (currentEditor.getType() != currentEditor.getEntry().getType()) {
2191 // The entry has changed type, so we must get a new editor.
2193 EntryEditor newEditor = getEntryEditor(currentEditor.getEntry());
2194 showEntryEditor(newEditor);
2196 currentEditor.updateAllFields();
2197 currentEditor.updateSource();
2203 * If an entry editor is showing, make sure its currently focused field
2204 * stores its changes, if any.
2206 public void storeCurrentEdit() {
2207 if (isShowingEditor()) {
2208 EntryEditor editor = (EntryEditor)splitPane.getBottomComponent();
2209 editor.storeCurrentEdit();
2215 * This method iterates through all existing entry editors in this
2216 * BasePanel, telling each to update all its instances of
2217 * FieldContentSelector. This is done to ensure that the list of words
2218 * in each selector is up-to-date after the user has made changes in
2219 * the Manage dialog.
2221 public void updateAllContentSelectors() {
2222 for (Iterator i=entryEditors.keySet().iterator(); i.hasNext();) {
2223 EntryEditor ed = (EntryEditor)entryEditors.get(i.next());
2224 ed.updateAllContentSelectors();
2228 public void rebuildAllEntryEditors() {
2229 for (Iterator i=entryEditors.keySet().iterator(); i.hasNext();) {
2230 EntryEditor ed = (EntryEditor)entryEditors.get(i.next());
2236 public void markBaseChanged() {
2239 // Put an asterix behind the file name to indicate the
2240 // database has changed.
2241 String oldTitle = frame.getTabTitle(this);
2242 if (!oldTitle.endsWith("*"))
2243 frame.setTabTitle(this, oldTitle+"*");
2245 // If the status line states that the base has been saved, we
2246 // remove this message, since it is no longer relevant. If a
2247 // different message is shown, we leave it.
2248 if (frame.statusLine.getText().startsWith("Saved database"))
2252 public void markNonUndoableBaseChanged() {
2253 nonUndoableChange = true;
2257 public synchronized void markChangedOrUnChanged() {
2258 if (undoManager.hasChanged()) {
2262 else if (baseChanged && !nonUndoableChange) {
2263 baseChanged = false;
2265 frame.setTabTitle(BasePanel.this, file.getName());
2267 frame.setTabTitle(BasePanel.this, Globals.lang("untitled"));
2272 * Selects a single entry, and scrolls the table to center it.
2274 * @param pos Current position of entry to select.
2277 public void selectSingleEntry(int pos) {
2278 mainTable.clearSelection();
2279 mainTable.addRowSelectionInterval(pos, pos);
2280 mainTable.scrollToCenter(pos, 0);
2284 * Selects all entries with a non-zero value in the field
2285 * @param field <code>String</code> field name.
2287 /* public void selectResults(String field) {
2288 LinkedList intervals = new LinkedList();
2289 int prevStart = -1, prevToSel = 0;
2290 // First we build a list of intervals to select, without touching the table.
2291 for (int i = 0; i < entryTable.getRowCount(); i++) {
2292 String value = (String) (database.getEntryById
2293 (tableModel.getIdForRow(i)))
2295 if ( (value != null) && !value.equals("0")) {
2300 else if (prevStart >= 0) {
2301 intervals.add(new int[] {prevStart, prevToSel});
2305 // Then select those intervals, if any.
2306 if (intervals.size() > 0) {
2307 entryTable.setSelectionListenerEnabled(false);
2308 entryTable.clearSelection();
2309 for (Iterator i=intervals.iterator(); i.hasNext();) {
2310 int[] interval = (int[])i.next();
2311 entryTable.addRowSelectionInterval(interval[0], interval[1]);
2313 entryTable.setSelectionListenerEnabled(true);
2317 public void setSearchMatcher(SearchMatcher matcher) {
2318 searchFilterList.setMatcher(matcher);
2321 public void setGroupMatcher(Matcher matcher) {
2322 groupFilterList.setMatcher(matcher);
2325 public void stopShowingSearchResults() {
2326 searchFilterList.setMatcher(NoSearchMatcher.INSTANCE);
2329 public void stopShowingGroup() {
2330 groupFilterList.setMatcher(NoSearchMatcher.INSTANCE);
2334 public BibtexDatabase getDatabase(){
2338 public void preambleEditorClosing() {
2339 preambleEditor = null;
2342 public void stringsClosing() {
2343 stringDialog = null;
2346 public void changeType(BibtexEntry entry, BibtexEntryType type) {
2347 changeType(new BibtexEntry[] {entry}, type);
2350 public void changeType(BibtexEntryType type) {
2351 BibtexEntry[] bes = mainTable.getSelectedEntries();
2352 changeType(bes, type);
2355 public void changeType(BibtexEntry[] bes, BibtexEntryType type) {
2357 if ((bes == null) || (bes.length == 0)) {
2358 output("First select the entries you wish to change type "+
2362 if (bes.length > 1) {
2363 int choice = JOptionPane.showConfirmDialog
2364 (this, "Multiple entries selected. Do you want to change"
2365 +"\nthe type of all these to '"+type.getName()+"'?",
2366 "Change type", JOptionPane.YES_NO_OPTION,
2367 JOptionPane.WARNING_MESSAGE);
2368 if (choice == JOptionPane.NO_OPTION)
2372 NamedCompound ce = new NamedCompound(Globals.lang("change type"));
2373 for (int i=0; i<bes.length; i++) {
2374 ce.addEdit(new UndoableChangeType(bes[i],
2377 bes[i].setType(type);
2380 output(Globals.lang("Changed type to")+" '"+type.getName()+"' "
2381 +Globals.lang("for")+" "+bes.length
2382 +" "+Globals.lang("entries")+".");
2384 undoManager.addEdit(ce);
2386 updateEntryEditorIfShowing();
2389 public boolean showDeleteConfirmationDialog(int numberOfEntries) {
2390 if (Globals.prefs.getBoolean("confirmDelete")) {
2391 String msg = Globals.lang("Really delete the selected")
2392 + " " + Globals.lang("entry") + "?",
2393 title = Globals.lang("Delete entry");
2394 if (numberOfEntries > 1) {
2395 msg = Globals.lang("Really delete the selected")
2396 + " " + numberOfEntries + " " + Globals.lang("entries") + "?";
2397 title = Globals.lang("Delete multiple entries");
2400 CheckBoxMessage cb = new CheckBoxMessage
2401 (msg, Globals.lang("Disable this confirmation dialog"), false);
2403 int answer = JOptionPane.showConfirmDialog(frame, cb, title,
2404 JOptionPane.YES_NO_OPTION,
2405 JOptionPane.QUESTION_MESSAGE);
2406 if (cb.isSelected())
2407 Globals.prefs.putBoolean("confirmDelete", false);
2408 return (answer == JOptionPane.YES_OPTION);
2414 * Activates or deactivates the entry preview, depending on the argument.
2415 * When deactivating, makes sure that any visible preview is hidden.
2418 public void setPreviewActive(boolean enabled) {
2419 selectionListener.setPreviewActive(enabled);
2423 class UndoAction extends BaseAction {
2424 public void action() {
2426 String name = undoManager.getUndoPresentationName();
2430 } catch (CannotUndoException ex) {
2431 frame.output(Globals.lang("Nothing to undo")+".");
2433 // After everything, enable/disable the undo/redo actions
2435 //updateUndoState();
2436 //redoAction.updateRedoState();
2437 markChangedOrUnChanged();
2441 class RedoAction extends BaseAction {
2442 public void action() {
2444 String name = undoManager.getRedoPresentationName();
2448 } catch (CannotRedoException ex) {
2449 frame.output(Globals.lang("Nothing to redo")+".");
2451 // After everything, enable/disable the undo/redo actions
2453 //updateRedoState();
2454 //undoAction.updateUndoState();
2455 markChangedOrUnChanged();
2459 // Method pertaining to the ClipboardOwner interface.
2460 public void lostOwnership(Clipboard clipboard, Transferable contents) {}
2463 public void setEntryEditorEnabled(boolean enabled) {
2464 if ((showing != null) && (splitPane.getBottomComponent() instanceof EntryEditor)) {
2465 EntryEditor ed = (EntryEditor)splitPane.getBottomComponent();
2466 if (ed.isEnabled() != enabled)
2467 ed.setEnabled(enabled);
2471 public String fileMonitorHandle() { return fileMonitorHandle; }
2473 public void fileUpdated() {
2475 return; // We are just saving the file, so this message is most likely due
2476 // to bad timing. If not, we'll handle it on the next polling.
2477 //Util.pr("File '"+file.getPath()+"' has been modified.");
2478 updatedExternally = true;
2480 // Adding the sidepane component is Swing work, so we must do this in the Swing
2482 Thread t = new Thread() {
2484 // Test: running scan automatically in background
2485 ChangeScanner scanner = new ChangeScanner(frame, BasePanel.this);
2486 scanner.changeScan(BasePanel.this.file());
2489 } catch (InterruptedException e) {
2490 e.printStackTrace();
2493 if (scanner.changesFound()) {
2494 FileUpdatePanel pan = new FileUpdatePanel(frame, BasePanel.this, sidePaneManager, file, scanner);
2495 sidePaneManager.add("fileUpdate", pan);
2496 setUpdatedExternally(false);
2497 //scanner.displayResult();
2499 setUpdatedExternally(false);
2500 //System.out.println("No changes found.");
2505 SwingUtilities.invokeLater(t);
2509 public void fileRemoved() {
2510 Util.pr("File '"+file.getPath()+"' has been deleted.");
2514 public void cleanUp() {
2515 if (fileMonitorHandle != null)
2516 Globals.fileUpdateMonitor.removeUpdateListener(fileMonitorHandle);
2519 public void setUpdatedExternally(boolean b) {
2520 updatedExternally = b;
2524 * Get an array containing the currently selected entries.
2526 * @return An array containing the selected entries.
2528 public BibtexEntry[] getSelectedEntries() {
2529 return mainTable.getSelectedEntries();
2533 * Get a String containing a comma-separated list of the bibtex keys
2534 * of the selected entries.
2536 * @return A comma-separated list of the keys of the selected entries.
2538 public String getKeysForSelection() {
2539 List entries = mainTable.getSelected();
2540 StringBuffer result = new StringBuffer();
2541 String citeKey = "";//, message = "";
2542 boolean first = true;
2543 for (Iterator i = entries.iterator(); i.hasNext();) {
2544 BibtexEntry bes = (BibtexEntry) i.next();
2545 citeKey = (String) bes.getField(GUIGlobals.KEY_FIELD);
2546 // if the key is empty we give a warning and ignore this entry
2547 if (citeKey == null || citeKey.equals(""))
2550 result.append(citeKey);
2553 result.append(",").append(citeKey);
2556 return result.toString();
2559 public GroupSelector getGroupSelector() {
2560 return frame.groupSelector;