package org.jabref.gui.cleanup; import java.awt.BorderLayout; import java.awt.Component; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; import javax.swing.DefaultListCellRenderer; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JList; import javax.swing.JPanel; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; import javax.swing.UIManager; import javax.swing.event.ListDataEvent; import javax.swing.event.ListDataListener; import org.jabref.JabRefGUI; import org.jabref.logic.cleanup.Cleanups; import org.jabref.logic.l10n.Localization; import org.jabref.model.cleanup.FieldFormatterCleanup; import org.jabref.model.cleanup.FieldFormatterCleanups; import org.jabref.model.cleanup.Formatter; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.entry.BibEntry; import org.jabref.model.entry.InternalBibtexFields; import org.jabref.model.metadata.MetaData; import com.jgoodies.forms.builder.FormBuilder; import com.jgoodies.forms.layout.FormLayout; public class FieldFormatterCleanupsPanel extends JPanel { private static final String DESCRIPTION = Localization.lang("Description") + ": "; private final JCheckBox cleanupEnabled; private FieldFormatterCleanups fieldFormatterCleanups; private JList<?> actionsList; private JComboBox<?> formattersCombobox; private JComboBox<String> selectFieldCombobox; private JButton addButton; private JTextArea descriptionAreaText; private JButton removeButton; private JButton resetButton; private JButton recommendButton; private final FieldFormatterCleanups defaultFormatters; public FieldFormatterCleanupsPanel(String description, FieldFormatterCleanups defaultFormatters) { this.defaultFormatters = Objects.requireNonNull(defaultFormatters); cleanupEnabled = new JCheckBox(description); } public void setValues(MetaData metaData) { Objects.requireNonNull(metaData); Optional<FieldFormatterCleanups> saveActions = metaData.getSaveActions(); setValues(saveActions.orElse(Cleanups.DEFAULT_SAVE_ACTIONS)); } public void setValues(FieldFormatterCleanups formatterCleanups) { fieldFormatterCleanups = formatterCleanups; // first clear existing content this.removeAll(); List<FieldFormatterCleanup> configuredActions = fieldFormatterCleanups.getConfiguredActions(); //The copy is necessary because the original List is unmodifiable List<FieldFormatterCleanup> actionsToDisplay = new ArrayList<>(configuredActions); buildLayout(actionsToDisplay); } private void buildLayout(List<FieldFormatterCleanup> actionsToDisplay) { FormBuilder builder = FormBuilder.create().layout(new FormLayout( "left:pref, 13dlu, left:pref:grow, 4dlu, pref, 4dlu, pref", "pref, 2dlu, pref, 2dlu, pref, 4dlu, pref, 2dlu, fill:pref:grow, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu, pref, 2dlu")); builder.add(cleanupEnabled).xyw(1, 1, 7); actionsList = new JList<>(new CleanupActionsListModel(actionsToDisplay)); actionsList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); actionsList.addMouseMotionListener(new MouseMotionAdapter() { @Override public void mouseMoved(MouseEvent e) { super.mouseMoved(e); CleanupActionsListModel m = (CleanupActionsListModel) actionsList.getModel(); int index = actionsList.locationToIndex(e.getPoint()); if (index > -1) { actionsList.setToolTipText(m.getElementAt(index).getFormatter().getDescription()); } } }); actionsList.getModel().addListDataListener(new ListDataListener() { @Override public void intervalRemoved(ListDataEvent e) { //index0 is sufficient, because of SingleSelection if (e.getIndex0() == 0) { //when an item gets deleted, the next one becomes the new 0 actionsList.setSelectedIndex(e.getIndex0()); } if (e.getIndex0() > 0) { actionsList.setSelectedIndex(e.getIndex0() - 1); } } @Override public void intervalAdded(ListDataEvent e) { //empty, not needed } @Override public void contentsChanged(ListDataEvent e) { //empty, not needed } }); builder.add(actionsList).xyw(3, 5, 5); resetButton = new JButton(Localization.lang("Reset")); resetButton.addActionListener(e -> ((CleanupActionsListModel) actionsList.getModel()).reset(defaultFormatters)); BibDatabaseContext databaseContext = JabRefGUI.getMainFrame().getCurrentBasePanel().getDatabaseContext(); recommendButton = new JButton(Localization.lang("Recommended for %0", databaseContext.getMode().getFormattedName())); boolean isBiblatex = databaseContext.isBiblatexMode(); recommendButton.addActionListener(e -> { if (isBiblatex) { ((CleanupActionsListModel) actionsList.getModel()).reset(Cleanups.RECOMMEND_BIBLATEX_ACTIONS); } else { ((CleanupActionsListModel) actionsList.getModel()).reset(Cleanups.RECOMMEND_BIBTEX_ACTIONS); } }); removeButton = new JButton(Localization.lang("Remove selected")); removeButton.addActionListener( e -> ((CleanupActionsListModel) actionsList.getModel()).removeAtIndex(actionsList.getSelectedIndex())); builder.add(removeButton).xy(7, 11); builder.add(resetButton).xy(3, 11); builder.add(recommendButton).xy(5, 11); builder.add(getSelectorPanel()).xyw(3, 15, 5); makeDescriptionTextAreaLikeJLabel(); builder.add(descriptionAreaText).xyw(3, 17, 5); this.setLayout(new BorderLayout()); this.add(builder.getPanel(), BorderLayout.WEST); updateDescription(); // make sure the layout is set according to the checkbox cleanupEnabled.addActionListener(new EnablementStatusListener(fieldFormatterCleanups.isEnabled())); cleanupEnabled.setSelected(fieldFormatterCleanups.isEnabled()); } /** * Create a TextArea that looks and behaves like a JLabel. Has the advantage of supporting multine and wordwrap */ private void makeDescriptionTextAreaLikeJLabel() { descriptionAreaText = new JTextArea(DESCRIPTION); descriptionAreaText.setLineWrap(true); descriptionAreaText.setWrapStyleWord(true); descriptionAreaText.setColumns(6); descriptionAreaText.setEditable(false); descriptionAreaText.setOpaque(false); descriptionAreaText.setFocusable(false); descriptionAreaText.setCursor(null); descriptionAreaText.setFont(UIManager.getFont("Label.font")); } private void updateDescription() { FieldFormatterCleanup formatterCleanup = getFieldFormatterCleanup(); if (formatterCleanup != null) { descriptionAreaText.setText(DESCRIPTION + formatterCleanup.getFormatter().getDescription()); } else { Formatter selectedFormatter = getFieldFormatter(); if (selectedFormatter != null) { descriptionAreaText.setText(DESCRIPTION + selectedFormatter.getDescription()); } else { descriptionAreaText.setText(DESCRIPTION); } } } /** * This panel contains the two comboboxes and the Add button * @return Returns the created JPanel */ private JPanel getSelectorPanel() { FormBuilder builder = FormBuilder.create() .layout(new FormLayout("left:pref:grow, 4dlu, left:pref:grow, 4dlu, fill:pref:grow, 4dlu, right:pref", "fill:pref:grow, 2dlu, pref, 2dlu")); List<String> fieldNames = InternalBibtexFields.getAllPublicAndInternalFieldNames(); fieldNames.add(BibEntry.KEY_FIELD); Collections.sort(fieldNames); String[] allPlusKey = fieldNames.toArray(new String[fieldNames.size()]); selectFieldCombobox = new JComboBox<>(allPlusKey); selectFieldCombobox.setEditable(true); builder.add(selectFieldCombobox).xy(1, 1); List<String> formatterNames = Cleanups.getAvailableFormatters().stream() .map(Formatter::getName).collect(Collectors.toList()); List<String> formatterDescriptions = Cleanups.getAvailableFormatters().stream() .map(Formatter::getDescription).collect(Collectors.toList()); formattersCombobox = new JComboBox<>(formatterNames.toArray()); formattersCombobox.setRenderer(new DefaultListCellRenderer() { @Override public Component getListCellRendererComponent(JList<?> list, Object value, int index, boolean isSelected, boolean cellHasFocus) { if ((-1 < index) && (index < formatterDescriptions.size()) && (value != null)) { setToolTipText(formatterDescriptions.get(index)); } return super.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus); } }); formattersCombobox.addItemListener(e -> updateDescription()); builder.add(formattersCombobox).xy(3, 1); addButton = new JButton(Localization.lang("Add")); addButton.addActionListener(e -> { FieldFormatterCleanup newAction = getFieldFormatterCleanup(); if (newAction == null) { return; } ((CleanupActionsListModel) actionsList.getModel()).addCleanupAction(newAction); }); builder.add(addButton).xy(5, 1); return builder.getPanel(); } public void storeSettings(MetaData metaData) { Objects.requireNonNull(metaData); FieldFormatterCleanups formatterCleanups = getFormatterCleanups(); // if all actions have been removed, remove the save actions from the MetaData if (formatterCleanups.getConfiguredActions().isEmpty()) { metaData.clearSaveActions(); return; } metaData.setSaveActions(formatterCleanups); } public FieldFormatterCleanups getFormatterCleanups() { List<FieldFormatterCleanup> actions = ((CleanupActionsListModel) actionsList.getModel()).getAllActions(); return new FieldFormatterCleanups(cleanupEnabled.isSelected(), actions); } public boolean hasChanged() { return !fieldFormatterCleanups.equals(getFormatterCleanups()); } public boolean isDefaultSaveActions() { return Cleanups.DEFAULT_SAVE_ACTIONS.equals(getFormatterCleanups()); } private FieldFormatterCleanup getFieldFormatterCleanup() { Formatter selectedFormatter = getFieldFormatter(); String fieldKey = selectFieldCombobox.getSelectedItem().toString(); return new FieldFormatterCleanup(fieldKey, selectedFormatter); } private Formatter getFieldFormatter() { Formatter selectedFormatter = null; String selectedFormatterName = formattersCombobox.getSelectedItem().toString(); for (Formatter formatter : Cleanups.getAvailableFormatters()) { if (formatter.getName().equals(selectedFormatterName)) { selectedFormatter = formatter; break; } } return selectedFormatter; } class EnablementStatusListener implements ActionListener { public EnablementStatusListener(boolean initialStatus) { setStatus(initialStatus); } @Override public void actionPerformed(ActionEvent e) { boolean enablementStatus = cleanupEnabled.isSelected(); setStatus(enablementStatus); } private void setStatus(boolean status) { actionsList.setEnabled(status); selectFieldCombobox.setEnabled(status); formattersCombobox.setEnabled(status); addButton.setEnabled(status); removeButton.setEnabled(status); resetButton.setEnabled(status); recommendButton.setEnabled(status); } } }