Save This Page
Home » openjdk-7 » net » sf » bibkeeper » [javadoc | source]
    1   package net.sf.bibkeeper;
    2   
    3   import net.sf.bibkeeper.undo;
    4   import net.sf.bibkeeper.export;
    5   import net.sf.bibkeeper.groups.QuickSearchRule;
    6   import javax.swing;
    7   import java.awt;
    8   import java.awt.event;
    9   import java.io;
   10   import java.util;
   11   import java.awt.datatransfer;
   12   import javax.swing.undo;
   13   
   14   public class BibtexBaseFrame extends JFrame implements MouseListener, ClipboardOwner {
   15   
   16       static int frameCount = 0; 
   17       // This variable keeps track of how many Bibtex bases are open.
   18       // When the last one is closed, the program shuts down.
   19   
   20       BibtexDatabase database;
   21       // The database we are representing.
   22       File file = null, fileToOpen = null; // The filename of the database.
   23   
   24       Hashtable autoCompleters = new Hashtable();
   25       // Hashtable that holds as keys the names of the fields where 
   26       // autocomplete is active, and references to the autocompleter objects.
   27   
   28       // The undo manager.
   29       public CountingUndoManager undoManager = new CountingUndoManager();
   30   
   31       //BibkeeperPreferences prefs = BibkeeperPreferences.loadPreferences();
   32       BibkeeperPrefs prefs = new BibkeeperPrefs();
   33       // The user preferences are contained in this objects.
   34   
   35       ExampleFileFilter fileFilter;
   36       // File filter for .bib files.
   37   
   38       boolean baseChanged = false;
   39       // Used to track whether the base has changed since last save.
   40   
   41       //JPanel mainPanel = new JPanel();
   42       JSplitPane mainPanel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true);
   43       EntryTableModel tableModel = null;
   44       EntryTable entryTable = null;
   45       JLabel statusLine = new JLabel("", SwingConstants.LEFT);
   46   
   47       HashMap entryTypeForms = new HashMap();
   48       // Hashmap to keep track of which entries currently have open
   49       // EntryTypeForm dialogs.
   50       PreambleEditor preambleEditor = null;
   51       // Keeps track of the preamble dialog if it is open.
   52       StringDialog stringDialog = null;
   53       // Keeps track of the string dialog if it is open.
   54       public HelpDialog helpDiag = new HelpDialog(this);
   55       // The help window.
   56       SearchPane searchDialog = null;
   57       // The search pane.
   58       boolean showingSearchResults = false, 
   59   	showingGroup = false;
   60   
   61       SidePaneManager sidePaneManager;
   62       // The sidepane manager takes care of populating the sidepane.
   63       MetaData metaData;
   64       // MetaData parses, keeps and writes meta data.
   65   
   66       protected final static String KEY_PROPERTY = "bibtexkey";
   67       protected final static String SEARCH_PROPERTY = "search";
   68   
   69       public BibtexBaseFrame() {
   70           init();
   71           if ((prefs.getBoolean("openLastEdited")) && (prefs.get("lastEdited") != null)) {
   72               // Try to load latest edited database.
   73               fileToOpen = new File(prefs.get("lastEdited"));
   74   	    Util.pr("Opening last edited database: '"+fileToOpen.getPath()+"'");
   75               openDatabaseAction.openIt();
   76           }
   77       }
   78   
   79       public BibtexBaseFrame(BibtexDatabase db, MetaData meta) {
   80           // Create a BibtexBaseFrame for an existing unnamed database.
   81           init();
   82           database = db;
   83   	metaData = meta;
   84   	if (prefs.getBoolean("autoComplete")) {
   85   	    db.setCompleters(autoCompleters);
   86   	}
   87           setupDatabaseLayout();
   88       }
   89   
   90       public BibtexBaseFrame(BibtexDatabase db, File file, HashMap meta) {
   91           // Create a BibtexBaseFrame for an existing named database.
   92           init();
   93           database = db;
   94   	parseMetaData(meta);
   95   	if (prefs.getBoolean("autoComplete")) {
   96   	    db.setCompleters(autoCompleters);
   97   	}
   98   
   99           this.file = file;
  100           setupDatabaseLayout();
  101       }
  102   
  103       public BibtexBaseFrame(File file) {
  104   	init();
  105   	// We're given a file to load the database from.
  106   	if (file.exists()) {
  107   	    fileToOpen = file;
  108   	    openDatabaseAction.openIt();
  109   	}
  110       }
  111   
  112       public void init () {	
  113   	// Create a BibtexBaseFrame independent of database. Called by all
  114   	// constructors.
  115   	frameCount++; // Another one is opened.
  116   
  117   	// Add a ref to this frame to the global HashMap.
  118   	GUIGlobals.frames.put(""+frameCount, this);
  119   
  120   	// Set up which fields should be autocompleted.
  121   	assignAutoCompleters();
  122   
  123   	// Prevent the frame from closing on its own, and redirect closing events
  124   	// to the CloseAction.
  125   	setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
  126   	addWindowListener(new WindowAdapter() {
  127   		public void windowClosing(WindowEvent e) {
  128   		    closeAction.actionPerformed(null);
  129   		}	
  130   	    });
  131   
  132   	// Set up a file filter to choose .bib files. We use Sun's ExampleFileFilter.
  133   	fileFilter = new ExampleFileFilter();
  134   	fileFilter.addExtension("bib");
  135   	fileFilter.setDescription("BibTeX databases");
  136   	
  137   	//setIconImage((new ImageIcon(GUIGlobals.appIconFile)).getImage());
  138      
  139       // initialize the labelMaker
  140       labelMaker = new LabelMaker() ; 
  141       labelMaker.addRule(new ArticleLabelRule(),BibtexEntryType.ARTICLE) ; 
  142       labelMaker.addRule(new BookLabelRule(),BibtexEntryType.BOOK) ; 
  143       labelMaker.addRule(new IncollectionLabelRule(),BibtexEntryType.INCOLLECTION) ; 
  144       labelMaker.addRule(new InproceedingsLabelRule(),BibtexEntryType.INPROCEEDINGS) ; 
  145   
  146   	setupLayout();	
  147   	setEmptyState();
  148       }
  149   
  150       private void setupLayout() {
  151   	GridBagLayout gbl = new GridBagLayout();
  152   	GridBagConstraints con = new GridBagConstraints();
  153   	getContentPane().setLayout(gbl);
  154   	setJMenuBar(createMenuBar());
  155   	JToolBar tb = createToolBar();
  156   	con.weightx = 1;
  157   	con.weighty = 0;
  158   	con.anchor = GridBagConstraints.NORTHWEST;
  159   	con.gridwidth = GridBagConstraints.REMAINDER;
  160   	con.fill = GridBagConstraints.BOTH;
  161   	gbl.setConstraints(tb,con);
  162   	getContentPane().add(tb);
  163   	con.weighty = 1;
  164   	con.anchor = GridBagConstraints.CENTER;
  165   	gbl.setConstraints(mainPanel,con);
  166   	getContentPane().add(mainPanel);
  167   	con.anchor = GridBagConstraints.WEST;
  168   	con.weighty = 0;
  169   	con.insets = new Insets(0, 5, 0, 0);
  170   	gbl.setConstraints(statusLine,con);
  171   	getContentPane().add(statusLine);
  172   
  173   	setLocation(getSetLocation());
  174   	setSize(getSetSize());
  175   
  176   	output("Welcome to Bibkeeper!");
  177       }
  178   
  179       public Dimension getSetSize() {
  180   	return new Dimension(prefs.getInt("sizeX"), prefs.getInt("sizeY"));
  181       }
  182   
  183       public Point getSetLocation() {
  184   	return new Point(prefs.getInt("posX"), prefs.getInt("posY"));
  185       }
  186       private void setEmptyState() {
  187   	// Disable actions that demand an open database.
  188   	copyKeyAction.setEnabled(false);
  189   	saveDatabaseAction.setEnabled(false);
  190   	saveAsDatabaseAction.setEnabled(false);
  191   	newEntryAction.setEnabled(false);
  192   	copyAction.setEnabled(false);
  193   	pasteAction.setEnabled(false);
  194   	removeEntryAction.setEnabled(false);	
  195   	editEntryAction.setEnabled(false);
  196   	editPreambleAction.setEnabled(false);
  197   	editStringsAction.setEnabled(false);
  198   	closeDatabaseAction.setEnabled(false);
  199   	mergeDatabaseAction.setEnabled(false);
  200   	searchPaneAction.setEnabled(false);
  201   	makeLabelAction.setEnabled(false);
  202   	showGroupsAction.setEnabled(false);
  203   	for (int i=0; i<newSpecificEntryAction.length; i++)
  204   	    newSpecificEntryAction[i].setEnabled(false);
  205   	setTitle(GUIGlobals.emptyTitle);       
  206       }
  207   
  208       private void setNonEmptyState() {
  209   	// Enable actions that demand an open database.
  210   	copyKeyAction.setEnabled(true);
  211   	saveDatabaseAction.setEnabled(true);
  212   	saveAsDatabaseAction.setEnabled(true);
  213   	newEntryAction.setEnabled(true);
  214   	removeEntryAction.setEnabled(true);
  215   	copyAction.setEnabled(true);
  216   	pasteAction.setEnabled(true);
  217   	editEntryAction.setEnabled(true);
  218   	editPreambleAction.setEnabled(true);
  219   	editStringsAction.setEnabled(true);
  220   	closeDatabaseAction.setEnabled(true);
  221   	mergeDatabaseAction.setEnabled(true);
  222   	searchPaneAction.setEnabled(true);
  223   	makeLabelAction.setEnabled(true);
  224   	showGroupsAction.setEnabled(true);
  225   	for (int i=0; i<newSpecificEntryAction.length; i++)
  226   	    newSpecificEntryAction[i].setEnabled(true);
  227   	//setupTableAction.setEnabled(true);
  228       }
  229   
  230       private void setupDatabaseLayout() {
  231   	// This method is called whenever this frame has been provided
  232   	// with a database, and completes the layout.
  233   
  234   	setupMainPanel();
  235   	//getContentPane().add(mainPanel, BorderLayout.CENTER);
  236   	if (file != null)
  237   	    setTitle(GUIGlobals.baseTitle+file.getName());
  238   	else
  239   	    setTitle(GUIGlobals.untitledTitle);
  240   
  241   	//DragNDropManager dndm = new DragNDropManager(this);
  242   
  243   
  244   	setNonEmptyState();
  245   	
  246       }
  247   
  248       private JToolBar createToolBar() {
  249   	JToolBar tb = new JToolBar();
  250   	tb.setFloatable(false);
  251   	tb.add(newDatabaseAction);
  252   	tb.add(openDatabaseAction);
  253   	tb.add(saveDatabaseAction);
  254   	tb.addSeparator();
  255   	//tb.add(closeDatabaseAction);
  256   	//tb.addSeparator();
  257   	tb.add(copyKeyAction);
  258   	tb.add(makeLabelAction);
  259   	tb.addSeparator();
  260   	tb.add(editPreambleAction);
  261   	tb.add(editStringsAction);
  262   	tb.add(newEntryAction);
  263   	tb.add(editEntryAction);
  264   	tb.add(removeEntryAction);
  265   	tb.add(copyAction);
  266   	tb.add(pasteAction);
  267   	tb.add(searchPaneAction);
  268   	tb.addSeparator();
  269   	tb.add(setupTableAction);
  270   	tb.addSeparator();
  271   	tb.add(new HelpAction(helpDiag,GUIGlobals.baseFrameHelp, "Help"));
  272   	//tb.setBackground(Color.white);
  273   	return tb;
  274       }
  275   
  276       private JMenuBar createMenuBar() {
  277   	JMenuBar mb = new JMenuBar();
  278   	JMenu file = new JMenu("File");
  279   	file.add(mItem(newDatabaseAction, null/*GUIGlobals.newKeyStroke*/));
  280   	file.add(mItem(openDatabaseAction, GUIGlobals.openKeyStroke));
  281   	file.add(mItem(saveDatabaseAction, GUIGlobals.saveKeyStroke));
  282   	file.add(mItem(saveAsDatabaseAction, null));
  283   	file.add(mItem(saveSpecialAction, null));
  284   	file.addSeparator();
  285   	file.add(mItem(mergeDatabaseAction, null));
  286   	file.addSeparator();
  287   	file.add(mItem(closeDatabaseAction, null));
  288   	file.add(mItem(closeAction, GUIGlobals.closeKeyStroke));
  289   	mb.add(file);
  290   
  291   	JMenu view = new JMenu("View"), 
  292   	    entry = new JMenu("Edit"),
  293   	    entryType = new JMenu("New ..."),
  294   	    bibtex = new JMenu("Bibtex");
  295   	for (int i=0; i<newSpecificEntryAction.length; i++)
  296   	    entryType.add(mItem(newSpecificEntryAction[i], 
  297   				newSpecificEntryAction[i].keyStroke));
  298   	entry.add(mItem(undoAction, GUIGlobals.undoStroke));
  299   	entry.add(mItem(redoAction, GUIGlobals.redoStroke));
  300   	entry.addSeparator();
  301   	entry.add(mItem(removeEntryAction, GUIGlobals.removeEntryKeyStroke));
  302   	entry.add(mItem(copyAction, GUIGlobals.copyStroke));
  303   	entry.add(mItem(pasteAction, GUIGlobals.pasteStroke));
  304   	entry.addSeparator();
  305   	entry.add(mItem(selectAllAction, GUIGlobals.selectAllKeyStroke));
  306   
  307   	view.add(mItem(showGroupsAction, GUIGlobals.showGroupsKeyStroke));
  308   
  309   	bibtex.add(entryType);
  310   	bibtex.add(mItem(newEntryAction, GUIGlobals.newEntryKeyStroke));
  311   	bibtex.addSeparator();
  312   	bibtex.add(mItem(copyKeyAction, GUIGlobals.copyKeyStroke));
  313   	bibtex.add(mItem(editPreambleAction, GUIGlobals.editPreambleKeyStroke));
  314   	bibtex.add(mItem(editStringsAction, GUIGlobals.editStringsKeyStroke));
  315   	bibtex.add(mItem(editEntryAction, GUIGlobals.editEntryKeyStroke));
  316   
  317   
  318   	mb.add(entry);
  319   	mb.add(view);
  320   	mb.add(bibtex);
  321   
  322   	JMenu tools = new JMenu("Tools");
  323   	tools.add(mItem(searchPaneAction, GUIGlobals.simpleSearchKeyStroke));
  324   	JMenu autoGenerateMenu = new JMenu("Autogenerate Bibtexkey") ; 
  325   	tools.add(mItem(makeLabelAction, GUIGlobals.generateKeyStroke));
  326   	tools.add(mItem(checkUniqueLabelAction, null));
  327   	//tools.add(autoGenerateMenu) ; 
  328   	mb.add(tools);
  329   
  330   	JMenu options = new JMenu("Options");
  331   	//options.add(mItem(setupTableAction, GUIGlobals.setupTableKeyStroke));
  332   	options.add(setupTableAction);
  333   	mb.add(options);
  334   
  335   	JMenu help = new JMenu("Help");
  336   	help.add(mItem(new HelpAction(helpDiag, GUIGlobals.baseFrameHelp, "Help"),
  337   		       GUIGlobals.helpKeyStroke));
  338   	help.add(new HelpAction("Help contents", helpDiag, 
  339   				GUIGlobals.helpContents, "Help contents"));
  340   	help.addSeparator();
  341   	help.add(mItem(aboutAction, null));
  342   	mb.add(help);
  343   
  344   	return mb;
  345       }
  346   
  347       private JMenuItem mItem(AbstractAction a, KeyStroke ks) {
  348   	// Set up a menu item with action and accelerator key.
  349   	JMenuItem mi = new JMenuItem();
  350   	mi.setAction(a);
  351   	if (ks != null)
  352   	    mi.setAccelerator(ks);
  353   	return mi;
  354       }
  355   
  356       //private void setupMainPanel() {
  357       public void setupMainPanel() {
  358   	tableModel = new EntryTableModel(this, database);
  359   	entryTable = new EntryTable(tableModel, prefs);
  360   	entryTable.addMouseListener(this);
  361   	entryTable.getInputMap().put(GUIGlobals.copyStroke, "copy");
  362   	entryTable.getInputMap().put(GUIGlobals.pasteStroke, "paste");
  363   	entryTable.getActionMap().put("copy", copyAction);
  364   	entryTable.getActionMap().put("paste", pasteAction);
  365   
  366   	// Set the right-click menu for the entry table.
  367   	RightClickMenu rcm = new RightClickMenu(this, metaData);
  368   	entryTable.setRightClickMenu(rcm);
  369   
  370   	//mainPanel.removeAll();
  371   	//mainPanel.setLayout(new GridLayout(1,1));
  372   	mainPanel.setRightComponent(entryTable.getPane());
  373   	sidePaneManager = new SidePaneManager
  374   	    (prefs, metaData, this, mainPanel);
  375   	sidePaneManager.populatePanel();
  376   
  377   	//mainPanel.setDividerLocation(GUIGlobals.SPLIT_PANE_DIVIDER_LOCATION);
  378   	mainPanel.setDividerSize(GUIGlobals.SPLIT_PANE_DIVIDER_SIZE);
  379   	mainPanel.setResizeWeight(0);
  380   	mainPanel.revalidate();
  381       }
  382   
  383       public void updatePreamble() {
  384   	if (preambleEditor != null)
  385   	    preambleEditor.updatePreamble();
  386       }
  387       
  388       public void assureStringDialogNotEditing() {
  389   	if (stringDialog != null)
  390   	    stringDialog.assureNotEditing();
  391       }
  392   
  393       public void updateStringDialog() {
  394   	if (stringDialog != null)
  395   	    stringDialog.refreshTable();
  396       }
  397       
  398       public void refreshTable() {
  399   	// This method is called by EntryTypeForm when a field value is
  400   	// stored. The table is scheduled for repaint.
  401   	tableModel.remap();
  402   	entryTable.revalidate();
  403   	entryTable.repaint();
  404       }
  405   
  406       public void markBaseChanged() {
  407   	baseChanged = true;
  408   
  409   	// Put an asterix behind the file name to indicate the database has changed.
  410   	if (!getTitle().endsWith("*"))
  411   	    setTitle(getTitle()+"*");
  412   
  413   	// If the status line states that the base has been saved, we remove this
  414   	// message, since it is no longer relevant. If a different message is shown,
  415   	// we leave it.
  416   	if (statusLine.getText().startsWith("Saved database"))
  417   	    output(" ");
  418   	    
  419   	// Update undo/redo states:
  420   	undoAction.updateUndoState();	   
  421   	redoAction.updateRedoState();
  422   
  423       }
  424   
  425       public synchronized void markChangedOrUnChanged() {
  426   	if (undoManager.hasChanged()) {
  427   	    if (!baseChanged)
  428   		markBaseChanged();
  429   	}
  430   	else if (baseChanged) {
  431   	    baseChanged = false;
  432   	    if (file != null)
  433   		setTitle(GUIGlobals.baseTitle+file.getName());
  434   	    else
  435   		setTitle(GUIGlobals.untitledTitle);
  436   	}
  437       }
  438   
  439       public Completer getAutoCompleter(String field) {
  440   	return (Completer)autoCompleters.get(field);
  441       }
  442   
  443       public void assignAutoCompleters() {
  444   	// Set up which fields should have autocompletion. This should
  445   	// probably be made customizable. Existing Completer objects are
  446   	// forgotten. The completers must be updated towards the database.
  447   	byte[] fields = prefs.getByteArray("autoCompFields");
  448   	autoCompleters = new Hashtable();
  449   	for (int i=0; i<fields.length; i++) {
  450   	    autoCompleters.put(GUIGlobals.ALL_FIELDS[fields[i]], new Completer());
  451   	}
  452   	
  453       }
  454       
  455       public void updateAutoCompleters() {
  456   	if (database != null)
  457   	    database.setCompleters(autoCompleters);
  458       }
  459   
  460       /**
  461        * This method is called after a database has been parsed. The
  462        * hashmap contains the contents of all comments in the .bib file
  463        * that started with the meta flag (GUIGlobals.META_FLAG).
  464        * In this method, the meta data are input to their respective
  465        * handlers.
  466        */
  467       public void parseMetaData(HashMap meta) {       
  468   	metaData = new MetaData(meta);
  469       }
  470   
  471       public void showSearchResults(String searchValueField) {
  472   	//entryTable.scrollTo(0);
  473   	if (searchValueField == DatabaseSearch.SEARCH)
  474   	    showingSearchResults = true;	           
  475   	else if (searchValueField == DatabaseSearch.GROUPSEARCH)
  476   	    showingGroup = true;
  477   	
  478   	entryTable.setShowingSearchResults(showingSearchResults,
  479   					   showingGroup);
  480   	entryTable.clearSelection();
  481   	entryTable.scrollTo(0);
  482   	refreshTable();
  483       }
  484   
  485       public void stopShowingSearchResults() {
  486   	showingSearchResults = false;
  487   	entryTable.setShowingSearchResults(showingSearchResults,
  488   					   showingGroup);
  489   	refreshTable();
  490       }
  491   
  492       public void stopShowingGroup() {
  493   	showingGroup = false;
  494   	entryTable.setShowingSearchResults(showingSearchResults,
  495   					   showingGroup);
  496   	refreshTable();
  497       }
  498   
  499       protected EntryTableModel getTableModel(){
  500   		return tableModel ; 
  501       }
  502   
  503       protected BibtexDatabase getDatabase(){
  504   		return database ; 
  505       }
  506   
  507       public void preambleEditorClosing() {
  508   	preambleEditor = null;
  509       }
  510   
  511       public void stringsClosing() {
  512   	stringDialog = null;
  513       }
  514   
  515       public void output(String s) {
  516   	statusLine.setText(s);
  517       }
  518   
  519       protected ParserResult loadDatabase(File fileToOpen) throws IOException {
  520   	// Temporary (old method):	
  521   	//FileLoader fl = new FileLoader();
  522   	//BibtexDatabase db = fl.load(fileToOpen.getPath());
  523   
  524          	BibtexParser bp = new BibtexParser(new FileReader(fileToOpen));	
  525   	ParserResult pr = bp.parse();
  526   	return pr;
  527       }
  528   
  529       // The action concerned with closing the window.
  530       CloseAction closeAction = new CloseAction(this);
  531       class CloseAction extends AbstractAction {
  532   	BibtexBaseFrame parent;
  533   	public CloseAction(BibtexBaseFrame parent_) {
  534   	    super("Close window");
  535   	    parent = parent_;
  536   	    putValue(SHORT_DESCRIPTION, "Close this window");
  537   	    //putValue(ACCELERATOR_KEY, GUIGlobals.closeKeyStroke);
  538   	}    
  539   	public void actionPerformed(ActionEvent e) {
  540   	    // Ask here if the user really wants to close, if the base
  541   	    // has not been saved since last save.
  542   	    boolean close = true;	    
  543   	    if (baseChanged) {
  544   		int answer = JOptionPane.showConfirmDialog(parent, "Database has changed. Do you want to save before closing?", "Save before closing", JOptionPane.YES_NO_CANCEL_OPTION);
  545   		if ((answer == JOptionPane.CANCEL_OPTION) || 
  546   		    (answer == JOptionPane.CLOSED_OPTION))
  547   		    close = false; // The user has cancelled.
  548   		if (answer == JOptionPane.YES_OPTION) {
  549   		    // The user wants to save.
  550   		    saveDatabaseAction.actionPerformed(null);
  551   		}
  552   	    }
  553   
  554   	    if (close) {
  555   		dispose();
  556   		if (--frameCount == 0) {
  557   		    // We save the window's size and location so the frame
  558   		    // will look the same next time. Also, if the program is
  559   		    // set to automatically open the latest database on 
  560   		    // startup, we save the currently active file.
  561   		    prefs.putInt("posX", parent.getLocation().x);
  562   		    prefs.putInt("posY", parent.getLocation().y);
  563   		    prefs.putInt("sizeX", parent.getSize().width);
  564   		    prefs.putInt("sizeY", parent.getSize().height);
  565   		    
  566   		    if (prefs.getBoolean("openLastEdited")) {
  567   			// Here we store the current file. If there is no current
  568   			// file, we remove any previously stored file name.
  569   			if ((database == null) || (file == null))
  570   			    prefs.remove("lastEdited");
  571   			else
  572   			    prefs.put("lastEdited", file.getPath());
  573   		    }
  574   		    System.exit(0); // All in order? End program.
  575   		} else {
  576   		    // There are other frames still open. We need to remove
  577   		    // this frame from the global HashMap.
  578   		    GUIGlobals.frames.remove(this);
  579   		}    
  580   	    }
  581   	}
  582       }
  583   
  584       // The action for closing the current database and leaving the window open.
  585       CloseDatabaseAction closeDatabaseAction = new CloseDatabaseAction(this);
  586       class CloseDatabaseAction extends AbstractAction {
  587   	BibtexBaseFrame parent;
  588   	public CloseDatabaseAction(BibtexBaseFrame parent_) {
  589   	    super("Close database"); 
  590   	    parent = parent_;
  591   	    putValue(SHORT_DESCRIPTION, "Close the current database");
  592   	}    
  593   	public void actionPerformed(ActionEvent e) {
  594   	    // Ask here if the user really wants to close, if the base
  595   	    // has not been saved since last save.
  596   	    boolean close = true;	    
  597   	    if (baseChanged) {
  598   		int answer = JOptionPane.showConfirmDialog(parent, "Database has changed. Do you want to save before closing?", "Save before closing", JOptionPane.YES_NO_CANCEL_OPTION);
  599   		if ((answer == JOptionPane.CANCEL_OPTION) || 
  600   		    (answer == JOptionPane.CLOSED_OPTION))
  601   		    close = false; // The user has cancelled.
  602   		if (answer == JOptionPane.YES_OPTION) {
  603   		    // The user wants to save.
  604   		    saveDatabaseAction.actionPerformed(null);
  605   		}
  606   	    }
  607   
  608   	    if (close) {
  609   		mainPanel.removeAll();
  610   		mainPanel.repaint();
  611   		database = null;
  612   		file = null;
  613   		baseChanged = false;
  614   		setEmptyState();
  615   		output("Closed database.");
  616   	    }
  617   	}
  618       }
  619   
  620   
  621   
  622       // The action concerned with opening an existing database.
  623       OpenDatabaseAction openDatabaseAction = new OpenDatabaseAction(this);
  624       class OpenDatabaseAction extends AbstractAction {
  625   	BibtexBaseFrame parent;
  626   	public OpenDatabaseAction(BibtexBaseFrame parent_) {
  627   	    super("Open database", 
  628   		  new ImageIcon(GUIGlobals.openIconFile));
  629   	    putValue(SHORT_DESCRIPTION, "Open BibTeX database");
  630   	    //putValue(MNEMONIC_KEY, GUIGlobals.openKeyCode);
  631   	    parent = parent_;
  632   	}    
  633   	public void actionPerformed(ActionEvent e) {
  634   	    // Open a new database.
  635   	    if ((e.getActionCommand() == null) || (e.getActionCommand().equals("Open database"))) {
  636   		JFileChooser chooser = (prefs.get("workingDirectory") == null) ?
  637   		    new JFileChooser((File)null) :
  638   		    new JFileChooser(new File(prefs.get("workingDirectory")));
  639   		chooser.setFileFilter(fileFilter);
  640   		int returnVal = chooser.showOpenDialog(parent);
  641   		if(returnVal == JFileChooser.APPROVE_OPTION) {
  642   		    fileToOpen = chooser.getSelectedFile();
  643   		}
  644   	    } else {
  645   		Util.pr(NAME);
  646   		Util.pr(e.getActionCommand());
  647   		fileToOpen = new File(Util.checkName(e.getActionCommand()));
  648   	    }
  649   	    openIt();
  650   	}
  651   	 
  652   	public void openIt() {
  653   	    if ((fileToOpen != null) && (fileToOpen.exists())) {
  654   		try {
  655   		    prefs.put("workingDirectory", fileToOpen.getPath());
  656   		    // Should this be done _after_ we know it was successfully opened?
  657   
  658   		    ParserResult pr = loadDatabase(fileToOpen);
  659   		    BibtexDatabase db = pr.getDatabase();
  660   		    HashMap meta = pr.getMetaData();
  661   
  662   		    // Put into this or a new window.
  663   		    if (database == null) {
  664   			// No database is yet open in this window, so we'll open
  665   			// it and just put it here.
  666   			file = fileToOpen; // Set the current file name pointer.
  667   			fileToOpen = null;
  668   			database = db;
  669   			parseMetaData(meta);
  670   			if (prefs.getBoolean("autoComplete")) {
  671   			    db.setCompleters(autoCompleters);
  672   			}
  673   			setupDatabaseLayout(); // Configure the frame.
  674   			output("Opened database '"+file.getPath()+"' with "+
  675   			       db.getEntryCount()+" entries.");
  676   		    } else {
  677   			// This window contains a database, so we'll have to create a new
  678   			// window for the database we open.
  679   			BibtexBaseFrame bbf = new BibtexBaseFrame
  680   			    (db, fileToOpen, meta);
  681   			bbf.output("Opened database '"+fileToOpen.getPath()+"'"
  682   				   +" with "+db.getEntryCount()+" entries.");
  683   			bbf.setVisible(true);
  684   			fileToOpen = null;
  685   		    }
  686   
  687   		} catch (Throwable ex) {
  688   		    JOptionPane.showMessageDialog
  689   			(parent, ex.getMessage(), 
  690   			 "Open database", JOptionPane.ERROR_MESSAGE);
  691   		    //output("Could not open: "+ex.getMessage());
  692   		    file = null;
  693   		}
  694   	    }
  695   	}
  696       }
  697   
  698       // The action concerned with opening a new database.
  699       NewDatabaseAction newDatabaseAction = new NewDatabaseAction();
  700       class NewDatabaseAction extends AbstractAction {
  701   	public NewDatabaseAction() {
  702   	    super("New database", 
  703   		  new ImageIcon(GUIGlobals.newIconFile));
  704   	    putValue(SHORT_DESCRIPTION, "New BibTeX database");
  705   	    //putValue(MNEMONIC_KEY, GUIGlobals.newKeyCode);	    
  706   	}    
  707   	public void actionPerformed(ActionEvent e) {
  708   
  709   	    // Create a new, empty, database.
  710   	    BibtexDatabase db = new BibtexDatabase();
  711   	    if (prefs.getBoolean("autoComplete"))
  712   		db.setCompleters(autoCompleters);
  713   
  714   	    // Put into this or a new window.
  715   	    if (database == null) {
  716   		// No database is yet open in this window, so we'll 
  717   		// just put it here.
  718   		database = db;
  719   		metaData = new MetaData();
  720   		setupDatabaseLayout(); // Configure the frame.
  721   		output("New database created.");
  722   	    } else {
  723   		// This window contains a database, so we'll have to create
  724   		// a new window for the new database.
  725   		BibtexBaseFrame bbf = new BibtexBaseFrame(db, new MetaData());
  726   		bbf.output("New database created.");
  727   		bbf.setVisible(true);
  728   	    }
  729   
  730   	}
  731       }
  732   
  733       // The action concerned with saving a database.
  734       SaveDatabaseAction saveDatabaseAction = new SaveDatabaseAction(this);   
  735       class SaveDatabaseAction extends AbstractAction {
  736   	BibtexBaseFrame parent;
  737   	public SaveDatabaseAction(BibtexBaseFrame parent_) {
  738   	    super("Save database", 
  739   		  new ImageIcon(GUIGlobals.saveIconFile));
  740   	    putValue(SHORT_DESCRIPTION, "Save current database");
  741   	    parent = parent_;
  742   	}    
  743   	public void actionPerformed(ActionEvent e) {
  744   
  745   	    if (file == null)
  746   		saveAsDatabaseAction.actionPerformed(null);
  747   	    else {
  748   		try {
  749   		    FileActions.saveDatabase(database, metaData, file,
  750   					     prefs, false, false);
  751   		    undoManager.markUnchanged();
  752   		    // (Only) after a successful save the following
  753   		    // statement marks that the base is unchanged
  754   		    // since last save:
  755   		    baseChanged = false;
  756   		    setTitle(GUIGlobals.baseTitle+file.getName());
  757   		    output("Saved database '"+file.getPath()+"'.");
  758   		} catch (SaveException ex) {
  759   		    if (ex.specificEntry()) {
  760   			// Error occured during processing of
  761   			// be. Highlight it:
  762   			int row = tableModel.getNumberFromName
  763   			    (ex.getEntry().getId()),
  764   			    topShow = Math.max(0, row-3);
  765   			//Util.pr(""+row);
  766   			entryTable.setRowSelectionInterval(row, row);   
  767   			entryTable.setColumnSelectionInterval
  768   			    (0, entryTable.getColumnCount()-1);
  769   			entryTable.scrollTo(topShow);
  770   		    }
  771   		    JOptionPane.showMessageDialog
  772   			(parent, "Could not save file.\n"+ex.getMessage(), 
  773   			 "Save database", JOptionPane.ERROR_MESSAGE);
  774   		}		
  775   	    }	
  776   	}
  777       }
  778   
  779       // The other action concerned with saving a database.
  780       SaveAsDatabaseAction saveAsDatabaseAction = new SaveAsDatabaseAction(this);
  781       class SaveAsDatabaseAction extends AbstractAction {
  782   	BibtexBaseFrame parent;
  783   	public SaveAsDatabaseAction(BibtexBaseFrame parent_) {
  784   	    super("Save database as ..", 
  785   		  new ImageIcon(GUIGlobals.saveAsIconFile));
  786   	    putValue(SHORT_DESCRIPTION, "Save current database");
  787   	    parent = parent_;
  788   	}    
  789   	public void actionPerformed(ActionEvent e) {
  790   
  791   	    JFileChooser chooser = new JFileChooser(prefs.get("workingDirectory"));
  792   	    chooser.setFileFilter(fileFilter);
  793   	    int returnVal = chooser.showSaveDialog(parent);
  794   	    if(returnVal == JFileChooser.APPROVE_OPTION) {
  795   		String name = chooser.getSelectedFile().getName(),
  796   		    path = chooser.getSelectedFile().getParent();
  797   		if (!name.endsWith(".bib"))
  798   		    name = name+".bib";
  799   		file = new File(path, name);
  800   		if (!file.exists() || (JOptionPane.showConfirmDialog(parent, "File '"+name+"' exists. Overwrite?", "Save database", JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION)) {
  801   		    saveDatabaseAction.actionPerformed(null);
  802   		    prefs.put("workingDirectory", path);
  803   		}
  804   		else
  805   		    file = null;
  806   	    } else {
  807   		// Cancelled.
  808   	    }
  809   	    
  810   
  811   	}
  812       }
  813   
  814       // The action for removing selected entries.
  815       RemoveEntryAction removeEntryAction = new RemoveEntryAction(this);
  816       class RemoveEntryAction extends AbstractAction {
  817   	BibtexBaseFrame parent;
  818   	public RemoveEntryAction(BibtexBaseFrame parent_) {
  819   	    super("Remove selected entries",  
  820   		  new ImageIcon(GUIGlobals.removeIconFile));
  821   	    parent = parent_;
  822   	    putValue(SHORT_DESCRIPTION, "Remove selected entries");	
  823   	}    
  824   	public void actionPerformed(ActionEvent e) {
  825   	    int[] 
  826   		rows = entryTable.getSelectedRows(),
  827   		cols = entryTable.getSelectedColumns();
  828   
  829   	    // Only works if the first column, or all columns, is selected.
  830   	    if ((((cols.length == 1) && (cols[0] == 0)) 
  831   		 || (cols.length == tableModel.getColumnCount()))
  832   		&& (rows.length > 0)) {
  833   		//&& (database.getEntryCount() > 0) && (entryTable.getSelectedRow() < database.getEntryCount())) {
  834   
  835   		String msg = "Really delete the selected entry?",
  836   		    title = "Delete entry";
  837   		if (rows.length > 1) {
  838   		    msg = "Really delete the selected "+rows.length+" entries?";
  839   		    title = "Delete multiple entries";
  840   		}
  841   		int answer = JOptionPane.showConfirmDialog(parent, msg, title, JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE);
  842   		if (answer == JOptionPane.YES_OPTION) {
  843   		    // Create a CompoundEdit to make the action undoable.
  844   		    NamedCompound ce = new NamedCompound
  845   			(rows.length > 1 ? "delete entries" : "delete entry");
  846   		    // Loop through the array of entries, and delete them.
  847   		    for (int i=0; i<rows.length; i++) {
  848   			String id = tableModel.getNameFromNumber(rows[i]);
  849   			BibtexEntry entry = database.getEntryByID(id);
  850   			database.removeEntry(id);
  851   			Object o = entryTypeForms.get(id);
  852   			if (o != null) {
  853   			    ((EntryTypeForm)o).dispose();
  854   			}
  855   			ce.addEdit(new UndoableRemoveEntry(database, entry,
  856   							   entryTypeForms));
  857   		    }
  858   		    entryTable.clearSelection();
  859   		    output("Deleted "+(rows.length>1 ? rows.length+" entries" : "entry")+".");
  860   		    ce.end();
  861   		    undoManager.addEdit(ce);		    
  862   		    refreshTable();
  863   		    markBaseChanged();
  864   		}
  865   	    }
  866   	}
  867   
  868   	};
  869   
  870       // The action for adding a new entry of unspecified type.
  871       NewEntryAction newEntryAction = new NewEntryAction(this);
  872       NewEntryAction[] newSpecificEntryAction = new NewEntryAction[] {
  873   	new NewEntryAction(this, BibtexEntryType.ARTICLE,
  874   			   GUIGlobals.newArticleKeyStroke),
  875   	new NewEntryAction(this, BibtexEntryType.BOOK,
  876   			   GUIGlobals.newBookKeyStroke),
  877   	new NewEntryAction(this, BibtexEntryType.PHDTHESIS, 
  878   			   GUIGlobals.newPhdthesisKeyStroke),
  879   	new NewEntryAction(this, BibtexEntryType.INBOOK, 
  880   			   GUIGlobals.newInBookKeyStroke),
  881   	new NewEntryAction(this, BibtexEntryType.MASTERSTHESIS, 
  882   			   GUIGlobals.newMasterKeyStroke),
  883   	new NewEntryAction(this, BibtexEntryType.PROCEEDINGS, 
  884   			   GUIGlobals.newProcKeyStroke),
  885   	new NewEntryAction(this, BibtexEntryType.INPROCEEDINGS),
  886   	new NewEntryAction(this, BibtexEntryType.INCOLLECTION),
  887   	new NewEntryAction(this, BibtexEntryType.BOOKLET), 
  888   	new NewEntryAction(this, BibtexEntryType.MANUAL),
  889   	new NewEntryAction(this, BibtexEntryType.TECHREPORT),
  890   	new NewEntryAction(this, BibtexEntryType.UNPUBLISHED, 
  891   			   GUIGlobals.newUnpublKeyStroke),
  892   	new NewEntryAction(this, BibtexEntryType.MISC) 
  893   
  894       };
  895       class NewEntryAction extends AbstractAction {
  896   
  897   	BibtexBaseFrame parent = null;
  898   	BibtexEntryType type = null; // The type of item to create.
  899   	KeyStroke keyStroke = null;  // Used for the specific instances.
  900   
  901   	public NewEntryAction(BibtexBaseFrame parent_) {
  902   	    // This action leads to a dialog asking for entry type.
  903   	    super("New entry",  
  904   		  new ImageIcon(GUIGlobals.addIconFile));
  905   	    putValue(SHORT_DESCRIPTION, "New BibTeX entry");
  906   	    //putValue(MNEMONIC_KEY, GUIGlobals.newEntryKeyCode);
  907   	    parent = parent_;
  908   	}    
  909   
  910   	public NewEntryAction(BibtexBaseFrame parent_, BibtexEntryType type_) { 
  911   	    // This action leads to the creation of a specific entry type.
  912   	    // No shortcut key is set for this action.
  913   	    super(type_.getName());
  914   	    type = type_;
  915   	    parent = parent_;
  916   	}    
  917   
  918   	public NewEntryAction(BibtexBaseFrame parent_, BibtexEntryType type_, 
  919   			      KeyStroke keyStroke_) {
  920   	    // This action leads to the creation of a specific entry type.
  921   	    super(type_.getName());
  922   	    type = type_;
  923   	    parent = parent_;
  924   	    keyStroke = keyStroke_;
  925   	    //putValue(MNEMONIC_KEY, keyCode);
  926   	}    
  927   
  928   	public void actionPerformed(ActionEvent e) {
  929   	    if (database == null)
  930   		throw new NullPointerException("Can't create new entry without an open database.");
  931   	    BibtexEntryType newType = type;
  932   	    if (newType == null) {
  933   		// Find out what type is wanted.
  934   		EntryTypeDialog etd = new EntryTypeDialog(parent);	       
  935   		// We want to center the dialog, to make it look nicer.
  936   		Util.placeDialog(etd, parent);
  937   		etd.setVisible(true);
  938   		newType = etd.getChoice();
  939   	    }
  940   	    if (newType != null) { // Only if the dialog was not cancelled.
  941   		String id = Util.createID(newType, database);
  942   		BibtexEntry be = new BibtexEntry(id, newType);
  943   		try {
  944   		    database.insertEntry(be);
  945   		    
  946   		    // Create an UndoableInsertEntry object.
  947   		    undoManager.addEdit(new UndoableInsertEntry(database, be, 
  948   								entryTypeForms));							       
  949   		    output("Added new "+newType.getName().toLowerCase()+" entry.");
  950   		    refreshTable();
  951   		    markBaseChanged(); // The database just changed.
  952   		    if (prefs.getBoolean("autoOpenForm")) {
  953   			EntryTypeForm etf = new EntryTypeForm(parent, be, prefs);
  954   			Util.placeDialog(etf, parent);
  955   			etf.setVisible(true);
  956   			entryTypeForms.put(id, etf);
  957   		    }
  958   		} catch (KeyCollisionException ex) {
  959   		    Util.pr(ex.getMessage());
  960   		}
  961   	    }
  962   	    
  963   	}
  964       }
  965   
  966       // The action for copying selected entries.
  967       SelectAllAction selectAllAction = new SelectAllAction(this);
  968       class SelectAllAction extends AbstractAction {
  969           BibtexBaseFrame parent;
  970           public SelectAllAction(BibtexBaseFrame parent_) {
  971               super("Select All", null);
  972               putValue(SHORT_DESCRIPTION, "SelectAll");
  973               parent = parent_;
  974           }
  975   
  976           public void actionPerformed(ActionEvent e) {
  977   			entryTable.selectAll() ; 	
  978           }
  979   
  980       }
  981       
  982   
  983       // The action for copying selected entries.
  984       CopyAction copyAction = new CopyAction(this);
  985       class CopyAction extends AbstractAction {
  986   	BibtexBaseFrame parent;
  987   	public CopyAction(BibtexBaseFrame parent_) {
  988   	    super("Copy",
  989   		  new ImageIcon(GUIGlobals.copyIconFile));
  990   	    putValue(SHORT_DESCRIPTION, "Copy");
  991   	    parent = parent_;
  992   	}
  993      
  994   	public void actionPerformed(ActionEvent e) {
  995   	    BibtexEntry[] bes = entryTable.getSelectedEntries();
  996   
  997   	    // Entries are copied if only the first or multiple
  998   	    // columns are selected.
  999   	    if ((bes != null) && (bes.length > 0)) {
 1000   		TransferableBibtexEntry trbe = new TransferableBibtexEntry(bes);
 1001   		Toolkit.getDefaultToolkit().getSystemClipboard().setContents(trbe, parent);
 1002   		output("Copied "+(bes.length>1 ? bes.length+" entries." : "1 entry."));
 1003   	    } else {
 1004   		// The user maybe selected a single cell.
 1005   		int[] rows = entryTable.getSelectedRows(),
 1006   		    cols = entryTable.getSelectedColumns();
 1007   		if ((cols.length == 1) && (rows.length == 1)) {
 1008   		    // Copy single value.
 1009   		    Object o = tableModel.getValueAt(rows[0], cols[0]);
 1010   		    if (o != null) {
 1011   			StringSelection ss = new StringSelection(o.toString());
 1012   			Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss, parent);
 1013   			output("Copied cell contents.");
 1014   		    }
 1015   		} /*else 
 1016   		    output("Copy multiple cell values: not yet supported.");*/
 1017   	    }
 1018   	}
 1019       }
 1020   
 1021   
 1022       // Run through the entire list, making sure that each of the 
 1023       // selected Bibtexkeys is unique
 1024       CheckUniqueLabelAction checkUniqueLabelAction = new CheckUniqueLabelAction(this);
 1025       class CheckUniqueLabelAction extends AbstractAction {
 1026           BibtexBaseFrame parent;
 1027           public CheckUniqueLabelAction(BibtexBaseFrame parent_) {
 1028               super("Remove duplicate bibtex keys", null);
 1029               parent = parent_ ;
 1030               putValue(SHORT_DESCRIPTION, "Remove duplicate bibtex keys");
 1031           }
 1032   
 1033   	public void actionPerformed(ActionEvent e) {
 1034   	    int[] rows = entryTable.getSelectedRows() ;
 1035   
 1036   	    // If one or none are selected, we do as if all were selected:
 1037   	    if (rows.length < 2) {
 1038   		rows = new int[database.getEntryCount()];
 1039   		for (int i=0; i<database.getEntryCount(); i++)
 1040   		    rows[i] = i;
 1041   	    }
 1042   
 1043   	    int numSelected = rows.length ; 
 1044   
 1045   //            JOptionPane.showMessageDialog( parent, "Trying to check unique labels","Run it",JOptionPane.INFORMATION_MESSAGE) ; 
 1046   		
 1047               Hashtable uniqueTable = new Hashtable() ; 
 1048               Hashtable ignoreTable = new Hashtable() ; 
 1049               BibtexEntry bes = null ; 
 1050               String bibtexKey = null ; 
 1051               boolean finish = false ; 
 1052   	    NamedCompound ce;
 1053   
 1054               for(int i = 0 ; i < numSelected  && !finish ; i++){
 1055                  bes = database.getEntryByID(tableModel.getNameFromNumber(rows[i]));
 1056                  bibtexKey = (String) bes.getField(KEY_PROPERTY) ; 
 1057                   // do compare
 1058                  if( uniqueTable.containsKey(bibtexKey) ){
 1059                      Object[] options = { "stop search","ignore","fix"} ; 
 1060                   int answer = JOptionPane.showOptionDialog
 1061   		    (parent, "Not a unique key: "+bibtexKey, 
 1062   		     "Make unique",
 1063   		     JOptionPane.YES_NO_CANCEL_OPTION, 
 1064   		     JOptionPane.QUESTION_MESSAGE,null, options,options[2]);
 1065   		// " FIX or DELETE, IGNORE, STOP_SEARCH?"
 1066   
 1067   
 1068                       // if match, query=delete,ignore,ignoreall,fix
 1069                       // fix entry
 1070   //                    if ((answer == JOptionPane.CLOSED_OPTION)) {
 1071                       if (answer == 2) {
 1072   			// do label stuff here
 1073    
 1074   			bes = database.getEntryByID
 1075   			    (tableModel.getNameFromNumber(rows[i]));    
 1076   			// append the label, test and re-insert
 1077   			while( uniqueTable.containsKey(bibtexKey) ){
 1078   			    //                                 if(bibtexKey.indexOf(":")<0){
 1079   			    //                                     bibtexKey += ":" ; 
 1080   			    //                                 }
 1081   			    bibtexKey += "i" ; 
 1082   			}
 1083   			Object oldValue = bes.getField(GUIGlobals.KEY_FIELD);
 1084   			bes.setField(GUIGlobals.KEY_FIELD,bibtexKey) ;
 1085   			
 1086   			// Store undo info:
 1087   			ce = new NamedCompound("key change");
 1088   			ce.addEdit(new UndoableFieldChange
 1089   				   (bes, GUIGlobals.KEY_FIELD, oldValue,
 1090   				    bibtexKey));
 1091   			ce.end();
 1092   			undoManager.addEdit(ce);
 1093   			
 1094   			markBaseChanged();
 1095   			refreshTable();
 1096                       } // end of if actions from dialog
 1097                       /*else
 1098                       // delete entry, deletes the duplicate entry
 1099                       if ((answer == JOptionPane.CANCEL_OPTION)) {
 1100                           bes = database.removeEntry(tableModel.getNameFromNumber(rows[i]));
 1101   			}*/
 1102                       else
 1103                       // skip it
 1104                       if ((answer == JOptionPane.NO_OPTION)) {
 1105                            // do nothing
 1106                       }
 1107   //                    else
 1108   //                    if ((answer == JOptionPane.YES_OPTION)) {
 1109   //                        // stop processing
 1110   //                        finish = true ; 
 1111   //                    }
 1112                       else
 1113                       {
 1114                           finish = true ; 
 1115                       }
 1116   
 1117              } // end of if a match
 1118   
 1119               uniqueTable.put(bibtexKey,bibtexKey) ; 
 1120   
 1121   
 1122           } // end of for loop
 1123   
 1124   //            markBaseChanged() ; 
 1125   //            refreshTable() ; 
 1126               JOptionPane.showMessageDialog( parent, "Finished search","Finished",JOptionPane.INFORMATION_MESSAGE) ; 
 1127           } // end of action performed
 1128       }  // end of class
 1129   
 1130   
 1131   
 1132   
 1133       // The action for auto-generating labels.
 1134       MakeLabelAction makeLabelAction = new MakeLabelAction(this);
 1135       class MakeLabelAction extends AbstractAction {
 1136           BibtexBaseFrame parent;
 1137           public MakeLabelAction(BibtexBaseFrame parent_) {
 1138               super("Autogenerate bibtex keys", new ImageIcon(GUIGlobals.genKeyIconFile));
 1139               parent = parent_ ;
 1140               putValue(SHORT_DESCRIPTION, "Autogenerate bibtex keys");
 1141           }
 1142   
 1143   		public void actionPerformed(ActionEvent e) {
 1144               int[] rows = entryTable.getSelectedRows() ;
 1145   			int numSelected = rows.length ; 
 1146               BibtexEntry bes = null ;
 1147   	    if (numSelected > 0) {
 1148   		int answer = JOptionPane.showConfirmDialog
 1149   		    (parent, "Generate bibtex key"+
 1150   		     (numSelected>1 ? "s for the selected "+numSelected+" entries?" :
 1151   							    " for the selected entry?"), "Autogenerate Bibtexkey", 
 1152   							   JOptionPane.YES_NO_CANCEL_OPTION);
 1153   		if (answer != JOptionPane.YES_OPTION) {
 1154   		    return ; 
 1155   		}
 1156               } else { // None selected. Inform the user to select entries first.
 1157   		JOptionPane.showMessageDialog(parent, "First select the entries you want keys to be generated for.",
 1158   					      "Autogenerate Bibtexkey", JOptionPane.INFORMATION_MESSAGE);
 1159   		return ;
 1160   	    }
 1161   
 1162   	    output("Generating Bibtexkey for "+numSelected+(numSelected>1 ? " entries" : "entry"));
 1163   
 1164   	    NamedCompound ce = new NamedCompound("autogenerate keys");
 1165   	    //BibtexEntry be;
 1166   	    Object oldValue;
 1167               for(int i = 0 ; i < numSelected ; i++){
 1168   		bes = database.getEntryByID(tableModel.getNameFromNumber(rows[i]));
 1169   		oldValue = bes.getField(GUIGlobals.KEY_FIELD);
 1170   		bes = labelMaker.applyRule(bes) ; 
 1171   		ce.addEdit(new UndoableFieldChange
 1172   			   (bes, GUIGlobals.KEY_FIELD, oldValue,
 1173   			    bes.getField(GUIGlobals.KEY_FIELD)));
 1174               }
 1175   	    ce.end();
 1176   	    undoManager.addEdit(ce);
 1177               markBaseChanged() ; 
 1178               refreshTable() ; 
 1179           }
 1180       }
 1181   
 1182       // The action for reading another database and adding its contents into the currently
 1183       // open one.
 1184       MergeDatabaseAction mergeDatabaseAction = new MergeDatabaseAction(this);
 1185       class MergeDatabaseAction extends AbstractAction {
 1186           BibtexBaseFrame parent;
 1187           public MergeDatabaseAction(BibtexBaseFrame parent_) {
 1188               super("Merge with database...", null);
 1189               parent = parent_ ;
 1190               putValue(SHORT_DESCRIPTION, "Import the contents of another database");
 1191           }
 1192   	public void actionPerformed(ActionEvent e) {
 1193   	    JFileChooser chooser = (prefs.get("workingDirectory") == null) ?
 1194   		new JFileChooser((File)null) :
 1195   		new JFileChooser(new File(prefs.get("workingDirectory")));
 1196   	    chooser.setDialogTitle("Merge with database");
 1197   	    chooser.setFileFilter(fileFilter);
 1198   	    int returnVal = chooser.showOpenDialog(parent);
 1199   	    if(returnVal == JFileChooser.APPROVE_OPTION) {
 1200   		fileToOpen = chooser.getSelectedFile();
 1201   		if ((fileToOpen != null) && (fileToOpen.exists())) {
 1202   		    try {
 1203   			//prefs.put("workingDirectory", fileToOpen.getPath());
 1204   			// I think it is reasonable not to update the working directory
 1205   			// here, since the current database is the same.
 1206   
 1207   			ParserResult pr = loadDatabase(fileToOpen);
 1208   			BibtexDatabase db = pr.getDatabase();
 1209   			//HashMap metaData = pr.getMetaData();
 1210   			// We ignore metadata, and keep the ones we have.
 1211   
 1212   			// Database has been successfully opened, and
 1213   			// now we insert its contents. Entries are OK,
 1214   			// just need to avoid key collisions.  Strings
 1215   			// must be put in the right order, and equal
 1216   			// ones must be resolved. The preambles are
 1217   			// appended, but this might require user
 1218   			// supervision.
 1219   		
 1220   			boolean ignoredString = false;
 1221   			String message = "Merged with database: ";
 1222   			MergeDialog md = new MergeDialog(parent);
 1223   		        Util.placeDialog(md, parent);
 1224   			md.setVisible(true);			
 1225   			if (md.ok()) {
 1226   			    NamedCompound ce = new NamedCompound("merge");
 1227   			    // Preamble:
 1228   			    if (md.mergePreamble()) {				
 1229   				String s = db.getPreamble(),
 1230   				    oldValue = database.getPreamble();
 1231   				if (s != null) {
 1232   				    if (database.getPreamble() == null)
 1233   					database.setPreamble(s);
 1234   				    else
 1235   					database.setPreamble(database.getPreamble()
 1236   							     +"\n"
 1237   							     +s);
 1238   				    updatePreamble();
 1239   
 1240   				    // Store undo info:
 1241   				    ce.addEdit(new UndoablePreambleChange
 1242   					       (database, parent, oldValue, database.getPreamble()));
 1243   				}
 1244   			    }
 1245   			    // Strings:
 1246   			    if (md.mergeStrings()) {
 1247   				int count = 0;
 1248   				for (int i=0; i<db.getStringCount(); i++) {
 1249   				    BibtexString bs = db.getString(i);
 1250   				    if (!database.hasStringLabel(bs.getName()))
 1251   					try {
 1252   					    int pos = database.getStringCount();
 1253   					    database.addString(bs, pos);
 1254   					    count++;
 1255   					    // Store undo info:
 1256   					    ce.addEdit(new UndoableInsertString
 1257   						       (parent, database, bs, pos));
 1258   					    
 1259   					} catch (KeyCollisionException ex) {}
 1260   				    else
 1261   					ignoredString = true; // A string had to be skipped.
 1262   				}
 1263   				message = message 
 1264   				    +(count == 0 ? "0 strings" :
 1265   				      count+" string"+(count > 1 ? "s" : ""));
 1266   
 1267   			    }
 1268   
 1269   			    // Entries:
 1270   			    if (md.mergeEntries()) {
 1271   				for (Iterator i=db.getKeySet().iterator(); i.hasNext();) {
 1272   				    try {
 1273   					BibtexEntry be = db.getEntryByID((String)(i.next()));
 1274   					be = (BibtexEntry)(be.clone());
 1275   					// Change the ID to ensure that there is no key collision.
 1276   					be.setId(Util.createID(be.getType(), database));
 1277   					database.insertEntry(be);
 1278   
 1279   					// Store undo info:
 1280   					ce.addEdit(new UndoableInsertEntry
 1281   						   (database, be, entryTypeForms));
 1282   
 1283   				    } catch (KeyCollisionException exx) {}
 1284   				}
 1285   				message = message 
 1286   				    +(md.mergeStrings() ? " and " : "")
 1287   				    +db.getEntryCount()+" entr"
 1288   				    +(((db.getEntryCount() > 1) 
 1289   				       || (db.getEntryCount() == 0)) ? "ies" : "y");
 1290   			    }
 1291   
 1292   			    // Finish undo information:
 1293   			    ce.end();
 1294   			    undoManager.addEdit(ce);
 1295   
 1296   			    if (ignoredString)
 1297   				JOptionPane.showMessageDialog
 1298   				    (parent, "At least one string could not be imported "
 1299   				     +"because its name was already used",
 1300   				     "Could not import all strings",
 1301   				     JOptionPane.WARNING_MESSAGE);
 1302   			    output(message+".");
 1303   			    markBaseChanged() ; 
 1304   			    refreshTable() ; 
 1305   			}
 1306   	    
 1307   		    } catch (IOException ex) {
 1308   			ex.printStackTrace();
 1309   			JOptionPane.showMessageDialog
 1310   			    (parent, ex.getMessage(), 
 1311   			     "Open database", JOptionPane.ERROR_MESSAGE);
 1312   
 1313   			//output("Could not open: "+ex.getMessage());
 1314   		    }		    
 1315   		}
 1316   	    }
 1317           }
 1318       }
 1319   
 1320   
 1321       // The action for pasting entries.
 1322       PasteAction pasteAction = new PasteAction();
 1323       class PasteAction extends AbstractAction {
 1324   	public PasteAction() {
 1325   	    super("Paste",
 1326   		  new ImageIcon(GUIGlobals.pasteIconFile));
 1327   	    putValue(SHORT_DESCRIPTION, "Paste");
 1328   	}
 1329      
 1330   	public void actionPerformed(ActionEvent e) {
 1331   	    // We pick an object from the clipboard, check if it exists, and if it is a set of entries.
 1332   	    Transferable content = Toolkit.getDefaultToolkit().getSystemClipboard().getContents(null);
 1333   	    if (content != null) {
 1334   		DataFlavor[] flavor = content.getTransferDataFlavors();
 1335   		if ((flavor != null) && (flavor.length > 0) && flavor[0].equals(TransferableBibtexEntry.entryFlavor)) {
 1336   		    // We have determined that the clipboard data is a set of entries.
 1337   		    BibtexEntry[] bes = null;
 1338   		    try {
 1339   			bes = (BibtexEntry[])(content.getTransferData(TransferableBibtexEntry.entryFlavor));
 1340   		    } catch (UnsupportedFlavorException ex) {
 1341   		    } catch (IOException ex) {}
 1342   
 1343   		    if ((bes != null) && (bes.length > 0)) {
 1344   			NamedCompound ce = new NamedCompound
 1345   			    (bes.length > 1 ? "paste entries" : "paste entry");
 1346   			for (int i=0; i<bes.length; i++) {
 1347   			    try { 
 1348   				BibtexEntry be = (BibtexEntry)(bes[i].clone());
 1349   				// We have to clone the entries, since the pasted
 1350   				// entries must exist independently of the copied
 1351   				// ones.
 1352   				be.setId(Util.createID(be.getType(), database));
 1353   				database.insertEntry(be);
 1354   				ce.addEdit(new UndoableInsertEntry
 1355   					   (database, be, entryTypeForms));
 1356   			    } catch (KeyCollisionException ex) {
 1357   				Util.pr("KeyCollisionException... this shouldn't happen.");
 1358   			    }
 1359   			}
 1360   			ce.end();
 1361   			undoManager.addEdit(ce);
 1362   			tableModel.remap();
 1363   			entryTable.clearSelection();
 1364   			entryTable.revalidate();
 1365   			output("Pasted "+(bes.length>1 ? bes.length+" entries." : "1 entry."));
 1366   			refreshTable();
 1367   			markBaseChanged();
 1368   		    }
 1369   		}
 1370   		if ((flavor != null) && (flavor.length > 0) && flavor[0].equals(DataFlavor.stringFlavor)) { 
 1371   		    // We have determined that the clipboard data is a string.
 1372   		    int[] rows = entryTable.getSelectedRows(),
 1373   			cols = entryTable.getSelectedColumns();
 1374   		    if ((cols != null) && (cols.length == 1) && (cols[0] != 0)
 1375   			&& (rows != null) && (rows.length == 1)) {
 1376   			try {
 1377   			    tableModel.setValueAt((String)(content.getTransferData(DataFlavor.stringFlavor)), rows[0], cols[0]);
 1378   			    refreshTable();
 1379   			    markBaseChanged();			   
 1380   			    output("Pasted cell contents");
 1381   			} catch (UnsupportedFlavorException ex) {
 1382   			} catch (IOException ex) {
 1383   			} catch (IllegalArgumentException ex) {
 1384   			    output("Can't paste.");
 1385   			}
 1386   		    }
 1387   		}
 1388   	    }
 1389   	}
 1390       }
 1391   
 1392       // The action concerned with copying a BibTeX key to the clipboard.
 1393       CopyKeyAction copyKeyAction = new CopyKeyAction();
 1394       class CopyKeyAction extends AbstractAction {
 1395   	public CopyKeyAction() {
 1396   	    super("Copy BibTeX key",
 1397   		  //  new ImageIcon(GUIGlobals.pre + "copyKey2.gif"));
 1398   		  new ImageIcon(GUIGlobals.copyKeyIconFile));
 1399   	    putValue(SHORT_DESCRIPTION, "Copy BibTeX key of the selected entry to clipboard");
 1400   	    //putValue(ACCELERATOR_KEY, GUIGlobals.copyKeyStroke);
 1401   	    //putValue(MNEMONIC_KEY, GUIGlobals.copyKeyCode);
 1402   	}
 1403      
 1404   	public void actionPerformed(ActionEvent e) {
 1405   	    int clickedOn = entryTable.getSelectedRow();
 1406   	    if (clickedOn >= 0) {
 1407   		String id =  tableModel.getNameFromNumber(clickedOn);
 1408   		BibtexEntry be = database.getEntryByID(id);
 1409   		//Util.pr(be.getId());
 1410   		String s = (String)(be.getField(KEY_PROPERTY));
 1411   		if (s != null) {
 1412   		    StringSelection ss = new StringSelection(s);
 1413   		    Toolkit.getDefaultToolkit().getSystemClipboard().setContents(ss,ss);
 1414   		    output("Copied BibTeX key: '"+s+"'.");
 1415   		} else {
 1416   		    output("Entry has no BibTeX key!");
 1417   		}
 1418   	    }	
 1419   	}
 1420       }
 1421   
 1422   
 1423       // The action for searching columns for info using regular expressions.
 1424       SearchPaneAction searchPaneAction = new SearchPaneAction(this);
 1425       class SearchPaneAction extends AbstractAction {
 1426           BibtexBaseFrame parent;
 1427   	//        SearchPane searchDialog = null ; 
 1428           public SearchPaneAction(BibtexBaseFrame parent_) {
 1429               super("Search", 
 1430                 new ImageIcon(GUIGlobals.searchIconFile));
 1431               putValue(SHORT_DESCRIPTION, "Search");
 1432               parent = parent_;
 1433           }    
 1434           public void actionPerformed(ActionEvent e) {
 1435               if(searchDialog==null){
 1436                   searchDialog = new SearchPane(parent,prefs,"SearchPane",false) ; 
 1437   		//Util.placeDialog(searchDialog, parent);
 1438               }else{
 1439                   searchDialog.show() ; 
 1440               }
 1441           }
 1442       }
 1443   
 1444   
 1445   
 1446       // The action for opening the preferences dialog.
 1447       SetupTableAction setupTableAction = new SetupTableAction(this);
 1448       class SetupTableAction extends AbstractAction {
 1449   	BibtexBaseFrame parent;
 1450   	public SetupTableAction(BibtexBaseFrame parent_) {
 1451   	    super("Preferences", 
 1452   		  new ImageIcon(GUIGlobals.prefsIconFile));
 1453   	    putValue(SHORT_DESCRIPTION, "Preferences");
 1454   	    parent = parent_;
 1455   	}    
 1456   	public void actionPerformed(ActionEvent e) {
 1457   		PrefsDialog.showPrefsDialog(parent, prefs);
 1458   		// This action can be invoked without an open database, so
 1459   		// we have to check if we have one before trying to invoke
 1460   		// methods to execute changes in the preferences.
 1461   
 1462   		// We want to notify all frames about the changes to 
 1463   		// avoid problems when changing the column set.
 1464   		java.util.Iterator i = GUIGlobals.frames.keySet().iterator();
 1465   		for (; i.hasNext();) {
 1466   		    BibtexBaseFrame bf = (BibtexBaseFrame)
 1467   			(GUIGlobals.frames.get(i.next()));
 1468   		    if (bf.database != null) {
 1469   			bf.setupMainPanel();
 1470   		    }
 1471   		}
 1472   	}
 1473       }
 1474   
 1475   
 1476       EditEntryAction editEntryAction = new EditEntryAction(this);
 1477       class EditEntryAction extends AbstractAction {
 1478   	BibtexBaseFrame parent;
 1479   	public EditEntryAction(BibtexBaseFrame parent_) {
 1480   	    super("Edit selected entry", new ImageIcon(GUIGlobals.editEntryIconFile));
 1481   	    putValue(SHORT_DESCRIPTION, "Edit selected entry");
 1482   	    parent = parent_;
 1483   	}    
 1484   	public void actionPerformed(ActionEvent e) {
 1485   	    int clickedOn = -1;
 1486   	    // We demand that one and only one row is selected.
 1487   	    if (entryTable.getSelectedRowCount() == 1) {
 1488   		clickedOn = entryTable.getSelectedRow();		
 1489   	    }
 1490   	    if (clickedOn >= 0) {
 1491   		String id =  tableModel.getNameFromNumber(clickedOn);
 1492   		
 1493   		// First we check that no editor is already open for this
 1494   		// entry.
 1495   		if (!entryTypeForms.containsKey(id)) {
 1496   		    BibtexEntry be = database.getEntryByID(id);
 1497   		    EntryTypeForm form = new EntryTypeForm(parent, be, prefs);
 1498   		    Util.placeDialog(form, parent); // We want to center the editor.
 1499   		    form.setVisible(true);
 1500   		    entryTypeForms.put(id, form);
 1501   		} else {
 1502   		    ((EntryTypeForm)(entryTypeForms.get(id))).setVisible(true);
 1503   		}
 1504   	    }
 1505   	}
 1506       }
 1507   
 1508       EditPreambleAction editPreambleAction = new EditPreambleAction(this);
 1509       class EditPreambleAction extends AbstractAction {
 1510   	BibtexBaseFrame parent;
 1511   	public EditPreambleAction(BibtexBaseFrame parent_) {
 1512   	    super("Edit preamble", new ImageIcon(GUIGlobals.preambleIconFile));
 1513   	    putValue(SHORT_DESCRIPTION, "Edit preamble");
 1514   	    parent = parent_;
 1515   	}    
 1516   	public void actionPerformed(ActionEvent e) {
 1517   	    if (preambleEditor == null) {
 1518   		PreambleEditor form = new PreambleEditor(parent, database, prefs);
 1519   		Util.placeDialog(form, parent);
 1520   		form.setVisible(true);
 1521   		preambleEditor = form;
 1522   	    } else {
 1523   		preambleEditor.setVisible(true);
 1524   	    }
 1525   	    
 1526   	}
 1527       }
 1528   
 1529       EditStringsAction editStringsAction = new EditStringsAction(this);
 1530       class EditStringsAction extends AbstractAction {
 1531   	BibtexBaseFrame parent;
 1532   	public EditStringsAction(BibtexBaseFrame parent_) {
 1533   	    super("Edit strings", new ImageIcon(GUIGlobals.stringsIconFile));
 1534   	    putValue(SHORT_DESCRIPTION, "Edit strings");
 1535   	    parent = parent_;
 1536   	}    
 1537   	public void actionPerformed(ActionEvent e) {
 1538   	    if (stringDialog == null) {
 1539   		StringDialog form = new StringDialog(parent, database, prefs);
 1540   		form.setVisible(true);
 1541   		stringDialog = form;
 1542   	    } else {
 1543   		stringDialog.setVisible(true);
 1544   	    }
 1545   	    
 1546   	}
 1547       }
 1548   
 1549       UndoAction undoAction = new UndoAction(this);
 1550       class UndoAction extends AbstractAction {
 1551   	BibtexBaseFrame parent;
 1552   	public UndoAction(BibtexBaseFrame parent_) {
 1553   	    super(undoManager.getUndoPresentationName(),
 1554   		  new ImageIcon(GUIGlobals.undoIconFile));
 1555   	    //putValue(SHORT_DESCRIPTION, "Undo action");
 1556   	    parent = parent_;
 1557   	    //setEnabled(false);
 1558   	}    
 1559   	public void actionPerformed(ActionEvent e) {
 1560   	    try {
 1561   		String name = undoManager.getUndoPresentationName();
 1562   		undoManager.undo();
 1563   		markBaseChanged();
 1564   		refreshTable();
 1565   		output(name);
 1566   	    } catch (CannotUndoException ex) {
 1567   		output("Nothing to undo.");
 1568   		/*JOptionPane.showMessageDialog
 1569   		    (parent, "Error: cannot undo!", "Cannot undo",
 1570   		    JOptionPane.ERROR_MESSAGE);*/
 1571   	    }
 1572   	    // After everything, enable/disable the undo/redo actions
 1573   	    // appropriately.
 1574   	    updateUndoState();
 1575   	    redoAction.updateRedoState();
 1576   	    markChangedOrUnChanged();
 1577   	}
 1578   
 1579   	public void updateUndoState() {
 1580   	    //setEnabled(undoManager.canUndo());
 1581   	    putValue(AbstractAction.NAME, 
 1582   		     undoManager.getUndoPresentationName());
 1583   	}
 1584       }
 1585   
 1586       RedoAction redoAction = new RedoAction(this);
 1587       class RedoAction extends AbstractAction {
 1588   	BibtexBaseFrame parent;
 1589   	public RedoAction(BibtexBaseFrame parent_) {
 1590   	    super(undoManager.getRedoPresentationName(), 
 1591   		  new ImageIcon(GUIGlobals.redoIconFile));
 1592   	    //putValue(SHORT_DESCRIPTION, "Undo action");
 1593   	    parent = parent_;
 1594   	    //setEnabled(false);
 1595   	}    
 1596   	public void actionPerformed(ActionEvent e) {
 1597   	    try {
 1598   		String name = undoManager.getRedoPresentationName();
 1599   		undoManager.redo();
 1600   		markBaseChanged();
 1601   		refreshTable();
 1602   		output(name);
 1603   	    } catch (CannotRedoException ex) {
 1604   		output("Nothing to redo.");
 1605   		/*JOptionPane.showMessageDialog
 1606   		    (parent, "Error: cannot redo!", "Cannot redo",
 1607   		    JOptionPane.ERROR_MESSAGE);*/
 1608   	    }
 1609   	    // After everything, enable/disable the undo/redo actions
 1610   	    // appropriately.
 1611   	    updateRedoState();
 1612   	    undoAction.updateUndoState();	   
 1613   	    markChangedOrUnChanged();
 1614   	}
 1615   
 1616   	public void updateRedoState() {
 1617   	    //setEnabled(undoManager.canRedo());
 1618   	    putValue(AbstractAction.NAME, 
 1619   		     undoManager.getRedoPresentationName());
 1620   	}
 1621   
 1622       }
 1623   
 1624       AboutAction aboutAction = new AboutAction(this);
 1625       class AboutAction extends AbstractAction {
 1626   	BibtexBaseFrame parent;
 1627   	public AboutAction(BibtexBaseFrame parent_) {
 1628   	    super("About Bibkeeper");//, 
 1629   	    //  new ImageIcon(GUIGlobals.addColumnIconFile));
 1630   	    parent = parent_;
 1631   	}    
 1632   	public void actionPerformed(ActionEvent e) {
 1633   	    JDialog about = new JDialog(parent, "About Bibkeeper", true);
 1634   	    JEditorPane jp = new JEditorPane();
 1635   	    JScrollPane sp = new JScrollPane(jp, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
 1636   					     JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
 1637   	    jp.setEditable(false);
 1638   	    try {
 1639   		jp.setPage(GUIGlobals.aboutPage);
 1640   		// We need a hyperlink listener to be able to switch to the license
 1641   		// terms and back.
 1642   		jp.addHyperlinkListener(new javax.swing.event.HyperlinkListener() {
 1643   			public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent e) {    
 1644   			    if (e.getEventType() 
 1645   				== javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) 
 1646   				try {
 1647   				    ((JEditorPane)e.getSource()).setPage(e.getURL());
 1648   				} catch (IOException ex) {}
 1649   			}
 1650   		    });
 1651   		about.getContentPane().add(sp);
 1652   		about.setSize(GUIGlobals.aboutSize);
 1653   		Util.placeDialog(about, parent);
 1654   		about.setVisible(true);
 1655   	    } catch (IOException ex) {
 1656   		JOptionPane.showMessageDialog(parent, "Could not load file 'About.html'", "Error", JOptionPane.ERROR_MESSAGE); 
 1657   	    }
 1658   
 1659   	}
 1660       }
 1661   
 1662       ShowGroupsAction showGroupsAction = new ShowGroupsAction(this);
 1663       class ShowGroupsAction extends AbstractAction {
 1664   	BibtexBaseFrame parent;
 1665   	public ShowGroupsAction(BibtexBaseFrame parent_) {
 1666   	    super("Toggle groups interface", 
 1667   		  new ImageIcon(GUIGlobals.groupsIconFile));
 1668   	    parent = parent_;
 1669   	}    
 1670   	public void actionPerformed(ActionEvent e) {
 1671   	    sidePaneManager.togglePanel("groups");
 1672   	}
 1673       }
 1674   
 1675       public void addToGroup(String groupName, String regexp, String field) {
 1676   	
 1677   	boolean giveWarning = false;
 1678   	for (int i=0; i<GUIGlobals.ALL_FIELDS.length; i++) {
 1679   	    if (field.equals(GUIGlobals.ALL_FIELDS[i])
 1680   		&& !field.equals("keywords")) {
 1681   		giveWarning = true;
 1682   		break;
 1683   	    }	       
 1684   	}
 1685   	if (giveWarning) {
 1686   	    String message = "This action will modify the '"+field+"' field "
 1687   		+"of your entries.\nThis could cause undesired changes to "
 1688   		+"your entries, so it\nis recommended that you change the field "
 1689   		+"in your group\ndefinition to 'keywords' or a non-standard name."
 1690   		+"\n\nDo you still want to continue?";
 1691   	    int choice = JOptionPane.showConfirmDialog
 1692   		(this, message, "Warning", JOptionPane.YES_NO_OPTION,
 1693   		 JOptionPane.WARNING_MESSAGE);
 1694   	    
 1695   	    if (choice == JOptionPane.NO_OPTION)
 1696   		return;
 1697   	}
 1698   	
 1699   	BibtexEntry[] bes = entryTable.getSelectedEntries();	    
 1700   	if ((bes != null) && (bes.length > 0)) {
 1701   	    QuickSearchRule qsr = new QuickSearchRule(field, regexp);
 1702   	    NamedCompound ce = new NamedCompound("add to group");
 1703   	    boolean hasEdits = false;
 1704   	    for (int i=0; i<bes.length; i++) {
 1705   		if (qsr.applyRule(null, bes[i]) == 0) {
 1706   		    String oldContent = (String)bes[i].getField(field),
 1707   			pre = " ",
 1708   			post = "";
 1709   		    String newContent = 
 1710   			(oldContent==null ? "" : oldContent+pre)
 1711   			+regexp+post;
 1712   		    bes[i].setField
 1713   			(field, newContent);
 1714   		    
 1715   		    // Store undo information.
 1716   		    ce.addEdit(new UndoableFieldChange
 1717   			       (bes[i], field, oldContent, newContent));
 1718   		    hasEdits = true;
 1719   		}
 1720   	    }
 1721   	    if (hasEdits) {
 1722   		ce.end();
 1723   		undoManager.addEdit(ce);
 1724   		refreshTable();
 1725   		markBaseChanged();
 1726   	    }		    
 1727   
 1728   	    output("Appended '"+regexp+"' to the '"
 1729   		   +field+"' field of "+bes.length+" entr"+
 1730   		   (bes.length > 1 ? "ies." : "y."));
 1731   	}       
 1732       }
 1733   
 1734       public void removeFromGroup
 1735   	(String groupName, String regexp, String field) {
 1736   	
 1737   	boolean giveWarning = false;
 1738   	for (int i=0; i<GUIGlobals.ALL_FIELDS.length; i++) {
 1739   	    if (field.equals(GUIGlobals.ALL_FIELDS[i])
 1740   		&& !field.equals("keywords")) {
 1741   		giveWarning = true;
 1742   		break;
 1743   	    }	       
 1744   	}
 1745   	if (giveWarning) {
 1746   	    String message = "This action will modify the '"+field+"' field "
 1747   		+"of your entries.\nThis could cause undesired changes to "
 1748   		+"your entries, so it\nis recommended that you change the field "
 1749   		+"in your group\ndefinition to 'keywords' or a non-standard name."
 1750   		+"\n\nDo you still want to continue?";
 1751   	    int choice = JOptionPane.showConfirmDialog
 1752   		(this, message, "Warning", JOptionPane.YES_NO_OPTION,
 1753   		 JOptionPane.WARNING_MESSAGE);
 1754   	    
 1755   	    if (choice == JOptionPane.NO_OPTION)
 1756   		return;
 1757   	}
 1758   	
 1759   	BibtexEntry[] bes = entryTable.getSelectedEntries();	    
 1760   	if ((bes != null) && (bes.length > 0)) {
 1761   	    QuickSearchRule qsr = new QuickSearchRule(field, regexp);
 1762   	    NamedCompound ce = new NamedCompound("remove from group");
 1763   	    boolean hasEdits = false;
 1764   	    for (int i=0; i<bes.length; i++) {
 1765   		if (qsr.applyRule(null, bes[i]) > 0) {
 1766   		    String oldContent = (String)bes[i].getField(field);
 1767   		    qsr.removeMatches(bes[i]);
 1768   		    		    // Store undo information.
 1769   		    ce.addEdit(new UndoableFieldChange
 1770   			       (bes[i], field, oldContent,
 1771   				bes[i].getField(field)));
 1772   		    hasEdits = true;
 1773   		}
 1774   	    }
 1775   	    if (hasEdits) {
 1776   		ce.end();
 1777   		undoManager.addEdit(ce);
 1778   		refreshTable();
 1779   		markBaseChanged();
 1780   	    }	    
 1781   	    
 1782   	    output("Removed '"+regexp+"' from the '"
 1783   		   +field+"' field of "+bes.length+" entr"+
 1784   		   (bes.length > 1 ? "ies." : "y."));
 1785   	}
 1786   	
 1787       }
 1788       
 1789   
 1790       SaveSpecialAction saveSpecialAction = new SaveSpecialAction(this);
 1791       class SaveSpecialAction extends AbstractAction {
 1792   	BibtexBaseFrame parent;
 1793   	public SaveSpecialAction(BibtexBaseFrame parent_) {
 1794   	    super("Save special",
 1795   		  new ImageIcon(GUIGlobals.saveAsIconFile));
 1796   		//, new ImageIcon(GUIGlobals.groupsIconFile));
 1797   	    parent = parent_;
 1798   	}    
 1799   	public void actionPerformed(ActionEvent e) {
 1800   	    SaveSpecialDialog ssd = new SaveSpecialDialog(parent);
 1801   	    ssd.show();
 1802   	    if (!ssd.okPressed())
 1803   		return; // Cancelled.
 1804   	    JFileChooser fs = new JFileChooser();
 1805   	    String[] fileType = ssd.getFileType();
 1806   	    ExampleFileFilter filter = new ExampleFileFilter
 1807   		(fileType[0], fileType[1]);
 1808   	    fs.setFileFilter(filter); //addChoosableFileFilter(filter);
 1809   	    int returnVal = fs.showSaveDialog(parent);
 1810   	    if(returnVal != JFileChooser.APPROVE_OPTION) {
 1811   		return; // Cancelled.
 1812   	    }
 1813   
 1814   	    String name = fs.getSelectedFile().getPath();
 1815   	    if (!name.endsWith("."+fileType[0]))
 1816   		name = name+"."+fileType[0];
 1817   	    File saveTo = new File(name);
 1818   
 1819   	    if (ssd.getChoice() == SaveSpecialDialog.SAVE_SEARCH) {
 1820   		try {
 1821   		    FileActions.saveDatabase(database, metaData, saveTo,
 1822   					     prefs, showingSearchResults,
 1823   					     showingGroup);
 1824   		} catch (SaveException ex) {
 1825   		    if (ex.specificEntry()) {
 1826   			// Error occured during processing of
 1827   			// be. Highlight it:
 1828   			int row = tableModel.getNumberFromName
 1829   			    (ex.getEntry().getId()),
 1830   			    topShow = Math.max(0, row-3);
 1831   			//Util.pr(""+row);
 1832   			entryTable.setRowSelectionInterval(row, row);   
 1833   			entryTable.setColumnSelectionInterval
 1834   			    (0, entryTable.getColumnCount()-1);
 1835   			entryTable.scrollTo(topShow);
 1836   		    }
 1837   		    JOptionPane.showMessageDialog
 1838   			(parent, "Could not save file.\n"+ex.getMessage(), 
 1839   			 "Save database", JOptionPane.ERROR_MESSAGE);
 1840   
 1841   		}
 1842   	    } else if (ssd.getChoice() == 
 1843   		       SaveSpecialDialog.OPENOFFICE_EXPORT) {		
 1844   
 1845   		// Show a warning message.
 1846   		JOptionPane.showMessageDialog
 1847   		    (parent, "Warning: this feature is rather experimental "
 1848   		     +"so far.\nCrossreferences will be resolved where "
 1849   		     +"possible, but\nLaTeX commands will not be removed.",
 1850   		     "Export to OpenOffice.org format", 
 1851   		     JOptionPane.WARNING_MESSAGE);
 1852   
 1853   		// The openoffice exporter.
 1854   		OpenofficeTextExport expo =
 1855   		    new OpenofficeTextExport(prefs);
 1856   		try {
 1857   		    expo.export(database, saveTo);
 1858   		    output("Exported in OpenOffice CSV format to '"
 1859   			   +saveTo.getPath()+"'.");
 1860   		} catch (SaveException ex) {
 1861   		    JOptionPane.showMessageDialog
 1862   			(null, "Error exporting database:\n"+ex.getMessage(),
 1863   			 "Error exporting database", 
 1864   			 JOptionPane.ERROR_MESSAGE);
 1865   		}
 1866   
 1867   	    
 1868   
 1869   	    }
 1870   	}
 1871       }
 1872   
 1873       public void mouseClicked(MouseEvent e) {
 1874   	// Intercepts mouse clicks from the JTable showing the base contents.
 1875   	// A double click on an entry should open the entry's editor.
 1876   	if (e.getClickCount() == 2) {
 1877   	    editEntryAction.actionPerformed(null);
 1878   	}
 1879   	
 1880       }
 1881   
 1882       
 1883   
 1884       public void entryTypeFormClosing(String id) {
 1885   	// Called by EntryTypeForm when closing.
 1886   	entryTypeForms.remove(id);
 1887       }
 1888   
 1889       public void mouseEntered(MouseEvent e) {}
 1890       public void mouseExited(MouseEvent e) {}
 1891       public void mousePressed(MouseEvent e) {}
 1892       public void mouseReleased(MouseEvent e) {}
 1893   
 1894       // Method pertaining to the ClipboardOwner interface.
 1895       public void lostOwnership(Clipboard clipboard, Transferable contents) {}
 1896   
 1897       // a field which contains the label, the rule(s) are made only if called
 1898       // add more LabelMaker's if more patterns are needed
 1899       protected LabelMaker labelMaker = null ; 
 1900   
 1901   
 1902   }

Save This Page
Home » openjdk-7 » net » sf » bibkeeper » [javadoc | source]