/* * EntryCustomizationDialog2.java * * Created on February 10, 2005, 9:57 PM */ package net.sf.jabref.gui; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.util.*; import java.util.List; import javax.swing.*; import javax.swing.event.ListDataListener; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import net.sf.jabref.*; import com.jgoodies.forms.builder.ButtonBarBuilder; public class EntryCustomizationDialog2 extends JDialog implements ListSelectionListener, ActionListener { protected JabRefFrame frame; protected GridBagLayout gbl = new GridBagLayout(); protected GridBagConstraints con = new GridBagConstraints(); protected FieldSetComponent reqComp, optComp; protected EntryTypeList typeComp; protected JButton ok, cancel, apply, helpButton, delete, importTypes, exportTypes; protected final List<String> preset = java.util.Arrays.asList(BibtexFields.getAllFieldNames()); protected String lastSelected = null; protected Map<String, List<String>> reqLists = new HashMap<String, List<String>>(), optLists = new HashMap<String, List<String>>(); protected Set<String> defaulted = new HashSet<String>(), changed = new HashSet<String>(); /** Creates a new instance of EntryCustomizationDialog2 */ public EntryCustomizationDialog2(JabRefFrame frame) { super(frame.getFrame(), Globals.lang("Customize entry types"), false); this.frame = frame; initGui(); } protected final void initGui() { Container pane = getContentPane(); pane.setLayout(new BorderLayout()); JPanel main = new JPanel(), buttons = new JPanel(), right = new JPanel(); main.setLayout(new BorderLayout()); right.setLayout(new GridLayout(1, 2)); java.util.List<String> entryTypes = new ArrayList<String>(); for (Iterator<String> i=BibtexEntryType.ALL_TYPES.keySet().iterator(); i.hasNext();) { entryTypes.add(i.next()); } typeComp = new EntryTypeList(entryTypes); typeComp.addListSelectionListener(this); typeComp.addAdditionActionListener(this); typeComp.addDefaultActionListener(new DefaultListener()); typeComp.setListSelectionMode(ListSelectionModel.SINGLE_SELECTION); //typeComp.setEnabled(false); reqComp = new FieldSetComponent(Globals.lang("Required fields"), new ArrayList<String>(), preset, true, true); reqComp.setEnabled(false); reqComp.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); ListDataListener dataListener = new DataListener(); reqComp.addListDataListener(dataListener); optComp = new FieldSetComponent(Globals.lang("Optional fields"), new ArrayList<String>(), preset, true, true); optComp.setEnabled(false); optComp.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); optComp.addListDataListener(dataListener); right.add(reqComp); right.add(optComp); //right.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), Globals.lang("Fields"))); right.setBorder(BorderFactory.createEtchedBorder()); ok = new JButton("OK"); cancel = new JButton(Globals.lang("Cancel")); apply = new JButton(Globals.lang("Apply")); ok.addActionListener(this); apply.addActionListener(this); cancel.addActionListener(this); ButtonBarBuilder bb = new ButtonBarBuilder(buttons); buttons.setBorder(BorderFactory.createEmptyBorder(2,2,2,2)); bb.addGlue(); bb.addGridded(ok); bb.addGridded(apply); bb.addGridded(cancel); bb.addGlue(); AbstractAction closeAction = new AbstractAction() { public void actionPerformed(ActionEvent e) { dispose(); } }; ActionMap am = main.getActionMap(); InputMap im = main.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW); im.put(Globals.prefs.getKey("Close dialog"), "close"); am.put("close", closeAction); //con.fill = GridBagConstraints.BOTH; //con.weightx = 0.3; //con.weighty = 1; //gbl.setConstraints(typeComp, con); main.add(typeComp, BorderLayout.WEST); main.add(right, BorderLayout.CENTER); main.setBorder(BorderFactory.createEmptyBorder(5,5,5,5)); pane.add(main, BorderLayout.CENTER); pane.add(buttons, BorderLayout.SOUTH); pack(); } public void valueChanged(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } if (lastSelected != null) { // The entry type lastSelected is now unselected, so we store the current settings // for that type in our two maps. reqLists.put(lastSelected, reqComp.getFields()); optLists.put(lastSelected, optComp.getFields()); } String s = typeComp.getFirstSelected(); if (s == null) return; List<String> rl = reqLists.get(s); if (rl == null) { BibtexEntryType type = BibtexEntryType.getType(s); if (type != null) { String[] rf = type.getRequiredFieldsForCustomization(), of = type.getOptionalFields(); List<String> req, opt; if (rf != null) req = java.util.Arrays.asList(rf); else req = new ArrayList<String>(); if (of != null) opt = java.util.Arrays.asList(of); else opt = new ArrayList<String>(); reqComp.setFields(req); reqComp.setEnabled(true); optComp.setFields(opt); optComp.setEnabled(true); } else { // New entry, veintle reqComp.setFields(new ArrayList<String>()); reqComp.setEnabled(true); optComp.setFields(new ArrayList<String>()); optComp.setEnabled(true); new FocusRequester(reqComp); } } else { reqComp.setFields(rl); optComp.setFields(optLists.get(s)); } lastSelected = s; typeComp.enable(s, changed.contains(lastSelected) && !defaulted.contains(lastSelected)); } protected void applyChanges() { valueChanged(new ListSelectionEvent(new JList(), 0, 0, false)); // Iterate over our map of required fields, and list those types if necessary: List<String> types = typeComp.getFields(); for (Iterator<String> i=reqLists.keySet().iterator(); i.hasNext();) { String typeName = i.next(); if (!types.contains(typeName)) continue; List<String> reqFields = reqLists.get(typeName); List<String> optFields = optLists.get(typeName); String[] reqStr = new String[reqFields.size()]; reqStr = reqFields.toArray(reqStr); String[] optStr = new String[optFields.size()]; optStr = optFields.toArray(optStr); // If this type is already existing, check if any changes have // been made boolean changesMade = true; if (defaulted.contains(typeName)) { // This type should be reverted to its default setup. //System.out.println("Defaulting: "+typeName); String nm = Util.nCase(typeName); BibtexEntryType.removeType(nm); updateTypesForEntries(nm); continue; } BibtexEntryType oldType = BibtexEntryType.getType(typeName); if (oldType != null) { String[] oldReq = oldType.getRequiredFields(), oldOpt = oldType.getOptionalFields(); if (equalArrays(oldReq, reqStr) && equalArrays(oldOpt, optStr)) changesMade = false; } if (changesMade) { //System.out.println("Updating: "+typeName); CustomEntryType typ = new CustomEntryType(Util.nCase(typeName), reqStr, optStr); BibtexEntryType.ALL_TYPES.put(typeName.toLowerCase(), typ); updateTypesForEntries(typ.getName()); } } Set<Object> toRemove = new HashSet<Object>(); for (Iterator<String> i=BibtexEntryType.ALL_TYPES.keySet().iterator(); i.hasNext();) { Object o = i.next(); if (!types.contains(o)) { toRemove.add(o); } } // Remove those that should be removed: if (toRemove.size() > 0) { for (Iterator<Object> i=toRemove.iterator(); i.hasNext();) typeDeletion((String)i.next()); } updateTables(); } protected void typeDeletion(String name) { BibtexEntryType type = BibtexEntryType.getType(name); if (type instanceof CustomEntryType) { if (BibtexEntryType.getStandardType(name) == null) { int reply = JOptionPane.showConfirmDialog (frame, Globals.lang("All entries of this " +"type will be declared " +"typeless. Continue?"), Globals.lang("Delete custom format")+ " '"+Util.nCase(name)+"'", JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE); if (reply != JOptionPane.YES_OPTION) return; } BibtexEntryType.removeType(name); updateTypesForEntries(Util.nCase(name)); changed.remove(name); reqLists.remove(name); optLists.remove(name); } //messageLabel.setText("'"+type.getName()+"' "+ // Globals.lang("is a standard type.")); } protected boolean equalArrays(String[] one, String[] two) { if ((one == null) && (two == null)) return true; // Both null. if ((one == null) || (two == null)) return false; // One of them null, the other not. if (one.length != two.length) return false; // Different length. // If we get here, we know that both are non-null, and that they have the same length. for (int i=0; i<one.length; i++) { if (!one[i].equals(two[i])) return false; } // If we get here, all entries have matched. return true; } public void actionPerformed(ActionEvent e) { if (e.getSource() == ok) { applyChanges(); dispose(); } else if (e.getSource() == cancel) { dispose(); } else if (e.getSource() == apply) { applyChanges(); } else if (e.getSource() == typeComp) { //System.out.println("add: "+e.getActionCommand()); typeComp.selectField(e.getActionCommand()); } } /** * Cycle through all databases, and make sure everything is updated with * the new type customization. This includes making sure all entries have * a valid type, that no obsolete entry editors are around, and that * the right-click menus' change type menu is up-to-date. */ private void updateTypesForEntries(String typeName) { if (frame.getTabbedPane().getTabCount() == 0) return; for (int i=0; i<frame.getTabbedPane().getTabCount(); i++) { BasePanel bp = (BasePanel)frame.getTabbedPane().getComponentAt(i); // Invalidate associated cached entry editor bp.entryEditors.remove(typeName); for (BibtexEntry entry : bp.database().getEntries()){ entry.updateType(); } } } private void updateTables() { if (frame.getTabbedPane().getTabCount() == 0) return; for (int i=0; i<frame.getTabbedPane().getTabCount(); i++) { frame.getTabbedPane().getComponentAt(i); } } // DEFAULT button pressed. Remember that this entry should be reset to default, // unless changes are made later. class DefaultListener implements ActionListener { public void actionPerformed(ActionEvent e) { if (lastSelected == null) return; defaulted.add(lastSelected); BibtexEntryType type = BibtexEntryType.getStandardType(lastSelected); if (type != null) { String[] rf = type.getRequiredFieldsForCustomization(), of = type.getOptionalFields(); List<String> req, opt; if (rf != null) req = java.util.Arrays.asList(rf); else req = new ArrayList<String>(); if (of != null) opt = java.util.Arrays.asList(of); else opt = new ArrayList<String>(); reqComp.setFields(req); reqComp.setEnabled(true); optComp.setFields(opt); } } } class DataListener implements ListDataListener { public void intervalAdded(javax.swing.event.ListDataEvent e) { record(); } public void intervalRemoved(javax.swing.event.ListDataEvent e) { record(); } public void contentsChanged(javax.swing.event.ListDataEvent e) { record(); } private void record() { if (lastSelected == null) return; defaulted.remove(lastSelected); changed.add(lastSelected); typeComp.enable(lastSelected, true); } } }