4c967a92c8045b653f05ee8df676e85a6e9238e8
[debian/openrocket] / core / src / net / sf / openrocket / gui / dialogs / preset / ComponentPresetChooserDialog.java
1 package net.sf.openrocket.gui.dialogs.preset;
2
3
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.Arrays;
12 import java.util.List;
13
14 import javax.swing.JButton;
15 import javax.swing.JCheckBox;
16 import javax.swing.JDialog;
17 import javax.swing.JLabel;
18 import javax.swing.JPanel;
19 import javax.swing.JScrollPane;
20 import javax.swing.JTable;
21 import javax.swing.JTextField;
22 import javax.swing.ListSelectionModel;
23 import javax.swing.RowFilter;
24 import javax.swing.event.DocumentEvent;
25 import javax.swing.event.DocumentListener;
26 import javax.swing.table.TableModel;
27 import javax.swing.table.TableRowSorter;
28
29 import net.miginfocom.swing.MigLayout;
30 import net.sf.openrocket.gui.adaptors.Column;
31 import net.sf.openrocket.gui.adaptors.ColumnTableModel;
32 import net.sf.openrocket.gui.util.GUIUtil;
33 import net.sf.openrocket.l10n.Translator;
34 import net.sf.openrocket.preset.ComponentPreset;
35 import net.sf.openrocket.preset.TypedKey;
36 import net.sf.openrocket.rocketcomponent.InternalComponent;
37 import net.sf.openrocket.rocketcomponent.RocketComponent;
38 import net.sf.openrocket.rocketcomponent.SymmetricComponent;
39 import net.sf.openrocket.startup.Application;
40 import net.sf.openrocket.unit.Value;
41
42 public class ComponentPresetChooserDialog extends JDialog {
43         
44         private static final Translator trans = Application.getTranslator();
45         
46         private final RocketComponent component;
47         
48         private ComponentPresetTable componentSelectionTable;
49 //      private final JTable componentSelectionTable;
50         private final JTextField filterText;
51         private final JCheckBox foreDiameterFilterCheckBox;
52         private final JCheckBox aftDiameterFilterCheckBox;
53         
54         /*
55          * outerDiamtereColumnIndex is the index of the column associated with the OUTER_DIAMETER
56          * field.  This index is needed by the matchOuterDiameterCheckBox to implement filtering.
57          */
58         int aftDiameterColumnIndex = -1;
59         int foreDiameterColumnIndex = -1;
60
61         private final List<ComponentPreset> presets;
62         
63         private boolean okClicked = false;
64         
65         
66         public ComponentPresetChooserDialog(Window owner, RocketComponent component) {
67                 super(owner, trans.get("title"), Dialog.ModalityType.APPLICATION_MODAL);
68                 this.component = component;
69                 
70                 final TypedKey<?>[] columnKeys = component.getPresetType().getDisplayedColumns();
71
72                 presets = Application.getComponentPresetDao().listForType(component.getPresetType());
73
74                 for (int i = 0; i < columnKeys.length; i++) {
75                         final TypedKey<?> key = columnKeys[i];
76                         if ( key == ComponentPreset.OUTER_DIAMETER ) {
77                                 // magic +1 is because we have inserted the column for favorites above.
78                                 aftDiameterColumnIndex = i+1;
79                         }
80                         if ( key == ComponentPreset.FORE_OUTER_DIAMETER ) {
81                                 // magic +1 is because we have inserted the column for favorites above.
82                                 foreDiameterColumnIndex = i+1;
83                         }
84                 }
85                 
86                 /*
87                  * perhaps there is a better way for this.
88                  * 
89                  * This check basically says that if a component does not have a fore diameter, use the
90                  * outer_diameter when filtering.  The problem this introduced is when this dialog is
91                  * created for nose cones (which are aft of a body tube), you will be given the option
92                  * to filter based on matching fore diameter.
93                  */
94                 if ( foreDiameterColumnIndex < 0 ) {
95                         foreDiameterColumnIndex = aftDiameterColumnIndex;
96                 }
97                 
98                 /*
99                  * Add filter by text.
100                  */
101                 JPanel panel = new JPanel(new MigLayout("fill"));
102                 JLabel filterLabel = new JLabel(trans.get("ComponentPresetChooserDialog.filter.label"));
103                 panel.add(filterLabel);
104                 filterText = new JTextField(15);
105                 panel.add(filterText,"growx, growy 0, wrap");
106                 filterText.getDocument().addDocumentListener(new DocumentListener() {
107                         @Override
108                         public void changedUpdate(DocumentEvent e) {
109                                 updateFilters();
110                         }
111                         @Override
112                         public void insertUpdate(DocumentEvent e) {
113                                 updateFilters();
114                         }
115                         @Override
116                         public void removeUpdate(DocumentEvent e) {
117                                 updateFilters();
118                         }
119                 });
120                 
121                 /*
122                  * Add filter by fore diameter
123                  */
124                 foreDiameterFilterCheckBox = new JCheckBox();
125                 foreDiameterFilterCheckBox.setText(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter"));
126                 panel.add(foreDiameterFilterCheckBox, "skip, span 2");
127                 foreDiameterFilterCheckBox.addItemListener( new ItemListener () {
128                         @Override
129                         public void itemStateChanged(ItemEvent e) {
130                                 updateFilters();
131                         }
132                 });
133
134                 /* hide the fore diameter filter if it is not applicable */
135                 if ( foreDiameterColumnIndex < 0 || component.getPreviousComponent() == null ) {
136                         foreDiameterFilterCheckBox.setVisible(false);
137                 }
138                 
139                 /*
140                  * Add filter by aft diameter
141                  */
142                 aftDiameterFilterCheckBox = new JCheckBox();
143                 aftDiameterFilterCheckBox.setText(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter"));
144                 panel.add(aftDiameterFilterCheckBox, "skip, span 2, wrap");
145                 aftDiameterFilterCheckBox.addItemListener( new ItemListener () {
146                         @Override
147                         public void itemStateChanged(ItemEvent e) {
148                                 updateFilters();
149                         }
150                 });
151
152                 /* hide the aft diameter filter if it is not applicable */
153                 if ( aftDiameterColumnIndex < 0 || component.getNextComponent() == null ) {
154                         aftDiameterFilterCheckBox.setVisible(false);
155                 }
156                 
157                 componentSelectionTable = new ComponentPresetTable( presets, Arrays.<TypedKey<?>>asList(columnKeys) );
158                 
159
160                 JScrollPane scrollpane = new JScrollPane();
161                 scrollpane.setViewportView(componentSelectionTable);
162                 panel.add(scrollpane, "grow, width :500:, height :300:, spanx, wrap para");
163
164                 
165                 // OK / Cancel buttons
166                 JButton okButton = new JButton(trans.get("dlg.but.ok"));
167                 okButton.addActionListener(new ActionListener() {
168                         @Override
169                         public void actionPerformed(ActionEvent e) {
170                                 close(true);
171                         }
172                 });
173                 panel.add(okButton, "tag ok, spanx, split");
174                 
175                 //// Cancel button
176                 JButton cancelButton = new JButton(trans.get("dlg.but.cancel"));
177                 cancelButton.addActionListener(new ActionListener() {
178                         @Override
179                         public void actionPerformed(ActionEvent e) {
180                                 close(false);
181                         }
182                 });
183                 panel.add(cancelButton, "tag cancel");
184                 
185                 this.add(panel);
186                 
187                 this.setModal(true);
188                 this.pack();
189                 this.setLocationByPlatform(true);
190                 GUIUtil.setDisposableDialogOptions(this, okButton);
191                 
192                 //JComponent focus = selectionPanel.getDefaultFocus();
193                 //if (focus != null) {
194                 //      focus.grabFocus();
195                 //}
196                 
197                 // Set the closeable dialog after all initialization
198                 //selectionPanel.setCloseableDialog(this);
199         }
200         
201         /**
202          * Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted.
203          * 
204          * @return      the selected motor, or <code>null</code> if no motor has been selected or the selection was canceled.
205          */
206         public ComponentPreset getSelectedComponentPreset() {
207                 if (!okClicked)
208                         return null;
209                 int row = componentSelectionTable.getSelectedRow();
210                 row = componentSelectionTable.convertRowIndexToModel(row);
211                 return presets.get(row);
212         }
213         
214         public void close(boolean ok) {
215                 okClicked = ok;
216                 this.setVisible(false);
217         }
218         
219         private void updateFilters() {
220                 List<RowFilter<TableModel,Object>> filters = new ArrayList<RowFilter<TableModel,Object>> (2);
221                 String filterTextRegex = filterText.getText();
222                 if ( filterTextRegex != null ) {
223                         try {
224                                 // The "(?iu)" magic turns on case insensitivity with unicode chars
225                                 RowFilter<TableModel,Object> regexFilter = RowFilter.regexFilter("(?iu)"+filterTextRegex);
226                                 filters.add(regexFilter);
227                         } catch ( java.util.regex.PatternSyntaxException e ) {
228                         }
229                 }
230                 if ( aftDiameterFilterCheckBox.isSelected() ) {
231                         // FIXME - please verify this logic looks correct.
232                         // Grab the next component.
233                         // If this.component is an InternalComponent, then we want to filter the outer diameter field
234                         // against the next component's inner diameter.
235                         RocketComponent nextComponent = component.getNextComponent();
236                         if ( nextComponent != null && nextComponent instanceof SymmetricComponent ) {
237                                 SymmetricComponent parent = (SymmetricComponent) nextComponent;
238                                 double nextDiameter;
239                                 if ( this.component instanceof InternalComponent ) {
240                                         nextDiameter = parent.getInnerRadius(0) * 2.0;
241                                 } else {
242                                         nextDiameter = parent.getForeRadius() * 2.0;
243                                 }
244                                 RowFilter<TableModel,Object> outerDiameterFilter = new ComponentPresetRowFilter( nextDiameter, aftDiameterColumnIndex);
245                                 filters.add(outerDiameterFilter);
246                         }
247                 }
248                 if ( foreDiameterFilterCheckBox.isSelected() ) {
249                         // FIXME - please verify this logic looks correct.
250                         // Grab the previous component.
251                         // If this.component is an InternalComponent, then we want to filter the outer diameter field
252                         // against the previous component's inner diameter.
253                         RocketComponent previousComponent = component.getPreviousComponent();
254                         if ( previousComponent != null && previousComponent instanceof SymmetricComponent ) {
255                                 SymmetricComponent parent = (SymmetricComponent) previousComponent;
256                                 double previousDaimeter;
257                                 if ( this.component instanceof InternalComponent ) {
258                                         previousDaimeter = parent.getInnerRadius(parent.getLength()) * 2.0;
259                                 } else {
260                                         previousDaimeter = parent.getAftRadius() * 2.0;
261                                 }
262                                 RowFilter<TableModel,Object> outerDiameterFilter = new ComponentPresetRowFilter( previousDaimeter, foreDiameterColumnIndex);
263                                 filters.add(outerDiameterFilter);
264                         }
265                 }
266                 
267                 componentSelectionTable.setRowFilter( RowFilter.andFilter(filters) );
268         }
269 }