package org.jabref.gui.preftabs; import java.awt.BorderLayout; import java.awt.Dimension; import java.util.ArrayList; import java.util.Enumeration; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.BoxLayout; import javax.swing.DefaultListModel; import javax.swing.JButton; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextArea; import javax.swing.ListSelectionModel; import javax.swing.SwingWorker; import org.jabref.Globals; import org.jabref.JabRefGUI; import org.jabref.gui.BasePanel; import org.jabref.gui.PreviewPanel; import org.jabref.logic.citationstyle.CitationStyle; import org.jabref.logic.l10n.Localization; import org.jabref.logic.util.TestEntry; import org.jabref.preferences.PreviewPreferences; import com.jgoodies.forms.builder.FormBuilder; import com.jgoodies.forms.factories.Paddings; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; public class PreviewPrefsTab extends JPanel implements PrefsTab { private static final Log LOGGER = LogFactory.getLog(PreviewPrefsTab.class); private SwingWorker<List<CitationStyle>, Void> discoverCitationStyleWorker; private final DefaultListModel<Object> availableModel = new DefaultListModel<>(); private final DefaultListModel<Object> chosenModel = new DefaultListModel<>(); private final JList<Object> available = new JList<>(availableModel); private final JList<Object> chosen = new JList<>(chosenModel); private final JButton btnRight = new JButton("»"); private final JButton btnLeft = new JButton("«"); private final JButton btnUp = new JButton(Localization.lang("Up")); private final JButton btnDown = new JButton(Localization.lang("Down")); private final JTextArea layout = new JTextArea("", 1, 1); private final JButton btnTest = new JButton(Localization.lang("Test")); private final JButton btnDefault = new JButton(Localization.lang("Default")); private final JScrollPane scrollPane = new JScrollPane(layout); public PreviewPrefsTab() { setupLogic(); setupGui(); } private void setupLogic() { chosen.getSelectionModel().addListSelectionListener(event -> { boolean selectionEmpty = ((ListSelectionModel) event.getSource()).isSelectionEmpty(); btnLeft.setEnabled(!selectionEmpty); btnDown.setEnabled(!selectionEmpty); btnUp.setEnabled(!selectionEmpty); }); available.getSelectionModel() .addListSelectionListener(e -> btnRight.setEnabled(!((ListSelectionModel) e.getSource()).isSelectionEmpty())); btnRight.addActionListener(event -> { for (Object object : available.getSelectedValuesList()) { availableModel.removeElement(object); chosenModel.addElement(object); } }); btnLeft.addActionListener(event -> { for (Object object : chosen.getSelectedValuesList()) { availableModel.addElement(object); chosenModel.removeElement(object); } }); btnUp.addActionListener(event -> { List<Integer> newSelectedIndices = new ArrayList<>(); for (int oldIndex : chosen.getSelectedIndices()) { boolean alreadyTaken = newSelectedIndices.contains(oldIndex - 1); int newIndex = (oldIndex > 0 && !alreadyTaken) ? oldIndex - 1 : oldIndex; chosenModel.add(newIndex, chosenModel.remove(oldIndex)); newSelectedIndices.add(newIndex); } chosen.setSelectedIndices(ArrayUtils.toPrimitive(newSelectedIndices.toArray(new Integer[newSelectedIndices.size()]))); }); btnDown.addActionListener(event -> { List<Integer> newSelectedIndices = new ArrayList<>(); int[] selectedIndices = chosen.getSelectedIndices(); for (int i = selectedIndices.length - 1; i >= 0; i--) { int oldIndex = selectedIndices[i]; boolean alreadyTaken = newSelectedIndices.contains(oldIndex + 1); int newIndex = (oldIndex < chosenModel.getSize() - 1 && !alreadyTaken) ? oldIndex + 1 : oldIndex; chosenModel.add(newIndex, chosenModel.remove(oldIndex)); newSelectedIndices.add(newIndex); } chosen.setSelectedIndices(ArrayUtils.toPrimitive(newSelectedIndices.toArray(new Integer[newSelectedIndices.size()]))); }); btnDefault.addActionListener(event -> layout.setText(Globals.prefs.getPreviewPreferences() .getPreviewStyleDefault().replace("__NEWLINE__", "\n"))); btnTest.addActionListener(event -> { try { PreviewPanel testPane = new PreviewPanel(null, TestEntry.getTestEntry(), null) .setFixedLayout(layout.getText()); testPane.setPreferredSize(new Dimension(800, 350)); JOptionPane.showMessageDialog(PreviewPrefsTab.this, new JScrollPane(testPane), Localization.lang("Preview"), JOptionPane.PLAIN_MESSAGE); } catch (StringIndexOutOfBoundsException exception) { LOGGER.warn("Parsing error.", exception); JOptionPane.showMessageDialog(null, Localization.lang("Parsing error") + ": " + Localization.lang("illegal backslash expression") + ".\n" + exception.getMessage(), Localization.lang("Parsing error"), JOptionPane.ERROR_MESSAGE); } }); } private void setupGui() { JPanel chooseStyle = FormBuilder.create() .columns("0:grow, $lcgap, pref, $lcgap, 0:grow") .rows("pref, $lg, fill:pref:grow, $lg, pref:grow, $lg, pref:grow, $lg, pref:grow") .padding(Paddings.DIALOG) .addSeparator(Localization.lang("Current Preview")).xyw(1, 1, 5) .add(available).xywh(1, 3, 1, 7) .add(chosen).xywh(5, 3, 1, 7) .add(btnRight).xy(3, 3, "fill, bottom") .add(btnLeft).xy(3, 5, "fill, top") .add(btnUp).xy(3, 7, "fill, bottom") .add(btnDown).xy(3, 9, "fill, top") .build(); JPanel preview = FormBuilder.create() .columns("pref:grow, $lcgap, pref, $lcgap, pref") .rows("pref, $lg, fill:pref:grow") .padding(Paddings.DIALOG) .addSeparator(Localization.lang("Preview")).xy(1, 1) .add(btnTest).xy(3, 1) .add(btnDefault).xy(5, 1) .add(scrollPane).xyw(1, 3, 5) .build(); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); add(chooseStyle, BorderLayout.CENTER); add(preview, BorderLayout.PAGE_END); } @Override public void setValues() { PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences(); chosenModel.clear(); boolean isPreviewChosen = false; for (String style : previewPreferences.getPreviewCycle()) { if (CitationStyle.isCitationStyleFile(style)) { chosenModel.addElement(CitationStyle.createCitationStyleFromFile(style)); } else { if (isPreviewChosen) { LOGGER.error("Preview is already in the list, something went wrong"); continue; } isPreviewChosen = true; chosenModel.addElement(Localization.lang("Preview")); } } availableModel.clear(); if (!isPreviewChosen) { availableModel.addElement(Localization.lang("Preview")); } btnLeft.setEnabled(!chosen.isSelectionEmpty()); btnRight.setEnabled(!available.isSelectionEmpty()); btnUp.setEnabled(!chosen.isSelectionEmpty()); btnDown.setEnabled(!chosen.isSelectionEmpty()); if (discoverCitationStyleWorker != null) { discoverCitationStyleWorker.cancel(true); } discoverCitationStyleWorker = new SwingWorker<List<CitationStyle>, Void>() { @Override protected List<CitationStyle> doInBackground() throws Exception { return CitationStyle.discoverCitationStyles(); } @Override public void done() { if (this.isCancelled()) { return; } try { get().stream() .filter(style -> !previewPreferences.getPreviewCycle().contains(style.getFilepath())) .sorted((style0, style1) -> style0.getTitle().compareTo(style1.getTitle())) .forEach(availableModel::addElement); btnRight.setEnabled(!availableModel.isEmpty()); } catch (InterruptedException | ExecutionException e) { LOGGER.error("something went wrong while adding the discovered CitationStyles to the list "); } } }; discoverCitationStyleWorker.execute(); layout.setText(Globals.prefs.getPreviewPreferences().getPreviewStyle().replace("__NEWLINE__", "\n")); } @Override public void storeSettings() { List<String> styles = new ArrayList<>(); Enumeration<Object> elements = chosenModel.elements(); while (elements.hasMoreElements()) { Object obj = elements.nextElement(); if (obj instanceof CitationStyle) { styles.add(((CitationStyle) obj).getFilepath()); } else if (obj instanceof String) { styles.add("Preview"); } } PreviewPreferences previewPreferences = Globals.prefs.getPreviewPreferences() .getBuilder() .withPreviewCycle(styles) .withPreviewStyle(layout.getText().replace("\n", "__NEWLINE__")) .build(); Globals.prefs.storePreviewPreferences(previewPreferences); // update preview for (BasePanel basePanel : JabRefGUI.getMainFrame().getBasePanelList()) { basePanel.getPreviewPanel().updateLayout(); } } @Override public boolean validateSettings() { return !chosenModel.isEmpty(); } @Override public String getTabName() { return Localization.lang("Entry preview"); } }