more bug fixes and data convertors added

git-svn-id: http://svn.3splooges.com/romraider-arch/trunk@214 d2e2e1cd-ba16-0410-be16-b7c4453c7c2d
This commit is contained in:
kascade 2006-08-12 12:52:42 +00:00
parent cda992eee1
commit b6bc2a3677
24 changed files with 456 additions and 141 deletions

Binary file not shown.

Binary file not shown.

View File

@ -5,12 +5,20 @@ import enginuity.logger.definition.EcuParameter;
import enginuity.logger.definition.EcuParameterImpl;
import enginuity.logger.definition.convertor.AcceleratorOpeningAngleConvertor;
import enginuity.logger.definition.convertor.AirFuelRatioLambdaConvertor;
import enginuity.logger.definition.convertor.EcuParameterConvertor;
import enginuity.logger.definition.convertor.EngineSpeedConvertor;
import enginuity.logger.definition.convertor.ExhaustGasTemperatureConvertor;
import enginuity.logger.definition.convertor.GenericTemperatureConvertor;
import enginuity.logger.definition.convertor.ThrottleOpeningAngleConvertor;
import enginuity.logger.query.LoggerCallback;
import static enginuity.util.HexUtil.asHex;
import enginuity.logger.ui.LoggerDataRow;
import enginuity.logger.ui.LoggerDataTableModel;
import enginuity.logger.ui.SpringUtilities;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.JFreeChart;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import javax.swing.*;
import static javax.swing.JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED;
@ -29,9 +37,10 @@ import java.util.List;
public final class EcuLogger extends JFrame implements WindowListener, PropertyChangeListener {
private final Settings settings = new Settings();
private final LoggerController CONTROLLER = new LoggerControllerImpl(settings);
private final JTable dataTable = new JTable();
private final JTextArea dataTextArea = new JTextArea();
private final LoggerDataTableModel dataTableModel = new LoggerDataTableModel();
private final JPanel graphPanel = new JPanel();
private final JComboBox portsComboBox = new JComboBox();
private int loggerCount = 0;
public EcuLogger(String title) {
super(title);
@ -49,98 +58,63 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
// add test address to log (0x000008 = coolant temp, 8bit)
final EcuParameter ecuParam1 = new EcuParameterImpl("Coolant Temperature", "Coolant temperature in degrees C", "0x000008", new GenericTemperatureConvertor());
CONTROLLER.addLogger(ecuParam1, new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
dataTextArea.append(ecuParam1.getName() + " (" + ecuParam1.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
+ " " + convertor.getUnits() + "\n");
}
});
registerEcuParameterForLogging(ecuParam1);
// add test address to log (0x000106 = EGT, 8bit)
final EcuParameter ecuParam2 = new EcuParameterImpl("EGT", "Exhaust gas temperature in degrees C", "0x000106", new ExhaustGasTemperatureConvertor());
CONTROLLER.addLogger(ecuParam2, new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
dataTextArea.append(ecuParam2.getName() + " (" + ecuParam2.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
+ " " + convertor.getUnits() + "\n");
}
});
registerEcuParameterForLogging(ecuParam2);
// add test address to log (0x000046 = air/fuel ratio, 8bit)
final EcuParameter ecuParam3 = new EcuParameterImpl("AFR", "Air/Fuel Ratio in Lambda", "0x000046", new AirFuelRatioLambdaConvertor());
CONTROLLER.addLogger(ecuParam3, new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
dataTextArea.append(ecuParam3.getName() + " (" + ecuParam3.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
+ " " + convertor.getUnits() + "\n");
}
});
registerEcuParameterForLogging(ecuParam3);
// add test address to log (0x000029 = accelerator opening angle, 8bit)
final EcuParameter ecuParam4 = new EcuParameterImpl("Accel Opening Angle", "Accelerator opening angle in %", "0x000029", new AcceleratorOpeningAngleConvertor());
CONTROLLER.addLogger(ecuParam4, new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
dataTextArea.append(ecuParam4.getName() + " (" + ecuParam4.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
+ " " + convertor.getUnits() + "\n");
}
});
registerEcuParameterForLogging(ecuParam4);
// add test address to log (0x000015 = accelerator opening angle, 8bit)
final EcuParameter ecuParam5 = new EcuParameterImpl("Throttle Opening Angle", "Throttle opening angle in %", "0x000015", new ThrottleOpeningAngleConvertor());
CONTROLLER.addLogger(ecuParam5, new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
dataTextArea.append(ecuParam5.getName() + " (" + ecuParam5.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
+ " " + convertor.getUnits() + "\n");
}
});
registerEcuParameterForLogging(ecuParam5);
// add test address to log (0x00000E 0x00000F = engine speed, 16bit)
// final EcuParameter ecuParam5 = new EcuParameterImpl("Engine Speed", "Engine speed in rpm", "0x00000E00000F", new EngineSpeedConvertor());
// CONTROLLER.addLogger(ecuParam5, new LoggerCallback() {
// public void callback(byte[] value, EcuParameterConvertor convertor) {
// dataTextArea.append(ecuParam5.getName() + " (" + ecuParam5.getAddress() + ") => " + asHex(value) + " => " + convertor.convert(value)
// + " " + convertor.getUnits() + "\n");
// }
// });
final EcuParameter ecuParam6 = new EcuParameterImpl("Engine Speed", "Engine speed in rpm", "0x00000E00000F", new EngineSpeedConvertor());
registerEcuParameterForLogging(ecuParam6);
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
private void registerEcuParameterForLogging(final EcuParameter ecuParam) {
// add to data table
final LoggerDataRow dataRow = new LoggerDataRow(ecuParam);
dataTableModel.addRow(dataRow);
// add to charts
final XYSeries series = new XYSeries(ecuParam.getName());
final XYDataset xyDataset = new XYSeriesCollection(series);
final JFreeChart chart = ChartFactory.createXYLineChart(ecuParam.getName(), "Time (ms)", ecuParam.getName() + " (" + ecuParam.getConvertor().getUnits() + ")",
xyDataset, PlotOrientation.VERTICAL, true, true, false);
final JLabel chartLabel = new JLabel();
chartLabel.setIcon(new ImageIcon(chart.createBufferedImage(500, 300)));
graphPanel.add(chartLabel);
SpringUtilities.makeCompactGrid(graphPanel, ++loggerCount, 1, 10, 10, 20, 20);
// add to dashboard
// add logger and setup callback
CONTROLLER.addLogger(ecuParam, new LoggerCallback() {
public void callback(byte[] value) {
// update data table
dataRow.updateValue(value);
// update graph
series.add(System.currentTimeMillis(), ecuParam.getConvertor().convert(value));
chartLabel.setIcon(new ImageIcon(chart.createBufferedImage(500, 300)));
// update dashboard
}
});
}
private static void createAndShowGUI() {
//set look and feel
setLookAndFeel();
//make sure we have nice window decorations.
setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
//instantiate the controlling class.
EcuLogger ecuLogger = new EcuLogger("Enginuity ECU Logger");
//set remaining window properties
ecuLogger.setSize(new Dimension(1000, 600));
ecuLogger.setIconImage(new ImageIcon("./graphics/enginuity-ico.gif").getImage());
ecuLogger.setDefaultCloseOperation(EXIT_ON_CLOSE);
ecuLogger.addWindowListener(ecuLogger);
//display the window
ecuLogger.setLocationRelativeTo(null); //center it
ecuLogger.setVisible(true);
}
private static void setLookAndFeel() {
try {
//use the system look and feel.
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
private JComponent buildLeftComponent() {
JTable parameterList = new JTable();
return new JScrollPane(parameterList, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_AS_NEEDED);
@ -217,12 +191,11 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
}
private JComponent buildDataTab() {
//return new JScrollPane(dataTable, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
return new JScrollPane(dataTextArea, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
return new JScrollPane(new JTable(dataTableModel), VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
}
private JComponent buildGraphTab() {
JPanel graphPanel = new JPanel();
graphPanel.setLayout(new SpringLayout());
return new JScrollPane(graphPanel, VERTICAL_SCROLLBAR_AS_NEEDED, HORIZONTAL_SCROLLBAR_NEVER);
}
@ -256,4 +229,43 @@ public final class EcuLogger extends JFrame implements WindowListener, PropertyC
public void propertyChange(PropertyChangeEvent propertyChangeEvent) {
}
public static void main(String... args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
//set look and feel
setLookAndFeel();
//make sure we have nice window decorations.
setDefaultLookAndFeelDecorated(true);
JDialog.setDefaultLookAndFeelDecorated(true);
//instantiate the controlling class.
EcuLogger ecuLogger = new EcuLogger("Enginuity ECU Logger");
//set remaining window properties
ecuLogger.setSize(new Dimension(1000, 600));
ecuLogger.setIconImage(new ImageIcon("./graphics/enginuity-ico.gif").getImage());
ecuLogger.setDefaultCloseOperation(EXIT_ON_CLOSE);
ecuLogger.addWindowListener(ecuLogger);
//display the window
ecuLogger.setLocationRelativeTo(null); //center it
ecuLogger.setVisible(true);
}
private static void setLookAndFeel() {
try {
//use the system look and feel.
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -86,7 +86,7 @@ public final class SerialConnectionImpl implements SerialConnection {
serialPort.setRTS(false);
os.write(request, 0, request.length);
os.flush();
int timeout = 100;
int timeout = 1000;
while (is.available() < response.length) {
TimeUnit.MILLISECONDS.sleep(5);
timeout -= 5;

View File

@ -10,8 +10,6 @@ public interface EcuParameter {
String getAddress();
String getValueLength();
EcuParameterConvertor getConvertor();
}

View File

@ -33,11 +33,6 @@ public final class EcuParameterImpl implements EcuParameter {
return address;
}
//TODO: Is this required???
public String getValueLength() {
return null;
}
public EcuParameterConvertor getConvertor() {
return convertor;
}

View File

@ -7,12 +7,15 @@ import java.text.DecimalFormat;
public final class AcceleratorOpeningAngleConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0");
public String convert(byte[] bytes) {
double angle = (double) asInt(bytes) / 2.55;
return AcceleratorOpeningAngleConvertor.DECIMAL_FORMAT.format(angle);
public double convert(byte[] bytes) {
return (double) asInt(bytes) / 2.55;
}
public String getUnits() {
return "%";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -7,13 +7,16 @@ import java.text.DecimalFormat;
public final class AirFuelRatioLambdaConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0");
public String convert(byte[] bytes) {
double afr = (double) asInt(bytes) / 128.0;
return DECIMAL_FORMAT.format(afr);
public double convert(byte[] bytes) {
return (double) asInt(bytes) / 128.0;
}
public String getUnits() {
return "Lambda";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -2,8 +2,10 @@ package enginuity.logger.definition.convertor;
public interface EcuParameterConvertor {
String convert(byte[] bytes);
double convert(byte[] bytes);
String getUnits();
String format(double value);
}

View File

@ -2,14 +2,20 @@ package enginuity.logger.definition.convertor;
import static enginuity.util.ByteUtil.asInt;
public final class EngineSpeedConvertor implements EcuParameterConvertor {
import java.text.DecimalFormat;
public String convert(byte[] bytes) {
int rpm = asInt(bytes) / 4;
return String.valueOf(rpm);
public final class EngineSpeedConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0");
public double convert(byte[] bytes) {
return asInt(bytes) / 4;
}
public String getUnits() {
return "rpm";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -2,14 +2,20 @@ package enginuity.logger.definition.convertor;
import static enginuity.util.ByteUtil.asInt;
public final class ExhaustGasTemperatureConvertor implements EcuParameterConvertor {
import java.text.DecimalFormat;
public String convert(byte[] bytes) {
int degreesC = (asInt(bytes) + 40) * 5;
return String.valueOf(degreesC);
public final class ExhaustGasTemperatureConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0");
public double convert(byte[] bytes) {
return (asInt(bytes) + 40) * 5;
}
public String getUnits() {
return "C";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -2,15 +2,21 @@ package enginuity.logger.definition.convertor;
import static enginuity.util.ByteUtil.asInt;
public final class GenericTemperatureConvertor implements EcuParameterConvertor {
import java.text.DecimalFormat;
public String convert(byte[] bytes) {
int degreesC = asInt(bytes) - 40;
return String.valueOf(degreesC);
public final class GenericTemperatureConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0");
public double convert(byte[] bytes) {
return asInt(bytes) - 40;
}
public String getUnits() {
return "C";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -1,14 +0,0 @@
package enginuity.logger.definition.convertor;
import static enginuity.util.HexUtil.asHex;
public final class HexConvertor implements EcuParameterConvertor {
public String convert(byte[] bytes) {
return asHex(bytes);
}
public String getUnits() {
return "hex";
}
}

View File

@ -7,12 +7,15 @@ import java.text.DecimalFormat;
public final class ThrottleOpeningAngleConvertor implements EcuParameterConvertor {
private static final DecimalFormat DECIMAL_FORMAT = new DecimalFormat("0.0");
public String convert(byte[] bytes) {
double angle = (double) asInt(bytes) / 2.55;
return DECIMAL_FORMAT.format(angle);
public double convert(byte[] bytes) {
return (double) asInt(bytes) / 2.55;
}
public String getUnits() {
return "%";
}
public String format(double value) {
return DECIMAL_FORMAT.format(value);
}
}

View File

@ -3,7 +3,6 @@ package enginuity.logger.manager;
import enginuity.Settings;
import enginuity.logger.comms.SerialConnection;
import enginuity.logger.comms.SerialConnectionImpl;
import enginuity.logger.definition.convertor.EcuParameterConvertor;
import enginuity.logger.exception.NotConnectedException;
import enginuity.logger.exception.SerialCommunicationException;
import enginuity.logger.protocol.Protocol;
@ -39,7 +38,7 @@ public final class TransmissionManagerImpl implements TransmissionManager {
public void sendEcuInit(LoggerCallback callback) {
if (serialConnection != null) {
callback.callback(serialConnection.sendEcuInit(), null);
callback.callback(serialConnection.sendEcuInit());
} else {
throw new NotConnectedException("TransmissionManager must be started before a query can be sent!");
}
@ -66,7 +65,7 @@ public final class TransmissionManagerImpl implements TransmissionManager {
try {
txManager.start();
txManager.sendEcuInit(new LoggerCallback() {
public void callback(byte[] value, EcuParameterConvertor convertor) {
public void callback(byte[] value) {
System.out.println("ECU Init Response = " + HexUtil.asHex(value));
}
});

View File

@ -21,9 +21,10 @@ public final class SSMProtocol implements Protocol {
private static final byte READ_ADDRESS_RESPONSE = (byte) 0xE8;
private static final byte ECU_INIT_COMMAND = (byte) 0xBF;
private static final byte ECU_INIT_RESPONSE = (byte) 0xFF;
private static final int NON_DATA_BYTES = 6;
private static final int RESPONSE_NON_DATA_BYTES = 6;
private static final int ADDRESS_SIZE = 3;
private static final int DATA_SIZE = 1;
private static final int REQUEST_NON_DATA_BYTES = 7;
public byte[] constructReadMemoryRequest(RegisteredQuery query, int numBytes) {
checkNotNull(query, "query");
@ -42,7 +43,11 @@ public final class SSMProtocol implements Protocol {
public byte[] constructReadAddressResponse(Collection<RegisteredQuery> queries) {
checkNotNullOrEmpty(queries, "queries");
// 0x80 0xF0 0x10 data_length 0xE8 value1 value2 ... valueN checksum
return new byte[(DATA_SIZE * queries.size() + NON_DATA_BYTES) + (queries.size() * ADDRESS_SIZE + 7)];
int numAddresses = 0;
for (RegisteredQuery query : queries) {
numAddresses += (query.getBytes().length / ADDRESS_SIZE);
}
return new byte[(numAddresses * DATA_SIZE + RESPONSE_NON_DATA_BYTES) + (numAddresses * ADDRESS_SIZE + REQUEST_NON_DATA_BYTES)];
}
@SuppressWarnings({"PointlessArithmeticExpression"})
@ -67,10 +72,9 @@ public final class SSMProtocol implements Protocol {
public byte[] extractResponseData(byte[] response) {
checkNotNullOrEmpty(response, "response");
// 0x80 0xF0 0x10 data_length 0xE8 response_data checksum
//TODO: Take possible echoed request into account when extracting response!!
validateResponse(response);
byte[] data = new byte[response.length - NON_DATA_BYTES];
System.arraycopy(response, (NON_DATA_BYTES - 1), data, 0, data.length);
byte[] data = new byte[response.length - RESPONSE_NON_DATA_BYTES];
System.arraycopy(response, (RESPONSE_NON_DATA_BYTES - 1), data, 0, data.length);
return data;
}
@ -111,7 +115,7 @@ public final class SSMProtocol implements Protocol {
assertEquals(HEADER, response[i++], "Invalid header");
assertEquals(DIAGNOSTIC_TOOL_ID, response[i++], "Invalid diagnostic tool id");
assertEquals(ECU_ID, response[i++], "Invalid ECU id");
assertEquals(asByte(response.length - NON_DATA_BYTES + 1), response[i++], "Invalid response data length");
assertEquals(asByte(response.length - RESPONSE_NON_DATA_BYTES + 1), response[i++], "Invalid response data length");
assertOneOf(new byte[]{READ_ADDRESS_RESPONSE, READ_MEMORY_RESPONSE, ECU_INIT_RESPONSE}, response[i], "Invalid response code");
assertEquals(calculateChecksum(response), response[response.length - 1], "Invalid checksum");
}
@ -147,7 +151,7 @@ public final class SSMProtocol implements Protocol {
System.arraycopy(tmp, 0, tmp2, data.length, tmp.length);
data = tmp2;
}
byte[] request = new byte[data.length + (padContent ? 7 : NON_DATA_BYTES)];
byte[] request = new byte[data.length + (padContent ? REQUEST_NON_DATA_BYTES : RESPONSE_NON_DATA_BYTES)];
int i = 0;
request[i++] = HEADER;
request[i++] = ECU_ID;

View File

@ -1,9 +1,7 @@
package enginuity.logger.query;
import enginuity.logger.definition.convertor.EcuParameterConvertor;
public interface LoggerCallback {
void callback(byte[] value, EcuParameterConvertor convertor);
void callback(byte[] value);
}

View File

@ -4,10 +4,6 @@ import enginuity.logger.definition.EcuParameter;
import static enginuity.util.HexUtil.asBytes;
import static enginuity.util.ParamChecker.checkNotNull;
//TODO: change address into an EcuParameter object with getAddress() & getLength() methods
//TODO: use the getLength() method to do the response data extraction in SSMProtocol
@SuppressWarnings({"FieldCanBeLocal"})
public final class RegisteredQueryImpl implements RegisteredQuery {
private final EcuParameter ecuParam;
private final LoggerCallback callback;
@ -29,6 +25,6 @@ public final class RegisteredQueryImpl implements RegisteredQuery {
}
public void setResponse(byte[] response) {
callback.callback(response, ecuParam.getConvertor());
callback.callback(response);
}
}

View File

@ -0,0 +1,52 @@
package enginuity.logger.ui;
import enginuity.logger.definition.EcuParameter;
import javax.swing.table.AbstractTableModel;
public final class LoggerDataRow {
private EcuParameter ecuParam;
private AbstractTableModel parentTableModel;
private double minValue;
private double maxValue;
private double currentValue;
public LoggerDataRow(EcuParameter ecuParam) {
this.ecuParam = ecuParam;
}
public String getName() {
return ecuParam.getName();
}
public String getMinValue() {
return ecuParam.getConvertor().format(minValue);
}
public String getMaxValue() {
return ecuParam.getConvertor().format(maxValue);
}
public String getCurrentValue() {
return ecuParam.getConvertor().format(currentValue);
}
public String getUnits() {
return ecuParam.getConvertor().getUnits();
}
public void updateValue(byte[] bytes) {
currentValue = ecuParam.getConvertor().convert(bytes);
if (currentValue < minValue || minValue == 0.0) {
minValue = currentValue;
}
if (currentValue > maxValue || maxValue == 0.0) {
maxValue = currentValue;
}
parentTableModel.fireTableDataChanged();
}
public void setParentTableModel(AbstractTableModel parentTableModel) {
this.parentTableModel = parentTableModel;
}
}

View File

@ -0,0 +1,49 @@
package enginuity.logger.ui;
import javax.swing.table.AbstractTableModel;
public final class LoggerDataTableModel extends AbstractTableModel {
String[] columnNames = {"ECU Parameter", "Min Value", "Current Value", "Max Value", "Units"};
LoggerDataRow[] data = new LoggerDataRow[0];
public int getRowCount() {
return data.length;
}
public int getColumnCount() {
return columnNames.length;
}
public String getColumnName(int col) {
return columnNames[col];
}
public boolean isCellEditable(int row, int col) {
return false;
}
public Object getValueAt(int row, int col) {
switch (col) {
case 0:
return data[row].getName();
case 1:
return data[row].getMinValue();
case 2:
return data[row].getCurrentValue();
case 3:
return data[row].getMaxValue();
case 4:
return data[row].getUnits();
default:
return "Error!";
}
}
public synchronized void addRow(LoggerDataRow newRow) {
newRow.setParentTableModel(this);
LoggerDataRow[] newData = new LoggerDataRow[data.length + 1];
System.arraycopy(data, 0, newData, 0, data.length);
newData[newData.length - 1] = newRow;
data = newData;
}
}

View File

@ -0,0 +1,197 @@
package enginuity.logger.ui;
import javax.swing.*;
import java.awt.*;
/**
* A 1.4 file that provides utility methods for
* creating form- or grid-style layouts with SpringLayout.
* These utilities are used by several programs, such as
* SpringBox and SpringCompactGrid.
*/
public class SpringUtilities {
private SpringUtilities() {
}
/**
* A debugging utility that prints to stdout the component's
* minimum, preferred, and maximum sizes.
*/
public static void printSizes(Component c) {
System.out.println("minimumSize = " + c.getMinimumSize());
System.out.println("preferredSize = " + c.getPreferredSize());
System.out.println("maximumSize = " + c.getMaximumSize());
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component is as big as the maximum
* preferred width and height of the components.
* The parent is made just big enough to fit them all.
*
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeGrid(Container parent,
int rows, int cols,
int initialX, int initialY,
int xPad, int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout)parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeGrid must use SpringLayout.");
return;
}
Spring xPadSpring = Spring.constant(xPad);
Spring yPadSpring = Spring.constant(yPad);
Spring initialXSpring = Spring.constant(initialX);
Spring initialYSpring = Spring.constant(initialY);
int max = rows * cols;
//Calculate Springs that are the max of the width/height so that all
//cells have the same size.
Spring maxWidthSpring = layout.getConstraints(parent.getComponent(0)).
getWidth();
Spring maxHeightSpring = layout.getConstraints(parent.getComponent(0)).
getWidth();
for (int i = 1; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
maxWidthSpring = Spring.max(maxWidthSpring, cons.getWidth());
maxHeightSpring = Spring.max(maxHeightSpring, cons.getHeight());
}
//Apply the new width/height Spring. This forces all the
//components to have the same size.
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
cons.setWidth(maxWidthSpring);
cons.setHeight(maxHeightSpring);
}
//Then adjust the x/y constraints of all the cells so that they
//are aligned in a grid.
SpringLayout.Constraints lastCons = null;
SpringLayout.Constraints lastRowCons = null;
for (int i = 0; i < max; i++) {
SpringLayout.Constraints cons = layout.getConstraints(
parent.getComponent(i));
if (i % cols == 0) { //start of new row
lastRowCons = lastCons;
cons.setX(initialXSpring);
} else { //x position depends on previous component
cons.setX(Spring.sum(lastCons.getConstraint(SpringLayout.EAST),
xPadSpring));
}
if (i / cols == 0) { //first row
cons.setY(initialYSpring);
} else { //y position depends on previous row
cons.setY(Spring.sum(lastRowCons.getConstraint(SpringLayout.SOUTH),
yPadSpring));
}
lastCons = cons;
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH,
Spring.sum(
Spring.constant(yPad),
lastCons.getConstraint(SpringLayout.SOUTH)));
pCons.setConstraint(SpringLayout.EAST,
Spring.sum(
Spring.constant(xPad),
lastCons.getConstraint(SpringLayout.EAST)));
}
/* Used by makeCompactGrid. */
private static SpringLayout.Constraints getConstraintsForCell(
int row, int col,
Container parent,
int cols) {
SpringLayout layout = (SpringLayout) parent.getLayout();
Component c = parent.getComponent(row * cols + col);
return layout.getConstraints(c);
}
/**
* Aligns the first <code>rows</code> * <code>cols</code>
* components of <code>parent</code> in
* a grid. Each component in a column is as wide as the maximum
* preferred width of the components in that column;
* height is similarly determined for each row.
* The parent is made just big enough to fit them all.
*
* @param rows number of rows
* @param cols number of columns
* @param initialX x location to start the grid at
* @param initialY y location to start the grid at
* @param xPad x padding between cells
* @param yPad y padding between cells
*/
public static void makeCompactGrid(Container parent,
int rows, int cols,
int initialX, int initialY,
int xPad, int yPad) {
SpringLayout layout;
try {
layout = (SpringLayout)parent.getLayout();
} catch (ClassCastException exc) {
System.err.println("The first argument to makeCompactGrid must use SpringLayout.");
return;
}
//Align all cells in each column and make them the same width.
Spring x = Spring.constant(initialX);
for (int c = 0; c < cols; c++) {
Spring width = Spring.constant(0);
for (int r = 0; r < rows; r++) {
width = Spring.max(width,
getConstraintsForCell(r, c, parent, cols).
getWidth());
}
for (int r = 0; r < rows; r++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setX(x);
constraints.setWidth(width);
}
x = Spring.sum(x, Spring.sum(width, Spring.constant(xPad)));
}
//Align all cells in each row and make them the same height.
Spring y = Spring.constant(initialY);
for (int r = 0; r < rows; r++) {
Spring height = Spring.constant(0);
for (int c = 0; c < cols; c++) {
height = Spring.max(height,
getConstraintsForCell(r, c, parent, cols).
getHeight());
}
for (int c = 0; c < cols; c++) {
SpringLayout.Constraints constraints =
getConstraintsForCell(r, c, parent, cols);
constraints.setY(y);
constraints.setHeight(height);
}
y = Spring.sum(y, Spring.sum(height, Spring.constant(yPad)));
}
//Set the parent's size.
SpringLayout.Constraints pCons = layout.getConstraints(parent);
pCons.setConstraint(SpringLayout.SOUTH, y);
pCons.setConstraint(SpringLayout.EAST, x);
}
}