1 package net.sf.openrocket.gui.dialogs.preset;
4 import java.awt.Dialog;
5 import java.awt.Window;
6 import java.awt.event.ActionEvent;
7 import java.awt.event.ActionListener;
8 import java.awt.event.ItemEvent;
9 import java.awt.event.ItemListener;
10 import java.util.ArrayList;
11 import java.util.List;
13 import javax.swing.JButton;
14 import javax.swing.JCheckBox;
15 import javax.swing.JDialog;
16 import javax.swing.JLabel;
17 import javax.swing.JPanel;
18 import javax.swing.JScrollPane;
19 import javax.swing.JTable;
20 import javax.swing.JTextField;
21 import javax.swing.ListSelectionModel;
22 import javax.swing.RowFilter;
23 import javax.swing.event.DocumentEvent;
24 import javax.swing.event.DocumentListener;
25 import javax.swing.table.TableModel;
26 import javax.swing.table.TableRowSorter;
28 import net.miginfocom.swing.MigLayout;
29 import net.sf.openrocket.gui.adaptors.Column;
30 import net.sf.openrocket.gui.adaptors.ColumnTableModel;
31 import net.sf.openrocket.gui.util.GUIUtil;
32 import net.sf.openrocket.l10n.Translator;
33 import net.sf.openrocket.preset.ComponentPreset;
34 import net.sf.openrocket.preset.TypedKey;
35 import net.sf.openrocket.rocketcomponent.InternalComponent;
36 import net.sf.openrocket.rocketcomponent.RocketComponent;
37 import net.sf.openrocket.rocketcomponent.SymmetricComponent;
38 import net.sf.openrocket.startup.Application;
39 import net.sf.openrocket.unit.Value;
41 public class ComponentPresetChooserDialog extends JDialog {
43 private static final Translator trans = Application.getTranslator();
45 private final RocketComponent component;
47 private final JTable componentSelectionTable;
48 private final TableRowSorter<TableModel> sorter;
49 private final JTextField filterText;
50 private final JCheckBox foreDiameterFilterCheckBox;
51 private final JCheckBox aftDiameterFilterCheckBox;
54 * outerDiamtereColumnIndex is the index of the column associated with the OUTER_DIAMETER
55 * field. This index is needed by the matchOuterDiameterCheckBox to implement filtering.
57 int aftDiameterColumnIndex = -1;
58 int foreDiameterColumnIndex = -1;
60 private final List<ComponentPreset> presets;
62 private boolean okClicked = false;
65 public ComponentPresetChooserDialog(Window owner, RocketComponent component) {
66 super(owner, trans.get("title"), Dialog.ModalityType.APPLICATION_MODAL);
67 this.component = component;
69 final TypedKey<?>[] columnKeys = component.getPresetType().getDisplayedColumns();
71 presets = Application.getComponentPresetDao().listForType(component.getPresetType());
74 * Set up the Column Table model
76 final Column[] columns = new Column[columnKeys.length+1];
78 columns[0] = new Column(trans.get("table.column.Favorite") ) {
80 public Object getValueAt(int row) {
81 return Boolean.valueOf(ComponentPresetChooserDialog.this.presets.get(row).isFavorite());
85 public void setValueAt(int row, Object value) {
86 Application.getComponentPresetDao().setFavorite(ComponentPresetChooserDialog.this.presets.get(row), (Boolean) value);
90 public Class<?> getColumnClass() {
96 for (int i = 0; i < columnKeys.length; i++) {
97 final TypedKey<?> key = columnKeys[i];
98 if ( key == ComponentPreset.OUTER_DIAMETER ) {
99 // magic +1 is because we have inserted the column for favorites above.
100 aftDiameterColumnIndex = i+1;
102 if ( key == ComponentPreset.FORE_OUTER_DIAMETER ) {
103 // magic +1 is because we have inserted the column for favorites above.
104 foreDiameterColumnIndex = i+1;
106 columns[i+1] = new Column(trans.get("table.column." + key.getName())) {
108 public Object getValueAt(int row) {
109 ComponentPreset preset = ComponentPresetChooserDialog.this.presets.get(row);
110 if ( ! preset.has(key) ) {
113 Object value = preset.get(key);
114 if (key.getType() == Double.class && key.getUnitGroup() != null) {
115 return new Value( (Double) value, key.getUnitGroup() );
124 * perhaps there is a better way for this.
126 * This check basically says that if a component does not have a fore diameter, use the
127 * outer_diameter when filtering. The problem this introduced is when this dialog is
128 * created for nose cones (which are aft of a body tube), you will be given the option
129 * to filter based on matching fore diameter.
131 if ( foreDiameterColumnIndex < 0 ) {
132 foreDiameterColumnIndex = aftDiameterColumnIndex;
135 ColumnTableModel tableModel = new ColumnTableModel(columns) {
137 public int getRowCount() {
138 return ComponentPresetChooserDialog.this.presets.size();
142 public boolean isCellEditable(int rowIndex, int columnIndex) {
143 return columnIndex == 0;
150 * Add filter by text.
152 JPanel panel = new JPanel(new MigLayout("fill"));
153 JLabel filterLabel = new JLabel(trans.get("ComponentPresetChooserDialog.filter.label"));
154 panel.add(filterLabel);
155 filterText = new JTextField(15);
156 panel.add(filterText,"growx, growy 0, wrap");
157 filterText.getDocument().addDocumentListener(new DocumentListener() {
159 public void changedUpdate(DocumentEvent e) {
163 public void insertUpdate(DocumentEvent e) {
167 public void removeUpdate(DocumentEvent e) {
173 * Add filter by fore diameter
175 foreDiameterFilterCheckBox = new JCheckBox();
176 foreDiameterFilterCheckBox.setText(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter"));
177 panel.add(foreDiameterFilterCheckBox, "skip, span 2");
178 foreDiameterFilterCheckBox.addItemListener( new ItemListener () {
180 public void itemStateChanged(ItemEvent e) {
185 /* hide the fore diameter filter if it is not applicable */
186 if ( foreDiameterColumnIndex < 0 || component.getPreviousComponent() == null ) {
187 foreDiameterFilterCheckBox.setVisible(false);
191 * Add filter by aft diameter
193 aftDiameterFilterCheckBox = new JCheckBox();
194 aftDiameterFilterCheckBox.setText(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter"));
195 panel.add(aftDiameterFilterCheckBox, "skip, span 2, wrap");
196 aftDiameterFilterCheckBox.addItemListener( new ItemListener () {
198 public void itemStateChanged(ItemEvent e) {
203 /* hide the aft diameter filter if it is not applicable */
204 if ( aftDiameterColumnIndex < 0 || component.getNextComponent() == null ) {
205 aftDiameterFilterCheckBox.setVisible(false);
208 componentSelectionTable = new JTable( tableModel );
210 componentSelectionTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
212 sorter = new TableRowSorter<TableModel>(tableModel);
213 componentSelectionTable.setRowSorter(sorter);
215 JScrollPane scrollpane = new JScrollPane();
216 scrollpane.setViewportView(componentSelectionTable);
217 panel.add(scrollpane, "grow, width :500:, height :300:, spanx, wrap para");
220 // OK / Cancel buttons
221 JButton okButton = new JButton(trans.get("dlg.but.ok"));
222 okButton.addActionListener(new ActionListener() {
224 public void actionPerformed(ActionEvent e) {
228 panel.add(okButton, "tag ok, spanx, split");
231 JButton cancelButton = new JButton(trans.get("dlg.but.cancel"));
232 cancelButton.addActionListener(new ActionListener() {
234 public void actionPerformed(ActionEvent e) {
238 panel.add(cancelButton, "tag cancel");
244 this.setLocationByPlatform(true);
245 GUIUtil.setDisposableDialogOptions(this, okButton);
247 //JComponent focus = selectionPanel.getDefaultFocus();
248 //if (focus != null) {
249 // focus.grabFocus();
252 // Set the closeable dialog after all initialization
253 //selectionPanel.setCloseableDialog(this);
257 * Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted.
259 * @return the selected motor, or <code>null</code> if no motor has been selected or the selection was canceled.
261 public ComponentPreset getSelectedComponentPreset() {
264 int row = componentSelectionTable.getSelectedRow();
265 row = componentSelectionTable.convertRowIndexToModel(row);
266 return presets.get(row);
269 public void close(boolean ok) {
271 this.setVisible(false);
274 private void updateFilters() {
275 List<RowFilter<TableModel,Object>> filters = new ArrayList<RowFilter<TableModel,Object>> (2);
276 String filterTextRegex = filterText.getText();
277 if ( filterTextRegex != null ) {
279 // The "(?iu)" magic turns on case insensitivity with unicode chars
280 RowFilter<TableModel,Object> regexFilter = RowFilter.regexFilter("(?iu)"+filterTextRegex);
281 filters.add(regexFilter);
282 } catch ( java.util.regex.PatternSyntaxException e ) {
285 if ( aftDiameterFilterCheckBox.isSelected() ) {
286 // FIXME - please verify this logic looks correct.
287 // Grab the next component.
288 // If this.component is an InternalComponent, then we want to filter the outer diameter field
289 // against the next component's inner diameter.
290 RocketComponent nextComponent = component.getNextComponent();
291 if ( nextComponent != null && nextComponent instanceof SymmetricComponent ) {
292 SymmetricComponent parent = (SymmetricComponent) nextComponent;
294 if ( this.component instanceof InternalComponent ) {
295 nextDiameter = parent.getInnerRadius(0) * 2.0;
297 nextDiameter = parent.getForeRadius() * 2.0;
299 RowFilter<TableModel,Object> outerDiameterFilter = new ComponentPresetRowFilter( nextDiameter, aftDiameterColumnIndex);
300 filters.add(outerDiameterFilter);
303 if ( foreDiameterFilterCheckBox.isSelected() ) {
304 // FIXME - please verify this logic looks correct.
305 // Grab the previous component.
306 // If this.component is an InternalComponent, then we want to filter the outer diameter field
307 // against the previous component's inner diameter.
308 RocketComponent previousComponent = component.getPreviousComponent();
309 if ( previousComponent != null && previousComponent instanceof SymmetricComponent ) {
310 SymmetricComponent parent = (SymmetricComponent) previousComponent;
311 double previousDaimeter;
312 if ( this.component instanceof InternalComponent ) {
313 previousDaimeter = parent.getInnerRadius(parent.getLength()) * 2.0;
315 previousDaimeter = parent.getAftRadius() * 2.0;
317 RowFilter<TableModel,Object> outerDiameterFilter = new ComponentPresetRowFilter( previousDaimeter, foreDiameterColumnIndex);
318 filters.add(outerDiameterFilter);
322 sorter.setRowFilter( RowFilter.andFilter(filters) );