package context.arch.intelligibility.presenters;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashMap;
import java.util.Map;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.SpringLayout;
import javax.swing.SwingConstants;
import javax.swing.layout.SpringUtilities;
import context.arch.intelligibility.DescriptiveExplainerDelegate;
import context.arch.intelligibility.Explainer;
import context.arch.intelligibility.expression.Parameter;
import context.arch.intelligibility.expression.Reason;
import context.arch.intelligibility.query.Query;
import context.arch.intelligibility.query.WhatIfQuery;
import context.arch.intelligibility.reducers.ConjunctionReducer;
import context.arch.storage.AttributeNameValue;
/**
* GUI class to provide a UI for obtaining a What-If query from the user.
* Set it up by constructing, and setting the initial input values (via {@link #setInputs(Reason)}
* and when this UI is visible, it will show the inputs and their pre-set values.
* Users can change the values by typing (no type safety checks), and it would highlight if
* the values are changed from the initial values. To get the updated input values, call
* {@link #getInputs()}. To construct a query from these values, call
* {@link #getWhatIfQuery(String, long)}.
* @author Brian Y. Lim
* @see WhatIfQuery
*/
public class WhatIfPanel extends JPanel {
private static final long serialVersionUID = 1L;
private String context;
private DescriptiveExplainerDelegate descExplainer;
private JPanel inputsPanel;
private JScrollPane inputsWrapper;
private JButton askButton;
protected Map<Parameter<?>, JTextField> inputFields;
private ConjunctionReducer reducer;
/**
* Create the WhatIfPanel UI. Note that {@link #setInputs(Reason)} needs to be called before
* this can properly render.
* @param context the name of the attribute or output that the query will be about
* @param reducer for reducing which inputs to show for the user to manipulate; can be null
* @param descExplainer for providing pretty names to input labels
* @param listener for listening to when the Ask button is pressed. Then this would be a good time
* to get the changed input values or get the constructed What-If query.
*/
public WhatIfPanel(String context, ConjunctionReducer reducer, DescriptiveExplainerDelegate descExplainer, ActionListener listener) {
super();
setLayout(new BorderLayout());
this.context = context;
this.descExplainer = descExplainer;
inputsPanel = new JPanel();
inputsWrapper = new JScrollPane(inputsPanel);
add(inputsWrapper, BorderLayout.CENTER);
askButton = new JButton("Ask");
askButton.addActionListener(listener);
add(askButton, BorderLayout.SOUTH);
inputFields = new HashMap<Parameter<?>, JTextField>();
this.reducer = reducer;
this.setPreferredSize(new Dimension(300, 360));
}
/**
* Set initial, or pre-set input values to show first.
* Normally, this is just the result from {@link Explainer#getInputsExplanation()}
* @param inputs
*/
public void setInputs(Reason inputs) {
if (reducer != null) {
System.out.println("pre inputs = " + inputs);
inputs = reducer.apply(inputs); // reduce first
System.out.println("post inputs = " + inputs);
}
refreshAttributesPanel(inputs);
}
/**
* Gets the current values of the inputs, which may have been manipulated by the user.
* @return
*/
public Reason getInputs() {
Reason inputs = new Reason();
// read from GUI and set into new attribute values
for (Parameter<?> input : inputFields.keySet()) {
String valueStr = inputFields.get(input).getText();
inputs.add(Parameter.instance(
input.getName(),
AttributeNameValue.valueOf(input.getType(), valueStr) // parse value from text field to type of input
));
}
return inputs;
}
/**
* Gets a constructed {@link WhatIfQuery} from the input values.
* @param timestamp the time to query about
* @return
*/
public Query getWhatIfQuery(long timestamp) {
return new WhatIfQuery(WhatIfQuery.QUESTION_WHAT_IF, context,
getInputs(),
timestamp);
}
/**
* Called to update the panel displaying the inputs and text fields,
* depending on how many inputs there are, and their values.
* @param inputs
*/
private void refreshAttributesPanel(Reason inputs) {
// remove old
remove(inputsWrapper);
inputFields.clear();
inputsPanel = new JPanel();
inputsPanel.setLayout(new SpringLayout());
int num = 0;
for (final Parameter<?> input : inputs) {
String context = input.getName();
final String value = input.getValue().toString();
JLabel label = new JLabel(descExplainer.getPrettyName(context) + ":", SwingConstants.TRAILING);
inputsPanel.add(label);
JLabel icon = new JLabel(ContextIcons.get(context, value));
inputsPanel.add(icon);
final JTextField valueField = new JTextField(value, 6);
//valueField.setEditable(editable);
valueField.addKeyListener(new KeyAdapter() {
@Override
public void keyReleased(KeyEvent e) {
/*
* check if value changed, if so, then highlight it
*/
if (!valueField.getText().equals(value)) {
valueField.setBackground(Color.orange);
}
else {
valueField.setBackground(Color.white);
}
}
});
inputFields.put(input, valueField);
inputsPanel.add(valueField);
num++;
}
SpringUtilities.makeCompactGrid(inputsPanel,
num, 3, // rows, cols
6, 6, // initX, initY
6, 6); // xPad, yPad
// add new in old's place
inputsWrapper = TablePanelPresenter.scrollbarWrap(inputsPanel);
add(inputsWrapper, BorderLayout.CENTER);
//revalidate();
}
}