Made the ComponentPresetChooserDialog much more cool by reworking the table. User can hide and unhide columns, reorder columns, and change units by using a right click context menu in the header bar.

This commit is contained in:
Kevin Ruland 2012-04-18 17:43:09 +00:00
parent 64297d8ee7
commit 94e8b3460b
6 changed files with 606 additions and 60 deletions

View File

@ -1593,6 +1593,9 @@ ComponentPresetChooserDialog.title = Choose component preset
ComponentPresetChooserDialog.filter.label = Filter:
ComponentPresetChooserDialog.checkbox.filterAftDiameter = Match aft diameter
ComponentPresetChooserDialog.checkbox.filterForeDiameter = Match fore diameter
ComponentPresetChooserDialog.menu.sortAsc = Sort Ascending
ComponentPresetChooserDialog.menu.sortDesc = Sort Descending
ComponentPresetChooserDialog.menu.units = Units
table.column.Favorite = Favorite
table.column.Manufacturer = Manufacturer
table.column.PartNo = Part Number

View File

@ -8,6 +8,7 @@ import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JButton;
@ -44,8 +45,8 @@ public class ComponentPresetChooserDialog extends JDialog {
private final RocketComponent component;
private final JTable componentSelectionTable;
private final TableRowSorter<TableModel> sorter;
private ComponentPresetTable componentSelectionTable;
// private final JTable componentSelectionTable;
private final JTextField filterText;
private final JCheckBox foreDiameterFilterCheckBox;
private final JCheckBox aftDiameterFilterCheckBox;
@ -70,29 +71,6 @@ public class ComponentPresetChooserDialog extends JDialog {
presets = Application.getComponentPresetDao().listForType(component.getPresetType());
/*
* Set up the Column Table model
*/
final Column[] columns = new Column[columnKeys.length+1];
columns[0] = new Column(trans.get("table.column.Favorite") ) {
@Override
public Object getValueAt(int row) {
return Boolean.valueOf(ComponentPresetChooserDialog.this.presets.get(row).isFavorite());
}
@Override
public void setValueAt(int row, Object value) {
Application.getComponentPresetDao().setFavorite(ComponentPresetChooserDialog.this.presets.get(row), (Boolean) value);
}
@Override
public Class<?> getColumnClass() {
return Boolean.class;
}
};
for (int i = 0; i < columnKeys.length; i++) {
final TypedKey<?> key = columnKeys[i];
if ( key == ComponentPreset.OUTER_DIAMETER ) {
@ -103,21 +81,6 @@ public class ComponentPresetChooserDialog extends JDialog {
// magic +1 is because we have inserted the column for favorites above.
foreDiameterColumnIndex = i+1;
}
columns[i+1] = new Column(trans.get("table.column." + key.getName())) {
@Override
public Object getValueAt(int row) {
ComponentPreset preset = ComponentPresetChooserDialog.this.presets.get(row);
if ( ! preset.has(key) ) {
return null;
}
Object value = preset.get(key);
if (key.getType() == Double.class && key.getUnitGroup() != null) {
return new Value( (Double) value, key.getUnitGroup() );
} else {
return value;
}
}
};
}
/*
@ -132,20 +95,6 @@ public class ComponentPresetChooserDialog extends JDialog {
foreDiameterColumnIndex = aftDiameterColumnIndex;
}
ColumnTableModel tableModel = new ColumnTableModel(columns) {
@Override
public int getRowCount() {
return ComponentPresetChooserDialog.this.presets.size();
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 0;
}
};
/*
* Add filter by text.
*/
@ -205,12 +154,8 @@ public class ComponentPresetChooserDialog extends JDialog {
aftDiameterFilterCheckBox.setVisible(false);
}
componentSelectionTable = new JTable( tableModel );
componentSelectionTable = new ComponentPresetTable( presets, Arrays.<TypedKey<?>>asList(columnKeys) );
componentSelectionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
sorter = new TableRowSorter<TableModel>(tableModel);
componentSelectionTable.setRowSorter(sorter);
JScrollPane scrollpane = new JScrollPane();
scrollpane.setViewportView(componentSelectionTable);
@ -319,6 +264,6 @@ public class ComponentPresetChooserDialog extends JDialog {
}
}
sorter.setRowFilter( RowFilter.andFilter(filters) );
componentSelectionTable.setRowFilter( RowFilter.andFilter(filters) );
}
}

View File

@ -0,0 +1,253 @@
package net.sf.openrocket.gui.dialogs.preset;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.RowFilter;
import javax.swing.RowSorter.SortKey;
import javax.swing.SortOrder;
import javax.swing.event.ListSelectionEvent;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.TypedKey;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
public class ComponentPresetTable extends JTable {
private static final Translator trans = Application.getTranslator();
private final TableRowSorter<TableModel> sorter;
private final List<ComponentPreset> presets;
private final AbstractTableModel tableModel;
private final XTableColumnModel tableColumnModel;
private final ComponentPresetTableColumn[] columns;
public ComponentPresetTable(List<ComponentPreset> presets, List<TypedKey<?>> visibleColumnKeys) {
super();
this.presets = presets;
this.columns = new ComponentPresetTableColumn[ComponentPreset.orderedKeyList.size()+1];
tableColumnModel = new XTableColumnModel();
/*
* Set up the Column Table model
*/
columns[0] = new ComponentPresetTableColumn.Favorite(0);
tableColumnModel.addColumn(columns[0]);
List<TableColumn> hiddenColumns = new ArrayList<TableColumn>();
{
int index = 1;
for (final TypedKey<?> key: ComponentPreset.orderedKeyList ) {
if ( key.getType() == Double.class && key.getUnitGroup() != null ) {
columns[index] = new ComponentPresetTableColumn.DoubleWithUnit((TypedKey<Double>)key,index);
} else {
columns[index] = new ComponentPresetTableColumn.Parameter(key,index);
}
tableColumnModel.addColumn(columns[index]);
if ( visibleColumnKeys.indexOf(key) < 0 ) {
hiddenColumns.add(columns[index]);
}
index ++;
}
}
tableModel = new AbstractTableModel() {
final ComponentPresetTableColumn[] myColumns = columns;
@Override
public int getRowCount() {
return ComponentPresetTable.this.presets.size();
}
@Override
public int getColumnCount() {
return myColumns.length;
}
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
return myColumns[columnIndex].getValueFromPreset(ComponentPresetTable.this.presets.get(rowIndex));
}
@Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
// Only support favorite
if ( columnIndex != 0 ) {
return;
}
ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex);
Application.getComponentPresetDao().setFavorite(preset, (Boolean) aValue);
}
@Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 0;
}
@Override
public Class<?> getColumnClass(int columnIndex) {
return columnIndex == 0 ? Boolean.class : Object.class;
}
};
this.setAutoCreateColumnsFromModel(false);
this.setColumnModel( tableColumnModel );
this.setModel(tableModel);
this.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
for ( TableColumn hiddenColumn : hiddenColumns ) {
tableColumnModel.setColumnVisible(hiddenColumn, false);
}
sorter = new TableRowSorter<TableModel>(tableModel);
this.setRowSorter(sorter);
JTableHeader header = this.getTableHeader();
header.setReorderingAllowed(true);
header.addMouseListener( new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if ( e.isPopupTrigger() ) {
doPopup(e);
}
}
@Override
public void mouseReleased(MouseEvent e) {
if ( e.isPopupTrigger() ) {
doPopup(e);
}
}
});
}
public void setRowFilter( RowFilter<? super TableModel ,? super Integer> filter ) {
sorter.setRowFilter( filter );
}
private void doPopup(MouseEvent evt ) {
// Figure out what column header was clicked on.
int colIndex = tableColumnModel.getColumnIndexAtX( evt.getX() );
ComponentPresetTableColumn colClicked = null;
if ( colIndex >=0 ) {
colClicked = (ComponentPresetTableColumn) tableColumnModel.getColumn(colIndex);
}
JPopupMenu columnMenu = new ColumnPopupMenu(colClicked, colIndex);
columnMenu.show(evt.getComponent(),evt.getX(),evt.getY());
}
private class ColumnPopupMenu extends JPopupMenu {
ColumnPopupMenu(ComponentPresetTableColumn colClicked, int colClickedIndex) {
if ( colClickedIndex >= 0 ) {
JCheckBoxMenuItem item = new SortAscColumnMenuItem(colClickedIndex);
this.add(item);
item = new SortDescColumnMenuItem(colClickedIndex);
this.add(item);
this.addSeparator();
if ( colClicked instanceof ComponentPresetTableColumn.DoubleWithUnit ) {
this.add( new UnitSelectorMenuItem( (ComponentPresetTableColumn.DoubleWithUnit) colClicked ));
this.addSeparator();
}
}
for( TableColumn c: columns ) {
JCheckBoxMenuItem item = new ToggleColumnMenuItem(c);
this.add(item);
}
}
private class SortAscColumnMenuItem extends JCheckBoxMenuItem implements ItemListener {
private int columnClicked;
SortAscColumnMenuItem(int columnClicked) {
super( trans.get("ComponentPresetChooserDialog.menu.sortAsc") );
this.addItemListener(this);
this.columnClicked = columnClicked;
}
@Override
public void itemStateChanged(ItemEvent e) {
sorter.setSortKeys( Collections.singletonList( new SortKey(columnClicked, SortOrder.ASCENDING)));
}
}
private class SortDescColumnMenuItem extends JCheckBoxMenuItem implements ItemListener {
private int columnClicked;
SortDescColumnMenuItem(int columnClicked) {
super( trans.get("ComponentPresetChooserDialog.menu.sortDesc") );
this.addItemListener(this);
this.columnClicked = columnClicked;
}
@Override
public void itemStateChanged(ItemEvent e) {
sorter.setSortKeys( Collections.singletonList( new SortKey(columnClicked, SortOrder.DESCENDING)));
}
}
private class ToggleColumnMenuItem extends JCheckBoxMenuItem implements ItemListener {
TableColumn col;
ToggleColumnMenuItem( TableColumn col ) {
super( String.valueOf(col.getHeaderValue()), tableColumnModel.isColumnVisible(col));
this.addItemListener(this);
this.col = col;
}
@Override
public void itemStateChanged(ItemEvent e) {
tableColumnModel.setColumnVisible(col, !tableColumnModel.isColumnVisible(col));
}
}
private class UnitSelectorMenuItem extends JMenu implements ItemListener {
ComponentPresetTableColumn.DoubleWithUnit col;
UnitSelectorMenuItem( ComponentPresetTableColumn.DoubleWithUnit col ) {
super(trans.get("ComponentPresetChooserDialog.menu.units"));
this.col = col;
UnitGroup group = col.unitGroup;
Unit selectedUnit = col.selectedUnit;
for( Unit u : group.getUnits() ) {
JCheckBoxMenuItem item = new JCheckBoxMenuItem( u.toString() );
if ( u == selectedUnit ) {
item.setSelected(true);
}
item.addItemListener(this);
this.add(item);
}
}
@Override
public void itemStateChanged(ItemEvent e) {
JCheckBoxMenuItem item = (JCheckBoxMenuItem) e.getItem();
String val = item.getText();
col.selectedUnit = col.unitGroup.findApproximate(val);
ComponentPresetTable.this.tableModel.fireTableDataChanged();
return;
}
}
}
}

View File

@ -0,0 +1,83 @@
package net.sf.openrocket.gui.dialogs.preset;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.TypedKey;
import net.sf.openrocket.startup.Application;
import net.sf.openrocket.unit.Unit;
import net.sf.openrocket.unit.UnitGroup;
import net.sf.openrocket.unit.Value;
public abstract class ComponentPresetTableColumn extends TableColumn {
private static final Translator trans = Application.getTranslator();
protected ComponentPresetTableColumn( String header, int modelIndex ) {
this.setHeaderValue(header);
this.setModelIndex(modelIndex);
}
public abstract Object getValueFromPreset( ComponentPreset preset );
public static class Favorite extends ComponentPresetTableColumn {
public Favorite(int modelIndex) {
super(trans.get("table.column.Favorite"), modelIndex);
}
@Override
public Object getValueFromPreset( ComponentPreset preset ) {
return Boolean.valueOf(preset.isFavorite());
}
}
public static class Parameter extends ComponentPresetTableColumn {
protected final TypedKey<?> key;
public Parameter( TypedKey<?> key, int modelIndex ) {
super( trans.get("table.column." + key.getName()), modelIndex );
this.key = key;
}
@Override
public Object getValueFromPreset(ComponentPreset preset) {
return preset.has(key) ? preset.get(key) : null;
}
}
public static class DoubleWithUnit extends Parameter {
UnitGroup unitGroup;
Unit selectedUnit;
public DoubleWithUnit( TypedKey<Double> key, int modelIndex ) {
super(key,modelIndex);
this.unitGroup = key.getUnitGroup();
this.selectedUnit = unitGroup.getDefaultUnit();
}
@Override
public Object getValueFromPreset(ComponentPreset preset) {
Double value = (Double) super.getValueFromPreset(preset);
if ( value != null ) {
return new Value((Double)super.getValueFromPreset(preset),selectedUnit);
} else {
return null;
}
}
}
}

View File

@ -0,0 +1,241 @@
package net.sf.openrocket.gui.dialogs.preset;
import java.util.Enumeration;
import java.util.Vector;
import javax.swing.table.DefaultTableColumnModel;
import javax.swing.table.TableColumn;
public class XTableColumnModel extends DefaultTableColumnModel {
/** Array of TableColumn objects in this model.
* Holds all column objects, regardless of their visibility
*/
protected Vector<TableColumn> allTableColumns = new Vector<TableColumn>();
/**
* Creates an extended table column model.
*/
XTableColumnModel() {
super();
}
/**
* Sets the visibility of the specified TableColumn.
* The call is ignored if the TableColumn is not found in this column model
* or its visibility status did not change.
* <p>
*
* @param aColumn the column to show/hide
* @param visible its new visibility status
*/
// listeners will receive columnAdded()/columnRemoved() event
public void setColumnVisible(TableColumn column, boolean visible) {
if(!visible) {
super.removeColumn(column);
}
else {
// find the visible index of the column:
// iterate through both collections of visible and all columns, counting
// visible columns up to the one that's about to be shown again
int noVisibleColumns = tableColumns.size();
int noInvisibleColumns = allTableColumns.size();
int visibleIndex = 0;
for(int invisibleIndex = 0; invisibleIndex < noInvisibleColumns; ++invisibleIndex) {
TableColumn visibleColumn = (visibleIndex < noVisibleColumns ? (TableColumn)tableColumns.get(visibleIndex) : null);
TableColumn testColumn = (TableColumn)allTableColumns.get(invisibleIndex);
if(testColumn == column) {
if(visibleColumn != column) {
super.addColumn(column);
super.moveColumn(tableColumns.size() - 1, visibleIndex);
}
return; // ####################
}
if(testColumn == visibleColumn) {
++visibleIndex;
}
}
}
}
/**
* Makes all columns in this model visible
*/
public void setAllColumnsVisible(boolean visible) {
int noColumns = allTableColumns.size();
for(int columnIndex = 0; columnIndex < noColumns; ++columnIndex) {
TableColumn visibleColumn = (columnIndex < tableColumns.size() ? (TableColumn)tableColumns.get(columnIndex) : null);
TableColumn invisibleColumn = (TableColumn)allTableColumns.get(columnIndex);
if ( visible ) {
if(visibleColumn != invisibleColumn) {
super.addColumn(invisibleColumn);
super.moveColumn(tableColumns.size() - 1, columnIndex);
}
} else {
super.removeColumn(invisibleColumn);
}
}
}
/**
* Maps the index of the column in the table model at
* <code>modelColumnIndex</code> to the TableColumn object.
* There may me multiple TableColumn objects showing the same model column, though this is uncommon.
* This method will always return the first visible or else the first invisible column with the specified index.
* @param modelColumnIndex index of column in table model
* @return table column object or null if no such column in this column model
*/
public TableColumn getColumnByModelIndex(int modelColumnIndex) {
for (int columnIndex = 0; columnIndex < allTableColumns.size(); ++columnIndex) {
TableColumn column = (TableColumn)allTableColumns.get(columnIndex);
if(column.getModelIndex() == modelColumnIndex) {
return column;
}
}
return null;
}
/** Checks wether the specified column is currently visible.
* @param aColumn column to check
* @return visibility of specified column (false if there is no such column at all. [It's not visible, right?])
*/
public boolean isColumnVisible(TableColumn aColumn) {
return (tableColumns.indexOf(aColumn) >= 0);
}
/** Append <code>column</code> to the right of exisiting columns.
* Posts <code>columnAdded</code> event.
* @param column The column to be added
* @see #removeColumn
* @exception IllegalArgumentException if <code>column</code> is <code>null</code>
*/
@Override
public void addColumn(TableColumn column) {
allTableColumns.add(column);
super.addColumn(column);
}
/** Removes <code>column</code> from this column model.
* Posts <code>columnRemoved</code> event.
* Will do nothing if the column is not in this model.
* @param column the column to be added
* @see #addColumn
*/
@Override
public void removeColumn(TableColumn column) {
int allColumnsIndex = allTableColumns.indexOf(column);
if(allColumnsIndex != -1) {
allTableColumns.remove(allColumnsIndex);
}
super.removeColumn(column);
}
/** Moves the column from <code>oldIndex</code> to <code>newIndex</code>.
* Posts <code>columnMoved</code> event.
* Will not move any columns if <code>oldIndex</code> equals <code>newIndex</code>.
*
* @param oldIndex index of column to be moved
* @param newIndex new index of the column
* @exception IllegalArgumentException if either <code>oldIndex</code> or
* <code>newIndex</code>
* are not in [0, getColumnCount() - 1]
*/
@Override
public void moveColumn(int oldIndex, int newIndex) {
if ((oldIndex < 0) || (oldIndex >= getColumnCount()) ||
(newIndex < 0) || (newIndex >= getColumnCount()))
throw new IllegalArgumentException("moveColumn() - Index out of range");
TableColumn fromColumn = (TableColumn) tableColumns.get(oldIndex);
TableColumn toColumn = (TableColumn) tableColumns.get(newIndex);
int allColumnsOldIndex = allTableColumns.indexOf(fromColumn);
int allColumnsNewIndex = allTableColumns.indexOf(toColumn);
if(oldIndex != newIndex) {
allTableColumns.remove(allColumnsOldIndex);
allTableColumns.add(allColumnsNewIndex, fromColumn);
}
super.moveColumn(oldIndex, newIndex);
}
/**
* Returns the total number of columns in this model.
*
* @param onlyVisible if set only visible columns will be counted
* @return the number of columns in the <code>tableColumns</code> array
* @see #getColumns
*/
public int getColumnCount(boolean onlyVisible) {
Vector<TableColumn> columns = (onlyVisible ? tableColumns : allTableColumns);
return columns.size();
}
/**
* Returns an <code>Enumeration</code> of all the columns in the model.
*
* @param onlyVisible if set all invisible columns will be missing from the enumeration.
* @return an <code>Enumeration</code> of the columns in the model
*/
public Enumeration<TableColumn> getColumns(boolean onlyVisible) {
Vector<TableColumn> columns = (onlyVisible ? tableColumns : allTableColumns);
return columns.elements();
}
/**
* Returns the position of the first column whose identifier equals <code>identifier</code>.
* Position is the the index in all visible columns if <code>onlyVisible</code> is true or
* else the index in all columns.
*
* @param identifier the identifier object to search for
* @param onlyVisible if set searches only visible columns
*
* @return the index of the first column whose identifier
* equals <code>identifier</code>
*
* @exception IllegalArgumentException if <code>identifier</code>
* is <code>null</code>, or if no
* <code>TableColumn</code> has this
* <code>identifier</code>
* @see #getColumn
*/
public int getColumnIndex(Object identifier, boolean onlyVisible) {
if (identifier == null) {
throw new IllegalArgumentException("Identifier is null");
}
Vector<TableColumn> columns = (onlyVisible ? tableColumns : allTableColumns);
int noColumns = columns.size();
TableColumn column;
for(int columnIndex = 0; columnIndex < noColumns; ++columnIndex) {
column = (TableColumn)columns.get(columnIndex);
if(identifier.equals(column.getIdentifier()))
return columnIndex;
}
throw new IllegalArgumentException("Identifier not found");
}
/**
* Returns the <code>TableColumn</code> object for the column
* at <code>columnIndex</code>.
*
* @param columnIndex the index of the column desired
* @param onlyVisible if set columnIndex is meant to be relative to all visible columns only
* else it is the index in all columns
*
* @return the <code>TableColumn</code> object for the column
* at <code>columnIndex</code>
*/
public TableColumn getColumn(int columnIndex, boolean onlyVisible) {
return (TableColumn)tableColumns.elementAt(columnIndex);
}
}

View File

@ -4,6 +4,7 @@ import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
@ -148,6 +149,26 @@ public class ComponentPreset implements Comparable<ComponentPreset> {
keyMap.put(MASS.getName(), MASS);
}
public final static List<TypedKey<?>> orderedKeyList = Arrays.<TypedKey<?>>asList(
MANUFACTURER,
PARTNO,
DESCRIPTION,
OUTER_DIAMETER,
INNER_DIAMETER,
LENGTH,
SHOULDER_DIAMETER,
SHOULDER_LENGTH,
FORE_SHOULDER_DIAMETER,
FORE_SHOULDER_LENGTH,
SHAPE,
THICKNESS,
FILLED,
MASS,
FINISH,
MATERIAL
);
// package scope constructor to encourage use of factory.
ComponentPreset() {
}