refs #219 Present a dialog that allows per-unit selection for multidimensional units

This commit is contained in:
Derek Bankieris 2016-04-11 10:06:51 -05:00
parent 0da1ba7653
commit eee36193ea
4 changed files with 234 additions and 103 deletions

View File

@ -78,13 +78,13 @@ public class StripChart extends JXPanel {
double fixedAutoRange;
/** a combo box to hold variables available for adding to the strip chart */
JComboBox addComboBox = new JComboBox();
ComboBox<Variable<?>> addComboBox = new ComboBox<>();
/** a combo box to hold variables available for removing from the strip chart */
JComboBox removeComboBox = new JComboBox();
ComboBox<Variable<?>> removeComboBox = new ComboBox<>();
/** a combo box to hold variables available for specifying as the independent variable */
JComboBox domainComboBox = new JComboBox();
ComboBox<Variable<?>> domainComboBox = new ComboBox<>();
/**
* constructor
@ -101,8 +101,8 @@ public class StripChart extends JXPanel {
*/
// Until JFreeChart fixes setBaseShape, we must use the deprecated setShape.
@SuppressWarnings("deprecation")
StripChart(StripChartManager stripChartManager, final Variable domainVariable,
Collection<Variable> rangeVariables, Collection<Variable> allVariables,
StripChart(StripChartManager stripChartManager, final Variable<?> domainVariable,
Collection<? extends Variable<?>> rangeVariables, Collection<Variable<?>> allVariables,
final Mode mode, double autoRange, final boolean linesVisible,
final boolean pointsVisible, final boolean legendVisible) {
@ -323,7 +323,7 @@ public class StripChart extends JXPanel {
putValue(SHORT_DESCRIPTION, "Add the selected variable.");
}
public void actionPerformed(ActionEvent event) {
chartVariable((Variable)(addComboBox.getSelectedItem()));
chartVariable(addComboBox.getSelectedItem());
}
}), constraints);
@ -333,7 +333,7 @@ public class StripChart extends JXPanel {
putValue(SHORT_DESCRIPTION, "Remove the selected variable.");
}
public void actionPerformed(ActionEvent event) {
removeVariable((Variable)(removeComboBox.getSelectedItem()));
removeVariable(removeComboBox.getSelectedItem());
}
}), constraints);
@ -353,15 +353,15 @@ public class StripChart extends JXPanel {
domainComboBox.addActionListener(new ActionListener() {
Variable oldSelection;
Variable<?> oldSelection;
public void actionPerformed(ActionEvent event) {
Variable newSelection = (Variable)domainComboBox.getSelectedItem();
Variable<?> newSelection = domainComboBox.getSelectedItem();
if (newSelection != oldSelection) {
oldSelection = newSelection;
dataSet.removeAllSeries();
for (int i = 0; i < removeComboBox.getItemCount(); ++i) {
chartVariable((Variable)(removeComboBox.getItemAt(i)));
chartVariable(removeComboBox.getItemAt(i));
}
domainAxis.setLabel(newSelection != null ? newSelection.toString() : "");
}
@ -372,13 +372,13 @@ public class StripChart extends JXPanel {
setMode(mode);
for (Variable variable : allVariables) {
for (Variable<?> variable : allVariables) {
addVariable(variable);
}
domainComboBox.setSelectedItem(domainVariable);
for (Variable variable : rangeVariables) {
for (Variable<?> variable : rangeVariables) {
chartVariable(variable);
}
}
@ -453,7 +453,7 @@ public class StripChart extends JXPanel {
*
* @param variable the varible to be added
*/
void addVariable(Variable variable) {
void addVariable(Variable<?> variable) {
addToComboBox(addComboBox, variable);
addToComboBox(domainComboBox, variable);
}
@ -463,9 +463,9 @@ public class StripChart extends JXPanel {
*
* @param variable the varible to be charted
*/
void chartVariable(Variable variable) {
void chartVariable(Variable<?> variable) {
if (variable != null) {
dataSet.addSeries(stripChartManager.getPair((Variable)domainComboBox.getSelectedItem(), variable).getSeries());
dataSet.addSeries(stripChartManager.getPair(domainComboBox.getSelectedItem(), variable).getSeries());
addComboBox.removeItem(variable);
addToComboBox(removeComboBox, variable);
}
@ -476,7 +476,7 @@ public class StripChart extends JXPanel {
*
* @param variable the varible to be removed
*/
void removeVariable(Variable variable) {
void removeVariable(Variable<?> variable) {
if (variable != null) {
removeVariableFromDataSet(variable);
addToComboBox(addComboBox, variable);
@ -489,7 +489,7 @@ public class StripChart extends JXPanel {
*
* @param variable the varible to be removed
*/
void removeVariableFromDataSet(Variable variable) {
void removeVariableFromDataSet(Variable<?> variable) {
for (int i = 0; i < dataSet.getSeriesCount(); ++i) {
if (dataSet.getSeriesKey(i).equals(variable.toString())) {
dataSet.removeSeries(i);
@ -500,7 +500,7 @@ public class StripChart extends JXPanel {
public void deleteAllVariables() {
for (int i = domainComboBox.getItemCount(); i >= 0; --i) {
deleteVariable((Variable)domainComboBox.getItemAt(i));
deleteVariable(domainComboBox.getItemAt(i));
}
}
@ -509,7 +509,7 @@ public class StripChart extends JXPanel {
*
* @param variable the varible to be deleted
*/
public void deleteVariable(Variable variable) {
public void deleteVariable(Variable<?> variable) {
removeVariableFromDataSet(variable);
addComboBox.removeItem(variable);
removeComboBox.removeItem(variable);
@ -542,10 +542,10 @@ public class StripChart extends JXPanel {
*
* @return the variables currently being plotted
*/
public ArrayList<Variable> getPlottedVariables() {
ArrayList<Variable> variables = new ArrayList<Variable>();
public ArrayList<Variable<?>> getPlottedVariables() {
ArrayList<Variable<?>> variables = new ArrayList<>();
for (int i = 0; i < removeComboBox.getItemCount(); ++i) {
variables.add((Variable)removeComboBox.getItemAt(i));
variables.add(removeComboBox.getItemAt(i));
}
return variables;
}
@ -591,8 +591,8 @@ public class StripChart extends JXPanel {
*
* @return the current domain variable
*/
public Variable getDomainVariable() {
return (Variable)domainComboBox.getSelectedItem();
public Variable<?> getDomainVariable() {
return domainComboBox.getSelectedItem();
}
/**
@ -690,4 +690,22 @@ public class StripChart extends JXPanel {
}
/**
* JComboBox.getSelectedItem() is not generic, returning an Object instead of E.
* The reason for this is that an editable combo box allows you to type a string, which cannot
* be constrained to the generic type and may be returned by getSelectedItem(), directly into
* the component. However, getItemAt(getSelectedIndex()) will always return an E. If the
* selected item is a custom string in an editable combo box, the return value is null. We're
* not using editable combo boxes in this file, so this class relieves us of having to type
* comboBox.getItemAt(comboBox.getSelectedIndex()) in place of comboBox.getSelectedItem().
*/
private class ComboBox<E> extends JComboBox<E> {
@Override
public E getSelectedItem() {
return getItemAt(getSelectedIndex());
}
}
}

View File

@ -16,7 +16,7 @@ import trick.common.utils.vs.Variable;
public class StripChartManager {
/** variables whose historical values are being logged */
HashMap<Variable, ValueLog> valueLogs = new HashMap<Variable, ValueLog>();
HashMap<Variable<?>, ValueLog> valueLogs = new HashMap<Variable<?>, ValueLog>();
/** variables being logged with respect to another variable */
HashMap<PairKey, VariablePair> variablePairs = new HashMap<PairKey, VariablePair>();
@ -34,8 +34,8 @@ public class StripChartManager {
*
* @param variables the variables to be recorded
*/
public void addVariables(Collection<Variable> variables) {
for (Variable variable : variables) {
public void addVariables(Collection<Variable<?>> variables) {
for (Variable<?> variable : variables) {
addVariable(variable);
}
}
@ -47,7 +47,7 @@ public class StripChartManager {
*
* @param variable the variable to be recorded
*/
public void addVariable(Variable variable) {
public void addVariable(Variable<?> variable) {
if (!valueLogs.containsKey(variable)) {
try {
Double.parseDouble(variable.getValue().toVariableServer());
@ -92,7 +92,7 @@ public class StripChartManager {
* participating in and removes it from the strip charts
* @param variable the variable to remove
*/
public void removeVariable(Variable variable) {
public void removeVariable(Variable<?> variable) {
valueLogs.remove(variable);
// Copy the key set so we don't modify the iterator, producing a ConcurrentModificationException.
@ -113,7 +113,7 @@ public class StripChartManager {
* @param rangeVariable not sure
* @return a variable pair for the given arguments
*/
public VariablePair getPair(Variable domainVariable, Variable rangeVariable) {
public VariablePair getPair(Variable<?> domainVariable, Variable<?> rangeVariable) {
PairKey pairKey = new PairKey(domainVariable, rangeVariable);
if (variablePairs.containsKey(pairKey)) {
return variablePairs.get(pairKey);
@ -148,7 +148,7 @@ public class StripChartManager {
*
* @return a new strip chart
*/
public StripChart createStripChart(Variable domainVariable, Collection<Variable> rangeVariables) {
public StripChart createStripChart(Variable<?> domainVariable, Collection<? extends Variable<?>> rangeVariables) {
return createStripChart(domainVariable, rangeVariables, StripChart.Mode.Strip, 30, true, false, true);
}
@ -165,13 +165,13 @@ public class StripChartManager {
*
* @return a new strip chart
*/
public StripChart createStripChart(Variable domainVariable, Collection<Variable> rangeVariables,
public StripChart createStripChart(Variable<?> domainVariable, Collection<? extends Variable<?>> rangeVariables,
StripChart.Mode mode, double fixedAutoRange, boolean linesVisible, boolean pointsVisible,
boolean legendVisible) {
// Ensure all specified variables have already been added.
addVariable(domainVariable);
for (Variable variable : rangeVariables) {
for (Variable<?> variable : rangeVariables) {
addVariable(variable);
}
@ -216,7 +216,7 @@ public class StripChartManager {
static class ValueLog {
/** the variable being logged */
Variable variable;
Variable<?> variable;
/** the log of values */
ArrayList<Double> values = new ArrayList<Double>();
@ -230,7 +230,7 @@ public class StripChartManager {
* @param variable the variable to be logged
* @param logStart the manager count at which this log started
*/
public ValueLog(Variable variable, int logStart) {
public ValueLog(Variable<?> variable, int logStart) {
this.variable = variable;
this.logStart = logStart;
}
@ -256,7 +256,7 @@ public class StripChartManager {
*
* @return the variable being logged
*/
public Variable getVariable() {
public Variable<?> getVariable() {
return variable;
}
@ -305,10 +305,10 @@ public class StripChartManager {
static class PairKey {
/** the independent variable */
Variable domainVariable;
Variable<?> domainVariable;
/** the dependent variable */
Variable rangeVariable;
Variable<?> rangeVariable;
/**
* constructor
@ -316,7 +316,7 @@ public class StripChartManager {
* @param domainVariable the independent variable
* @param rangeVariable the depdendent variable
*/
public PairKey(Variable domainVariable, Variable rangeVariable) {
public PairKey(Variable<?> domainVariable, Variable<?> rangeVariable) {
this.domainVariable = domainVariable;
this.rangeVariable = rangeVariable;
}

View File

@ -462,17 +462,24 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
// Initialize the variable table.
variableTable = new VariableTable() {
JPopupMenu popupMenu = new JPopupMenu() {{
class VariablePopupMenu extends JPopupMenu {
/** the variable associated with this popup menu */
Variable variable;
}
VariablePopupMenu popupMenu = new VariablePopupMenu() {{
add(new JMenuItem(new AbstractAction() {
{
putValue(NAME, "Expand Parent in Tree");
}
public void actionPerformed(ActionEvent actionEvent) {
variableTree.expandParent(getLabel());
if (variable != null) {
variableTree.expandParent(variable.name);
}
}
}));
}};
{
setName("variableTable");
setFont(getFont().deriveFont(Float.parseFloat(trickProperties.getProperty(
@ -500,7 +507,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
}
row = getSelectedRow();
popupMenu.setLabel(row == -1 ? "No Selection" : (getValueAt(row, 0)).toString());
ArrayList<? extends Variable> selectedVariables = getSelectedVariables();
popupMenu.variable = selectedVariables.isEmpty() ? null : selectedVariables.get(0);
return super.getPopupLocation(event);
}
@ -687,8 +695,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
JComboBox comboBox = new JComboBox() {{
setToolTipText(
"<html>" +
"Order in which the Variable<br>" +
"<html>" +
"Order in which the Variable<br>" +
"Tree will be sorted." +
"</html>"
);
@ -735,8 +743,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
JComboBox comboBox = new JComboBox(Position.values()) {{
setToolTipText(
"<html>" +
"Position at which new variables will<br>" +
"<html>" +
"Position at which new variables will<br>" +
"be added to the variable table." +
"</html>"
);
@ -744,7 +752,7 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
JCheckBox asStringsCheckBox = new JCheckBox("char[] as string") {{
setToolTipText(
"<html>" +
"<html>" +
"Treat character arrays as strings when<br>" +
"adding them to the variable table." +
"</html>"
@ -840,8 +848,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
float fontSize = Float.parseFloat(fontSizeField.getText());
if (fontSize < 0) {
throw new NumberFormatException("Font size must be non-negative.");
}
}
}
}
catch (NumberFormatException numberFormatException) {
JOptionPane.showMessageDialog(getMainFrame(), numberFormatException,
"Invalid Value", JOptionPane.ERROR_MESSAGE);
@ -907,8 +915,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
setTitleJustification(CENTER);
setToolTipText(
"<html>" +
"Specify the units in which newly<br>" +
"added variables will be displayed." +
"Specify the units in which newly<br>" +
"added variables will be displayed." +
"</html>");
}});
@ -950,7 +958,7 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
}
});
}
}, constraints);
settingsDialog.addPanel(new JXPanel(new GridBagLayout()) {
@ -968,8 +976,8 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
setTitleJustification(CENTER);
setToolTipText(
"<html>" +
"Specify the format in which newly<br>" +
"added variables will be displayed." +
"Specify the format in which newly<br>" +
"added variables will be displayed." +
"</html>");
}});
@ -1784,7 +1792,7 @@ public class TVApplication extends RunTimeTrickApplication implements VariableLi
}
}
ArrayList<Variable> plottedVariables = new ArrayList<Variable>();
ArrayList<Variable<?>> plottedVariables = new ArrayList<>();
for (String plottedVariable : stripChartBean.plottedVariables) {
for (Variable variable : tvBean.variables) {
if (variable.name.equals(plottedVariable)) {

View File

@ -1,11 +1,17 @@
package trick.tv;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.Dialog.ModalityType;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@ -14,16 +20,24 @@ import java.util.HashSet;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.border.EmptyBorder;
import javax.swing.Box;
import javax.swing.DefaultCellEditor;
import javax.swing.DropMode;
import javax.swing.JComboBox;
import javax.swing.JComponent;
import javax.swing.JDialog;
import javax.swing.JRootPane;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import javax.swing.TransferHandler;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import org.jdesktop.swingx.JXButton;
import org.jdesktop.swingx.JXLabel;
import org.jdesktop.swingx.JXPanel;
import org.jdesktop.swingx.JXTable;
import org.jdesktop.swingx.decorator.ColorHighlighter;
import org.jdesktop.swingx.decorator.HighlighterFactory;
@ -45,11 +59,10 @@ public class VariableTable extends JXTable {
public enum Position {Top, Before, After, Bottom};
/** variables being tracked */
ArrayList<Variable<? extends TrickViewFluent>> variables =
new ArrayList<Variable<? extends TrickViewFluent>>();
ArrayList<Variable<? extends TrickViewFluent>> variables = new ArrayList<>();
/** variables being tracked */
HashSet<Variable> variablesHashSet = new HashSet<Variable>(variables.size());
HashSet<Variable<? extends TrickViewFluent>> variablesHashSet = new HashSet<>(variables.size());
/** variable server to which to send values changes */
VariableServerConnection variableServerConnection;
@ -339,8 +352,8 @@ public class VariableTable extends JXTable {
*
* @return the selected variables
*/
public ArrayList<Variable> getSelectedVariables() {
ArrayList<Variable> selectedVariables = new ArrayList<Variable>();
public ArrayList<Variable<? extends TrickViewFluent>> getSelectedVariables() {
ArrayList<Variable<? extends TrickViewFluent>> selectedVariables = new ArrayList<>();
int[] rowIndices = getSelectedRows();
for (int i = 0; i < rowIndices.length; ++i) {
@ -410,10 +423,15 @@ public class VariableTable extends JXTable {
case 1:
return value.getCellEditor();
case 2:
return new DefaultCellEditor(new JComboBox(UnitType.getAll(
variable.getUnits()).toArray()) {{
setSelectedItem(variable.getUnits());
}});
if (hasComplexUnits(variable)) {
selectUnitsIndividually(variable);
return null;
}
else {
return new DefaultCellEditor(new JComboBox(UnitType.getAll(variable.getUnits()).toArray()) {{
setSelectedItem(variable.getUnits());
}});
}
case 3:
return new DefaultCellEditor(new JComboBox(EnumSet.allOf(value.getFormatClass()).toArray()) {{
setSelectedItem(value.getFormat());
@ -421,6 +439,125 @@ public class VariableTable extends JXTable {
}
}
public boolean hasComplexUnits(Variable variable) {
String units = variable.getUnits();
return units.contains("*") || units.contains("/");
}
/**
* presents a dialog for specifying each individual dimension of a compound unit
*
* @param variable the variable whose units to set
*/
private void selectUnitsIndividually(Variable variable) {
if (variable != null) {
String afterMathSymbols = "(?<=[*/])|";
String beforeNumbers = "(?=\\d)|";
String beforeMathSymbolsNotPreceededByNumbers = "(?<!\\d)(?=[*/])";
String[] tokens = variable.getUnits().split(afterMathSymbols + beforeNumbers + beforeMathSymbolsNotPreceededByNumbers);
new JDialog(SwingUtilities.getWindowAncestor(this), "Select Units", ModalityType.APPLICATION_MODAL) {{
ArrayList<JComboBox<String>> comboBoxes = new ArrayList<>();
ArrayList<String> delimiters = new ArrayList<>();
final JXButton okButton = new JXButton(new AbstractAction("OK") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_O);
}
public void actionPerformed(ActionEvent actionEvent) {
String result = "";
for (int i = 0; i < comboBoxes.size(); ++i) {
result += comboBoxes.get(i).getSelectedItem();
if (i < delimiters.size()) {
result += delimiters.get(i);
}
}
setUnits(getSelectedVariables(), result);
setVisible(false);
}
});
final AbstractAction cancelAction = new AbstractAction("Cancel") {
{
putValue(MNEMONIC_KEY, KeyEvent.VK_C);
}
public void actionPerformed(ActionEvent e) {
setVisible(false);
}
};
setContentPane(new JXPanel(new BorderLayout()) {{
setBorder(new EmptyBorder(5, 5, 5, 5));
add(new JXPanel(new GridBagLayout()) {{
GridBagConstraints constraints = new GridBagConstraints() {{
gridy = 1;
fill = BOTH;
insets = new Insets(5, 2, 5, 2);
}};
boolean unit = true;
for (String token : tokens) {
if (unit) {
JComboBox<String> comboBox = new JComboBox<String>(
UnitType.getAll(token).toArray(new String[0])) {{
setSelectedItem(token);
}};
comboBoxes.add(comboBox);
add(comboBox, constraints);
}
else {
delimiters.add(token);
add(new JXLabel(token), constraints);
}
unit = !unit;
}
}}, BorderLayout.CENTER);
add(new JXPanel(new GridLayout(1, 4)) {{
add(Box.createHorizontalGlue());
add(okButton);
add(new JXButton(cancelAction));
add(Box.createHorizontalGlue());
}}, BorderLayout.SOUTH);
}});
JRootPane rootPane = getRootPane();
rootPane.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(
KeyStroke.getKeyStroke(KeyEvent.VK_ESCAPE, 0), "cancel");
rootPane.getActionMap().put("cancel", cancelAction);
rootPane.setDefaultButton(okButton);
pack();
setLocationRelativeTo(VariableTable.this);
}}.setVisible(true);
}
}
/**
* sets the units of all <code>variables</code> to the result of <code>units.toString()</code>.
* Illegal conversions are silently ignored.
*
* @param variables the variables whose units to set
* @param units the new units
*/
private void setUnits(ArrayList<? extends Variable> variables, Object units) {
for (Variable variable : variables) {
for (String string : UnitType.getAll(variable.getUnits())) {
if (string.equals(units)) {
try {
variable.sendUnitsToVariableServer(units.toString(),
variableServerConnection);
}
catch (IOException ioException) {
System.err.println("Failed to set variable \"" +
variable + "\" units to \"" + units + "\"");
ioException.printStackTrace(System.err);
}
break;
}
}
}
}
/**
* the model for this table
*/
@ -479,34 +616,21 @@ public class VariableTable extends JXTable {
@Override
public void setValueAt(Object value, int rowIndex, int columnIndex) {
final int[] rowIndices = getSelectedRows();
// Translate the view indicies to the model indicies.
for (int i = 0; i < rowIndices.length; i++) {
rowIndices[i] = convertRowIndexToModel(rowIndices[i]);
}
ArrayList<Variable<? extends TrickViewFluent>> selectedVariables =
new ArrayList<Variable<? extends TrickViewFluent>>() {{
for (int i : rowIndices) {
add(variables.get(i));
}
}};
switch (columnIndex) {
case 0:
nameChangeAction.actionPerformed(new ActionEvent(this, 0, value.toString()));
break;
case 1:
setValues(selectedVariables, value);
setValues(getSelectedVariables(), value);
break;
case 2:
setUnits(selectedVariables, value);
setUnits(getSelectedVariables(), value);
break;
case 3:
setFormat(selectedVariables, value);
for (int i : rowIndices) {
model.fireTableRowsUpdated(i, i);
setFormat(getSelectedVariables(), value);
for (int row : getSelectedRows()) {
int index = convertRowIndexToModel(row);
model.fireTableRowsUpdated(index, index);
}
break;
}
@ -530,25 +654,6 @@ public class VariableTable extends JXTable {
}
}
private void setUnits(ArrayList<? extends Variable> variables, Object units) {
for (Variable variable : variables) {
for (String string : UnitType.getAll(variable.getUnits())) {
if (string.equals(units)) {
try {
variable.sendUnitsToVariableServer(units.toString(),
variableServerConnection);
}
catch (IOException ioException) {
System.err.println("Failed to set variable \"" +
variable + "\" units to \"" + units + "\"");
ioException.printStackTrace(System.err);
}
break;
}
}
}
}
@SuppressWarnings("unchecked")
private void setFormat(ArrayList<Variable<? extends TrickViewFluent>> variables, Object format) {
for (Variable<? extends TrickViewFluent> variable : variables) {