package games.strategy.engine.framework.startup.ui.editors;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ItemEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import games.strategy.triplea.help.HelpSupport;
import games.strategy.triplea.ui.JButtonDialog;
/**
* Allows you put multiple beans in a list and use drop down to select which bean to configure.
* The bean's editor is displayed below the dropdown.
* Use <code>setBeans</code> to set the beans edited by this editor, and <code>setSelectedBean</code> to select a
* specific bean
* The editor automatically realigns the label of nested editors
*/
public class SelectAndViewEditor extends EditorPanel {
private static final long serialVersionUID = 1580648148539524876L;
JComboBox<IBean> m_selector = new JComboBox<>();
JPanel m_view = new JPanel();
JButton m_helpButton = new JButton("Help?");
private final PropertyChangeListener m_properChangeListener;
private EditorPanel m_editor;
private final JLabel m_selectorLabel;
private final JEditorPane m_helpPanel;
private final String m_defaultHelp;
/**
* creates a new editor.
*
* @param labelTitle
* the title in front of the combo box
* @param defaultHelp
* the name of the Help file to use when no bean is selected (when disabled)
*/
public SelectAndViewEditor(final String labelTitle, final String defaultHelp) {
super();
m_defaultHelp = defaultHelp;
final Font oldFont = m_helpButton.getFont();
m_helpButton.setFont(new Font(oldFont.getName(), Font.BOLD, oldFont.getSize()));
m_view.setLayout(new GridBagLayout());
m_selectorLabel = new JLabel(labelTitle + ":");
add(m_selectorLabel, new GridBagConstraints(0, 0, 1, 1, 0d, 0, GridBagConstraints.NORTHWEST,
GridBagConstraints.NONE, new Insets(0, 0, 1, 2), 0, 0));
add(m_selector, new GridBagConstraints(1, 0, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 1, 0), 0, 0));
add(m_helpButton, new GridBagConstraints(2, 0, 1, 1, 0d, 0, GridBagConstraints.NORTHEAST, GridBagConstraints.NONE,
new Insets(0, 0, 1, 0), 0, 0));
add(m_view, new GridBagConstraints(0, 1, 3, 1, 1.0, 1.0, GridBagConstraints.WEST, GridBagConstraints.HORIZONTAL,
new Insets(0, 0, 0, 0), 0, 0));
m_selector.setRenderer(new DisplayNameComboBoxRender());
m_selector.addItemListener(e -> {
if (e.getStateChange() == ItemEvent.SELECTED) {
updateView();
fireEditorChanged();
}
});
m_properChangeListener = evt -> fireEditorChanged();
m_helpPanel = new JEditorPane();
m_helpPanel.setEditable(false);
m_helpPanel.setContentType("text/html");
m_helpPanel.setAutoscrolls(true);
m_helpPanel.setBackground(m_selectorLabel.getBackground());
final Dimension preferredSize = new Dimension(500, 500);
m_helpPanel.setPreferredSize(preferredSize);
m_helpPanel.setSize(preferredSize);
final JScrollPane notesScroll = new JScrollPane();
notesScroll.setViewportView(m_helpPanel);
notesScroll.setBorder(null);
notesScroll.getViewport().setBorder(null);
m_helpButton.addActionListener(e -> {
String helpText;
if (getBean() == null) {
helpText = HelpSupport.loadHelp(m_defaultHelp);
} else {
helpText = getBean().getHelpText();
}
m_helpPanel.setText(helpText);
m_helpPanel.setCaretPosition(0);
JButtonDialog.showDialog(SelectAndViewEditor.this, "Help", notesScroll, "Close");
});
}
/**
* Updates the view panel below the combo box.
*/
private void updateView() {
// todo(kg) Have the View use a card layout instead of removing all content
// remove listeners from old editor, to avoid memory leak
if (m_editor != null) {
m_editor.removePropertyChangeListener(m_properChangeListener);
}
m_view.removeAll();
final IBean item = (IBean) m_selector.getSelectedItem();
m_editor = item.getEditor();
if (m_editor != null) {
// register a property change listener so we can re-notify our listeners
m_editor.addPropertyChangeListener(m_properChangeListener);
m_view.add(m_editor, new GridBagConstraints(0, 0, 1, 1, 1.0, 0, GridBagConstraints.NORTHWEST,
GridBagConstraints.HORIZONTAL, new Insets(0, 0, 0, 0), 0, 0));
m_editor.isBeanValid();
}
revalidate();
alignLabels();
}
/**
* Aligns label of this editor with the nested editor, by either resizing this (if it is smaller)
* or resizing the labels on the nested editor (if it is bigger).
*/
private void alignLabels() {
// resize the label to align with the nested editors labels
final int height = m_selectorLabel.getPreferredSize().height;
int width = m_selectorLabel.getPreferredSize().width;
if (m_editor != null) {
final int labelWidth = m_editor.getLabelWidth();
if (width < labelWidth) {
// resize this editors label
width = labelWidth;
} else {
// resize nested editors labels
m_editor.setLabelWidth(width);
}
}
final Dimension dimension = new Dimension(width, height);
m_selectorLabel.setPreferredSize(dimension);
m_selectorLabel.setSize(dimension);
}
/**
* Sets the list of possible beans to choose from.
*
* @param beans
* the list of beans
*/
public void setBeans(final List<? extends IBean> beans) {
m_selector.setModel(new DefaultComboBoxModel<>(beans.toArray(new IBean[beans.size()])));
updateView();
}
@Override
public boolean isBeanValid() {
return m_editor == null || m_editor.isBeanValid();
}
/**
* Returns the bean being edited.
*
* @return the current bean, or null if the bean doesn't have an editor (is disabled)
*/
@Override
public IBean getBean() {
if (m_editor == null) {
return null;
}
return m_editor.getBean();
}
/**
* Sets the bean on this editor.
* If an editor of the same class is found, it is selected an modified to match
* If no bean of this type is found, it is added to the list
*
* @param bean
* the bean
*/
public void setSelectedBean(final IBean bean) {
final DefaultComboBoxModel<IBean> model = (DefaultComboBoxModel<IBean>) m_selector.getModel();
final DefaultComboBoxModel<IBean> newModel = new DefaultComboBoxModel<>();
boolean found = false;
int i;
for (i = 0; i < model.getSize(); i++) {
final IBean candidate = model.getElementAt(i);
if (candidate.sameType(bean)) {
found = true;
newModel.addElement(bean);
} else {
newModel.addElement(candidate);
}
}
if (found) {
m_selector.setModel(newModel);
} else {
model.addElement(bean);
}
m_selector.setSelectedItem(bean);
updateView();
}
}