package org.jabref.gui.preftabs;
import java.awt.BorderLayout;
import java.awt.CardLayout;
import java.awt.Component;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.prefs.BackingStoreException;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JList;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import org.jabref.Globals;
import org.jabref.JabRefException;
import org.jabref.gui.DialogService;
import org.jabref.gui.FXDialogService;
import org.jabref.gui.GUIGlobals;
import org.jabref.gui.JabRefDialog;
import org.jabref.gui.JabRefFrame;
import org.jabref.gui.keyboard.KeyBinder;
import org.jabref.gui.maintable.MainTable;
import org.jabref.gui.util.DefaultTaskExecutor;
import org.jabref.gui.util.FileDialogConfiguration;
import org.jabref.logic.exporter.ExportFormat;
import org.jabref.logic.exporter.ExportFormats;
import org.jabref.logic.exporter.SavePreferences;
import org.jabref.logic.l10n.Localization;
import org.jabref.logic.layout.LayoutFormatterPreferences;
import org.jabref.logic.util.FileExtensions;
import org.jabref.preferences.JabRefPreferences;
import org.jabref.preferences.JabRefPreferencesFilter;
import org.jabref.shared.prefs.SharedDatabasePreferences;
import com.jgoodies.forms.builder.ButtonBarBuilder;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* Preferences dialog. Contains a TabbedPane, and tabs will be defined in
* separate classes. Tabs MUST implement the PrefsTab interface, since this
* dialog will call the storeSettings() method of all tabs when the user presses
* ok.
*
* With this design, it should be very easy to add new tabs later.
*
*/
public class PreferencesDialog extends JabRefDialog {
private static final Log LOGGER = LogFactory.getLog(PreferencesDialog.class);
private final JPanel main;
private final JabRefFrame frame;
private final JButton importPreferences = new JButton(Localization.lang("Import preferences"));
private final JButton exportPreferences = new JButton(Localization.lang("Export preferences"));
private final JButton showPreferences = new JButton(Localization.lang("Show preferences"));
private final JButton resetPreferences = new JButton(Localization.lang("Reset preferences"));
public PreferencesDialog(JabRefFrame parent) {
super(parent, Localization.lang("JabRef preferences"), false, PreferencesDialog.class);
JabRefPreferences prefs = JabRefPreferences.getInstance();
frame = parent;
main = new JPanel();
JPanel mainPanel = new JPanel();
JPanel lower = new JPanel();
getContentPane().setLayout(new BorderLayout());
getContentPane().add(mainPanel, BorderLayout.CENTER);
getContentPane().add(lower, BorderLayout.SOUTH);
final CardLayout cardLayout = new CardLayout();
main.setLayout(cardLayout);
List<PrefsTab> tabs = new ArrayList<>();
tabs.add(new GeneralTab(prefs));
tabs.add(new FileTab(frame, prefs));
tabs.add(new TablePrefsTab(prefs));
tabs.add(new TableColumnsTab(prefs, parent));
tabs.add(new PreviewPrefsTab());
tabs.add(new ExternalTab(frame, this, prefs));
tabs.add(new GroupsPrefsTab(prefs));
tabs.add(new EntryEditorPrefsTab(prefs));
tabs.add(new BibtexKeyPatternPrefTab(prefs, parent.getCurrentBasePanel()));
tabs.add(new ImportSettingsTab(prefs));
tabs.add(new ExportSortingPrefsTab(prefs));
tabs.add(new NameFormatterTab(prefs));
tabs.add(new XmpPrefsTab(prefs));
tabs.add(new NetworkTab(prefs));
tabs.add(new AdvancedTab(prefs));
tabs.add(new AppearancePrefsTab(prefs));
// add all tabs
tabs.forEach(tab -> main.add((Component) tab, tab.getTabName()));
mainPanel.setBorder(BorderFactory.createEtchedBorder());
String[] tabNames = tabs.stream().map(PrefsTab::getTabName).toArray(String[]::new);
JList<String> chooser = new JList<>(tabNames);
chooser.setBorder(BorderFactory.createEtchedBorder());
// Set a prototype value to control the width of the list:
chooser.setPrototypeCellValue("This should be wide enough");
chooser.setSelectedIndex(0);
chooser.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
// Add the selection listener that will show the correct panel when
// selection changes:
chooser.addListSelectionListener(e -> {
if (e.getValueIsAdjusting()) {
return;
}
String o = chooser.getSelectedValue();
cardLayout.show(main, o);
});
JPanel buttons = new JPanel();
buttons.setLayout(new GridLayout(4, 1));
buttons.add(importPreferences, 0);
buttons.add(exportPreferences, 1);
buttons.add(showPreferences, 2);
buttons.add(resetPreferences, 3);
JPanel westPanel = new JPanel();
westPanel.setLayout(new BorderLayout());
westPanel.add(chooser, BorderLayout.CENTER);
westPanel.add(buttons, BorderLayout.SOUTH);
mainPanel.setLayout(new BorderLayout());
mainPanel.add(putPanelInScrollPane(main), BorderLayout.CENTER);
mainPanel.add(putPanelInScrollPane(westPanel), BorderLayout.WEST);
JButton ok = new JButton(Localization.lang("OK"));
JButton cancel = new JButton(Localization.lang("Cancel"));
ok.addActionListener(new OkAction());
CancelAction cancelAction = new CancelAction();
cancel.addActionListener(cancelAction);
lower.setBorder(BorderFactory.createEmptyBorder(2, 2, 2, 2));
ButtonBarBuilder buttonBarBuilder = new ButtonBarBuilder(lower);
buttonBarBuilder.addGlue();
buttonBarBuilder.addButton(ok);
buttonBarBuilder.addButton(cancel);
buttonBarBuilder.addGlue();
// Key bindings:
KeyBinder.bindCloseDialogKeyToCancelAction(this.getRootPane(), cancelAction);
// Import and export actions:
exportPreferences.setToolTipText(Localization.lang("Export preferences to file"));
exportPreferences.addActionListener(new ExportAction());
importPreferences.setToolTipText(Localization.lang("Import preferences from file"));
importPreferences.addActionListener(e -> {
FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder()
.addExtensionFilter(FileExtensions.XML)
.withDefaultExtension(FileExtensions.XML)
.withInitialDirectory(getPrefsExportPath()).build();
DialogService ds = new FXDialogService();
Optional<Path> fileName = DefaultTaskExecutor
.runInJavaFXThread(() -> ds.showFileOpenDialog(fileDialogConfiguration));
if (fileName.isPresent()) {
try {
prefs.importPreferences(fileName.get().toString());
updateAfterPreferenceChanges();
JOptionPane.showMessageDialog(PreferencesDialog.this,
Localization.lang("You must restart JabRef for this to come into effect."),
Localization.lang("Import preferences"), JOptionPane.WARNING_MESSAGE);
this.dispose();
} catch (JabRefException ex) {
LOGGER.warn(ex.getMessage(), ex);
JOptionPane.showMessageDialog(PreferencesDialog.this, ex.getLocalizedMessage(),
Localization.lang("Import preferences"), JOptionPane.ERROR_MESSAGE);
}
}
});
showPreferences.addActionListener(
e -> new PreferencesFilterDialog(new JabRefPreferencesFilter(prefs), frame).setVisible(true));
resetPreferences.addActionListener(e -> {
if (JOptionPane.showConfirmDialog(PreferencesDialog.this,
Localization.lang("Are you sure you want to reset all settings to default values?"),
Localization.lang("Reset preferences"), JOptionPane.OK_CANCEL_OPTION) == JOptionPane.OK_OPTION) {
try {
prefs.clear();
new SharedDatabasePreferences().clear();
JOptionPane.showMessageDialog(PreferencesDialog.this,
Localization.lang("You must restart JabRef for this to come into effect."),
Localization.lang("Reset preferences"), JOptionPane.WARNING_MESSAGE);
} catch (BackingStoreException ex) {
LOGGER.warn(ex.getMessage(), ex);
JOptionPane.showMessageDialog(PreferencesDialog.this, ex.getLocalizedMessage(),
Localization.lang("Reset preferences"), JOptionPane.ERROR_MESSAGE);
}
updateAfterPreferenceChanges();
}
});
setValues();
pack();
}
private JScrollPane putPanelInScrollPane(JPanel panel) {
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
scrollPane.setBorder(null);
return scrollPane;
}
private String getPrefsExportPath() {
return Globals.prefs.get(JabRefPreferences.PREFS_EXPORT_PATH);
}
private void updateAfterPreferenceChanges() {
setValues();
Map<String, ExportFormat> customFormats = Globals.prefs.customExports.getCustomExportFormats(Globals.prefs,
Globals.journalAbbreviationLoader);
LayoutFormatterPreferences layoutPreferences = Globals.prefs
.getLayoutFormatterPreferences(Globals.journalAbbreviationLoader);
SavePreferences savePreferences = SavePreferences.loadForExportFromPreferences(Globals.prefs);
ExportFormats.initAllExports(customFormats, layoutPreferences, savePreferences);
Globals.prefs.updateEntryEditorTabList();
}
private void storeAllSettings() {
// First check that all tabs are ready to close:
Component[] preferenceTabs = main.getComponents();
for (Component tab : preferenceTabs) {
if (!((PrefsTab) tab).validateSettings()) {
return; // If not, break off.
}
}
// Then store settings and close:
for (Component tab : preferenceTabs) {
((PrefsTab) tab).storeSettings();
}
Globals.prefs.flush();
setVisible(false);
MainTable.updateRenderers();
GUIGlobals.updateEntryEditorColors();
frame.setupAllTables();
frame.getGroupSelector().revalidateGroups(); // icons may have changed
frame.output(Localization.lang("Preferences recorded."));
}
class OkAction extends AbstractAction {
public OkAction() {
super("OK");
}
@Override
public void actionPerformed(ActionEvent e) {
storeAllSettings();
}
}
class ExportAction extends AbstractAction {
public ExportAction() {
super("Export");
}
@Override
public void actionPerformed(ActionEvent e) {
FileDialogConfiguration fileDialogConfiguration = new FileDialogConfiguration.Builder()
.addExtensionFilter(FileExtensions.XML)
.withDefaultExtension(FileExtensions.XML)
.withInitialDirectory(Globals.prefs.get(JabRefPreferences.WORKING_DIRECTORY)).build();
DialogService ds = new FXDialogService();
Optional<Path> path = DefaultTaskExecutor
.runInJavaFXThread(() -> ds.showFileSaveDialog(fileDialogConfiguration));
path.ifPresent(exportFile -> {
try {
storeAllSettings();
Globals.prefs.exportPreferences(exportFile.toString());
Globals.prefs.put(JabRefPreferences.PREFS_EXPORT_PATH, exportFile.toString());
} catch (JabRefException ex) {
LOGGER.warn(ex.getMessage(), ex);
JOptionPane.showMessageDialog(PreferencesDialog.this, ex.getLocalizedMessage(),
Localization.lang("Export preferences"), JOptionPane.WARNING_MESSAGE);
}
});
}
}
public void setValues() {
// Update all field values in the tabs:
int count = main.getComponentCount();
Component[] comps = main.getComponents();
for (int i = 0; i < count; i++) {
((PrefsTab) comps[i]).setValues();
}
}
class CancelAction extends AbstractAction {
public CancelAction() {
super("Cancel");
}
@Override
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
}
}