package org.rr.commons.swing.dialogs;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.JTabbedPane;
import javax.swing.KeyStroke;
import javax.swing.border.TitledBorder;
import javax.swing.text.JTextComponent;
import org.rr.commons.swing.SwingUtils;
import org.rr.commons.swing.layout.EqualsLayout;
import org.rr.commons.utils.BooleanUtils;
import org.rr.commons.utils.CommonUtils;
import org.rr.commons.utils.StringUtil;
public class JPreferenceDialog extends JDialog {
private static final Dimension DEFAULT_SIZE = new Dimension(450,250);
public static final int ACTION_RESULT_OK = 0;
public static final int ACTION_RESULT_ABORT = 1;
private final LinkedHashMap<String, PreferenceEntry> preferenceEntries = new LinkedHashMap<String, PreferenceEntry>();
private final ActionListener okAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
actionResult = ACTION_RESULT_OK;
setVisible(false);
dispose();
}
};
private final ActionListener abortAction = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
actionResult = ACTION_RESULT_ABORT;
setVisible(false);
dispose();
}
};
private int actionResult = -1;
private boolean isInitialized = false;
public JPreferenceDialog(JFrame owner) {
super(owner);
setModal(true);
SwingUtils.centerOnScreen(this);
setSize(DEFAULT_SIZE);
setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
setTitle(Bundle.getString("PreferenceView.title"));
}
/**
* Adds a {@link PreferenceEntry} to this {@link JPreferenceDialog} instance.
* The order of the {@link PreferenceEntry} are taken under account for
* the order of the components shown to the user.
*
* @param entry The {@link PreferenceEntry} to be added.
*/
public void addPreferenceEntry(PreferenceEntry entry) {
preferenceEntries.put(entry.getName(), entry);
}
/**
* Gets the action result of this {@link JPreferenceDialog} instance.
* @return The action result. This is ACTION_RESULT_OK or ACTION_RESULT_ABORT.
*/
public int getActionResult() {
return this.actionResult;
}
/**
* Get all preference entries
*/
public List<PreferenceEntry> getPreferenceEntries() {
Collection<PreferenceEntry> values = preferenceEntries.values();
return new ArrayList<PreferenceEntry>(values);
}
/**
* Get the preference entry with the given name.
*/
public PreferenceEntry getPreferenceEntry(final String name) {
final PreferenceEntry preferenceEntry = preferenceEntries.get(name);
return preferenceEntry;
}
/**
* Get the result value for the {@link PreferenceEntry} with the given name.
*/
public boolean getBooleanValue(final String name) {
final PreferenceEntry preferenceEntry = preferenceEntries.get(name);
if(preferenceEntry != null) {
final Component component = preferenceEntry.getCustomComponent();
if(component instanceof JCheckBox) {
return ((JCheckBox)component).isSelected();
} else if(component instanceof JTextComponent) {
String text = ((JTextComponent)component).getText();
Boolean booleanValue = BooleanUtils.toBoolean(text);
return booleanValue.booleanValue();
} else if(component instanceof JComboBox) {
String text = StringUtil.toString( ((JComboBox)component).getSelectedItem() );
Boolean booleanValue = BooleanUtils.toBoolean(text);
return booleanValue.booleanValue();
}
}
throw new IllegalArgumentException("Invalid component '" + name + "'");
}
/**
* Get the result value for the {@link PreferenceEntry} with the given name.
*/
public String getStringValue(final String name) {
final PreferenceEntry preferenceEntry = preferenceEntries.get(name);
if(preferenceEntry != null) {
final Component component = preferenceEntry.getCustomComponent();
if(component instanceof JCheckBox) {
return Boolean.valueOf(((JCheckBox)component).isSelected()).toString();
} else if(component instanceof JTextComponent) {
String text = ((JTextComponent)component).getText();
return text;
} else if(component instanceof JComboBox) {
String text = StringUtil.toString( ((JComboBox)component).getSelectedItem() );
return text;
} else if(component instanceof JSlider) {
String text = StringUtil.toString( ((JSlider)component).getValue() );
return text;
}
}
throw new IllegalArgumentException("Invalid component '" + name + "'");
}
/**
* Get the result value for the {@link PreferenceEntry} with the given name.
*/
public Number getNumericValue(final String name) {
final PreferenceEntry preferenceEntry = preferenceEntries.get(name);
if(preferenceEntry != null) {
final Component component = preferenceEntry.getCustomComponent();
if(component instanceof JCheckBox) {
return ((JCheckBox)component).isSelected() ? Integer.valueOf(1) : Integer.valueOf(0);
} else if(component instanceof JTextComponent) {
String text = ((JTextComponent)component).getText();
return CommonUtils.toNumber(text);
} else if(component instanceof JComboBox) {
return CommonUtils.toNumber( ((JComboBox)component).getSelectedItem() );
} else if(component instanceof JSlider) {
return Integer.valueOf( ((JSlider)component).getValue() );
}
}
throw new IllegalArgumentException("Invalid component '" + name + "'");
}
public void setVisible(boolean visible) {
this.initialize();
super.setVisible(visible);
}
private void initialize() {
if(!isInitialized) {
isInitialized = true;
final List<String> categories = getPreferenceCategories();
final ArrayList<JPanel> generalPanels = new ArrayList<>(categories.size());
getContentPane().setLayout(new BorderLayout());
((JComponent)getContentPane()).registerKeyboardAction(abortAction, KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), JComponent.WHEN_IN_FOCUSED_WINDOW);
for(final String category : categories) {
final JPanel generalPanel = new JPanel();
GridBagLayout gbl_generalPanel = new GridBagLayout();
gbl_generalPanel.columnWidths = new int[]{0, 0, 0, 0};
gbl_generalPanel.rowHeights = new int[]{0, 0};
gbl_generalPanel.columnWeights = new double[]{0.0, 0.0, 0.0, Double.MIN_VALUE};
gbl_generalPanel.rowWeights = new double[]{0.0, Double.MIN_VALUE};
generalPanel.setLayout(gbl_generalPanel);
generalPanel.setName(category);
generalPanels.add(generalPanel);
List<PreferenceEntry> preferenceEntriesByCategory = getPreferenceEntriesByCategory(category);
int preferenceEntriesSize = preferenceEntriesByCategory.size();
for(int i = 0; i < preferenceEntriesSize; i++) {
final PreferenceEntry preferenceEntry = preferenceEntriesByCategory.get(i);
JLabel label = new JLabel(preferenceEntry.getLabel());
GridBagConstraints gbc_label = new GridBagConstraints();
gbc_label.insets = new Insets(0, 3, 0, 5);
gbc_label.gridx = 0;
gbc_label.gridy = i;
gbc_label.anchor = GridBagConstraints.WEST;
generalPanel.add(label, gbc_label);
Component c = preferenceEntry.getCustomComponent();
GridBagConstraints gbc_component = new GridBagConstraints();
gbc_component.gridx = 2;
gbc_component.gridy = i;
gbc_component.anchor = GridBagConstraints.WEST;
if(!(c instanceof JCheckBox)) {
gbc_component.weightx = 1.0;
gbc_component.fill = GridBagConstraints.HORIZONTAL;
}
generalPanel.add(c, gbc_component);
}
//add a panel at the end which fills the rest of the space so the
//other components are located at the top of the dialog panel.
Component fillPanel = new JPanel();
GridBagConstraints gbc_fillPanel = new GridBagConstraints();
gbc_fillPanel.gridx = 2;
gbc_fillPanel.gridwidth = 3;
gbc_fillPanel.gridy = preferenceEntriesSize;
gbc_fillPanel.fill = GridBagConstraints.BOTH;
gbc_fillPanel.weighty = 1f;
generalPanel.add(fillPanel, gbc_fillPanel);
int minHeight = (preferenceEntriesSize * 30) + 80;
if(getSize().width == DEFAULT_SIZE.width && getSize().height == DEFAULT_SIZE.height) {
setSize(DEFAULT_SIZE.width, minHeight);
}
setMinimumSize(new Dimension(DEFAULT_SIZE.width, minHeight));
}
//add the category panels to the dialog root pane.
if(generalPanels.size() > 1) {
JTabbedPane tabbedPane = new JTabbedPane(JTabbedPane.TOP);
getContentPane().add(tabbedPane, BorderLayout.CENTER);
for(JPanel generalPanel : generalPanels) {
tabbedPane.addTab(generalPanel.getName(), null, generalPanel, null);
}
} else if(!generalPanels.isEmpty()) {
JPanel panel = new JPanel(new BorderLayout());
getContentPane().add(panel, BorderLayout.CENTER);
JPanel generalPanel = generalPanels.get(0);
panel.add(generalPanel, BorderLayout.CENTER);
if(!generalPanel.getName().isEmpty()) {
TitledBorder border = new TitledBorder(generalPanel.getName());
generalPanel.setBorder(border);
}
}
JPanel bottomPanel = new JPanel();
bottomPanel.setLayout(new EqualsLayout(1));
getContentPane().add(bottomPanel, BorderLayout.SOUTH);
JButton btnAbort = new JButton(Bundle.getString("PreferenceDialog.Action.Cancel"));
bottomPanel.add(btnAbort);
btnAbort.addActionListener(abortAction);
JButton btnOK = new JButton(Bundle.getString("PreferenceDialog.Action.OK"));
bottomPanel.add(btnOK);
btnOK.addActionListener(okAction);
getRootPane().setDefaultButton(btnOK);
}
}
/**
* Get all {@link PreferenceEntry} instances for the given category.
* @return The desired {@link PreferenceEntry} instances. Never returns <code>null</code>.
*/
private List<PreferenceEntry> getPreferenceEntriesByCategory(String category) {
ArrayList<PreferenceEntry> result = new ArrayList<JPreferenceDialog.PreferenceEntry>();
for(PreferenceEntry preferenceEntry : preferenceEntries.values()) {
if(StringUtil.toString(category).equals(StringUtil.toString(preferenceEntry.getCategory()))) {
result.add(preferenceEntry);
}
}
return result;
}
/**
* Get a list of existing categories. Empty/null categories are contained as empty {@link String} category.
*/
private List<String> getPreferenceCategories() {
ArrayList<String> result = new ArrayList<>();
for(PreferenceEntry preferenceEntry : preferenceEntries.values()) {
String category = StringUtil.toString(preferenceEntry.getCategory());
if(!result.contains(category)) {
result.add(category);
}
}
return result;
}
public static class PreferenceEntry {
private String category;
private String name;
private String label;
private Component customComponent;
private Object value;
private Runnable okAction;
public PreferenceEntry(String name, String label, Component customComponent) {
this.name = name;
this.label = label;
this.customComponent = customComponent;
}
public PreferenceEntry(String name, String label, Component customComponent, String category) {
this(name, label, customComponent, category, null);
}
public PreferenceEntry(String name, String label, Component customComponent, String category, Runnable okAction) {
this.name = name;
this.label = label;
this.category = category;
this.customComponent = customComponent;
this.okAction = okAction;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getLabel() {
return label;
}
public void setLabel(String label) {
this.label = label;
}
public Component getCustomComponent() {
return customComponent;
}
public void setCustomComponent(Component customComponent) {
this.customComponent = customComponent;
}
public String getCategory() {
return category;
}
public void setCategory(String category) {
this.category = category;
}
public Object getValue() {
return value;
}
public Runnable getOkAction() {
return okAction;
}
public void setOkAction(Runnable okAction) {
this.okAction = okAction;
}
}
}