/* Copyright (C) 2001, 2007 United States Government as represented by the Administrator of the National Aeronautics and Space Administration. All Rights Reserved. */ package gov.nasa.worldwind.applications.sar; import gov.nasa.worldwind.geom.Angle; import javax.swing.*; import javax.swing.text.JTextComponent; import javax.swing.event.CellEditorListener; import javax.swing.table.*; import java.awt.*; import java.beans.*; import java.util.EventObject; /** * @author tag * @version $Id: PositionTable.java 4794 2008-03-22 20:55:21Z dcollins $ */ public class PositionTable extends JTable { private static final int ITEM_NUM_COLUMN = 0; private static final int LATITUDE_COLUMN = 1; private static final int LONGITUDE_COLUMN = 2; private static final int ALTITUDE_COLUMN = 3; private SARTrack sarTrack; private String elevationUnit; public PositionTable() { this.setToolTipText("Track Positions"); this.setModel(new MyTableModel()); this.setDefaultRenderer(Double.class, new DoubleCellRenderer("% 7.4f")); TableCellRenderer tcr = this.getTableHeader().getDefaultRenderer(); this.getTableHeader().getColumnModel().getColumn(ALTITUDE_COLUMN).setHeaderRenderer(new AltitudeHeaderRenderer(tcr, this)); this.getColumnModel().getColumn(ALTITUDE_COLUMN).setCellRenderer(new AltitudeCellRenderer("%,8.0f", this)); TableCellEditor tce = this.getDefaultEditor(Double.class); this.getColumnModel().getColumn(ALTITUDE_COLUMN).setCellEditor(new AltitudeCellEditor(tce, "%f", this)); { TableColumnModel cm = this.getColumnModel(); cm.getColumn(0).setResizable(false); cm.getColumn(0).setMinWidth(35); cm.getColumn(0).setPreferredWidth(35); cm.getColumn(1).setResizable(false); cm.getColumn(1).setMinWidth(70); cm.getColumn(1).setPreferredWidth(80); cm.getColumn(2).setResizable(false); cm.getColumn(2).setMinWidth(70); cm.getColumn(2).setPreferredWidth(80); cm.getColumn(3).setResizable(false); cm.getColumn(3).setMinWidth(70); cm.getColumn(3).setPreferredWidth(70); } } public SARTrack getSarTrack() { return sarTrack; } public void setSarTrack(SARTrack sarTrack) { this.sarTrack = sarTrack; updateTableData(); this.sarTrack.addPropertyChangeListener(TrackController.TRACK_MODIFY, new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent propertyChangeEvent) { updateTableData(); } }); } public String getElevationUnit() { return this.elevationUnit; } public void setElevationUnit(String unit) { this.elevationUnit = unit; } public void updateTableData() { ((AbstractTableModel) this.getModel()).fireTableDataChanged(); } private class MyTableModel extends AbstractTableModel { String[] columnNames = new String[] { "#", "Latitude\u00B0", "Longitude\u00B0", "Altitude" }; Class[] columnTypes = new Class[] { Integer.class, Double.class, Double.class, Double.class }; boolean[] columnEditable = new boolean[] { false, true, true, true }; public MyTableModel() { } @Override public Class<?> getColumnClass(int columnIndex) { return this.columnTypes[columnIndex]; } @Override public boolean isCellEditable(int rowIndex, int columnIndex) { return this.columnEditable[columnIndex]; } public int getRowCount() { return sarTrack != null ? sarTrack.size() : 0; } @Override public String getColumnName(int columnIndex) { return this.columnNames[columnIndex]; } public int getColumnCount() { return 4; } public Object getValueAt(int row, int col) { if (sarTrack == null) return null; switch (col) { case ITEM_NUM_COLUMN: return row; case LATITUDE_COLUMN: return sarTrack.get(row).getLatitude().degrees; case LONGITUDE_COLUMN: return sarTrack.get(row).getLongitude().degrees; case ALTITUDE_COLUMN: return sarTrack.get(row).getElevation(); } return null; } @Override public void setValueAt(Object object, int row, int col) { if (sarTrack == null) return; if (!(object instanceof Double)) return; double newVal = (Double) object; SARPosition curPos = sarTrack.get(row); SARPosition newPos; switch (col) { case LATITUDE_COLUMN: newPos = newVal < -90 || newVal > 90 ? curPos : new SARPosition(Angle.fromDegrees(newVal), curPos.getLongitude(), curPos.getElevation()); break; case LONGITUDE_COLUMN: newPos = newVal < -180 || newVal > 180 ? curPos : new SARPosition(curPos.getLatitude(), Angle.fromDegrees(newVal), curPos.getElevation()); break; case ALTITUDE_COLUMN: // The value stored in a SARPosition's elevation will always be in meters. // So when the altitude is displayed in feet, we will convert the incoming // value back to meters. This allows the user entring a value to operate in // whatever units are being displayed without thinking about conversion. if (SAR2.UNIT_IMPERIAL.equals(elevationUnit)) newVal = SAR2.feetToMeters(newVal); newPos = new SARPosition(curPos.getLatitude(), curPos.getLongitude(), newVal); break; default: return; } sarTrack.set(row, newPos); } } private static class AltitudeHeaderRenderer implements TableCellRenderer { private TableCellRenderer delegate; private PositionTable table; public AltitudeHeaderRenderer(TableCellRenderer delegate, PositionTable table) { this.delegate = delegate; this.table = table; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { if (this.delegate == null) return null; Component c = this.delegate.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (c == null || !(c instanceof JLabel)) return c; JLabel label = (JLabel) c; if (label.getText() == null) return c; if (SAR2.UNIT_IMPERIAL.equals(this.table.elevationUnit)) label.setText(label.getText() + " (ft)"); else // Default to metric units. label.setText(label.getText() + " (m)"); return label; } } private static class DoubleCellRenderer extends DefaultTableCellRenderer { private final String formatString; public DoubleCellRenderer(String formatString) { this.formatString = formatString; } @Override public Component getTableCellRendererComponent(JTable jTable, Object object, boolean b, boolean b1, int i, int i1) { Component c = super.getTableCellRendererComponent(jTable, object, b, b1, i, i1); if (!(c instanceof JLabel)) return c; JLabel label = (JLabel) c; if (label.getText() == null) return c; double value = Double.parseDouble(label.getText()); label.setText(String.format(this.formatString, value)); label.setHorizontalAlignment(SwingConstants.RIGHT); return c; } } private String makeElevationDescription(String formatString, double metersElevation) { String s; if (SAR2.UNIT_IMPERIAL.equals(this.elevationUnit)) s = String.format(formatString, SAR2.metersToFeet(metersElevation)); else // Default to metric units. s = String.format(formatString, metersElevation); return s; } private static class AltitudeCellRenderer extends DefaultTableCellRenderer { private final String formatString; private PositionTable table; private AltitudeCellRenderer(String formatString, PositionTable table) { this.formatString = formatString; this.table = table; } public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { Component c = super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column); if (c == null || !(c instanceof JLabel)) return c; JLabel label = (JLabel) c; if (label.getText() == null) return c; double d = Double.parseDouble(label.getText()); label.setText(this.table.makeElevationDescription(this.formatString, d)); label.setHorizontalAlignment(SwingConstants.RIGHT); return c; } } private static class AltitudeCellEditor implements TableCellEditor { private TableCellEditor delegate; private final String formatString; private PositionTable table; public AltitudeCellEditor(TableCellEditor delegate, String formatString, PositionTable table) { this.delegate = delegate; this.formatString = formatString; this.table = table; } public Object getCellEditorValue() { return this.delegate.getCellEditorValue(); } public boolean isCellEditable(EventObject eventObject) { return this.delegate.isCellEditable(eventObject); } public boolean shouldSelectCell(EventObject eventObject) { return this.delegate.shouldSelectCell(eventObject); } public boolean stopCellEditing() { return this.delegate.stopCellEditing(); } public void cancelCellEditing() { this.delegate.cancelCellEditing(); } public void addCellEditorListener(CellEditorListener cellEditorListener) { this.delegate.addCellEditorListener(cellEditorListener); } public void removeCellEditorListener(CellEditorListener cellEditorListener) { this.delegate.removeCellEditorListener(cellEditorListener); } public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, int column) { Component c = this.delegate.getTableCellEditorComponent(table, value, isSelected, row, column); if (c == null || !(c instanceof JTextComponent)) return c; JTextComponent label = (JTextComponent) c; if (label.getText() == null) return c; double d = Double.parseDouble(label.getText()); label.setText(this.table.makeElevationDescription(this.formatString, d)); return c; } } }