package net.sf.openrocket.gui.dialogs.preset; import java.awt.Dialog; import java.awt.Window; import java.awt.event.ActionEvent; 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; import javax.swing.JCheckBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; import javax.swing.RowFilter; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.table.TableColumn; import javax.swing.table.TableModel; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.preset.ComponentPreset; import net.sf.openrocket.preset.TypedKey; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.util.Chars; /** * Dialog shown for selecting a preset component. */ public class ComponentPresetChooserDialog extends JDialog { private static final Translator trans = Application.getTranslator(); private final RocketComponent component; private ComponentPresetTable componentSelectionTable; private JTextField filterText; private JCheckBox foreDiameterFilterCheckBox; private JCheckBox aftDiameterFilterCheckBox; private ComponentPresetRowFilter foreDiameterFilter; private ComponentPresetRowFilter aftDiameterFilter; /* * outerDiamtereColumnIndex is the index of the column associated with the OUTER_DIAMETER * field. This index is needed by the matchOuterDiameterCheckBox to implement filtering. */ int aftDiameterColumnIndex = -1; int foreDiameterColumnIndex = -1; private List<ComponentPreset> presets; private ComponentPreset.Type presetType; private boolean okClicked = false; public ComponentPresetChooserDialog(Window owner, RocketComponent component) { super(owner, trans.get("title"), Dialog.ModalityType.APPLICATION_MODAL); this.component = component; this.presetType = component.getPresetType(); this.presets = Application.getComponentPresetDao().listForType(component.getPresetType()); List<TypedKey<?>> displayedColumnKeys = Arrays.asList(component.getPresetType().getDisplayedColumns()); { final List<TypedKey<?>> columnKeys = ComponentPreset.ORDERED_KEY_LIST; int i = 0; // We start at 0 but use preincrement because the first column is favorite. for (final TypedKey<?> key : columnKeys) { // Note the increment early in the loop. This really means that initial loop i=1 // we do it here so the continue below doesn't mess up the counting. i++; // Don't allow the matching filters if the column is not part of the default set for // this kind of preset. if (!displayedColumnKeys.contains(key)) { continue; } if (key == ComponentPreset.OUTER_DIAMETER || key == ComponentPreset.AFT_OUTER_DIAMETER) { aftDiameterColumnIndex = i; } if (key == ComponentPreset.OUTER_DIAMETER || key == ComponentPreset.FORE_OUTER_DIAMETER) { foreDiameterColumnIndex = i; } } } JPanel panel = new JPanel(new MigLayout("fill, ins para")); /* * Add filter by text. */ JPanel sub = new JPanel(new MigLayout("fill, ins 0")); JLabel filterLabel = new JLabel(trans.get("ComponentPresetChooserDialog.filter.label")); sub.add(filterLabel, "gapright para"); filterText = new JTextField(); sub.add(filterText, "growx"); filterText.getDocument().addDocumentListener(new DocumentListener() { @Override public void changedUpdate(DocumentEvent e) { updateFilters(); } @Override public void insertUpdate(DocumentEvent e) { updateFilters(); } @Override public void removeUpdate(DocumentEvent e) { updateFilters(); } }); panel.add(sub, "growx, ay 0, gapright para"); panel.add(getFilterCheckboxes(), "wrap para"); componentSelectionTable = new ComponentPresetTable(presetType, presets, displayedColumnKeys); // GUIUtil.setAutomaticColumnTableWidths(componentSelectionTable, 20); int w = componentSelectionTable.getRowHeight() + 4; TableColumn tc = componentSelectionTable.getColumnModel().getColumn(0); tc.setPreferredWidth(w); tc.setMaxWidth(w); tc.setMinWidth(w); JScrollPane scrollpane = new JScrollPane(); scrollpane.setViewportView(componentSelectionTable); panel.add(scrollpane, "grow, width 700lp, height 300lp, spanx, wrap rel"); panel.add(new JLabel(Chars.UP_ARROW + " " + trans.get("lbl.favorites")), "spanx, gapleft 5px, wrap para"); // OK / Cancel buttons JButton okButton = new JButton(trans.get("dlg.but.ok")); okButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { close(true); } }); panel.add(okButton, "tag ok, spanx, split"); //// Cancel button JButton cancelButton = new JButton(trans.get("dlg.but.cancel")); cancelButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { close(false); } }); panel.add(cancelButton, "tag cancel"); this.add(panel); GUIUtil.rememberWindowSize(this); GUIUtil.setDisposableDialogOptions(this, okButton); } private JPanel getFilterCheckboxes() { SymmetricComponent sc; JPanel panel = new JPanel(new MigLayout("fill, ins 0")); /* * Add show all compatible check box. */ final List<ComponentPreset.Type> compatibleTypes = component.getPresetType().getCompatibleTypes(); final ComponentPreset.Type nativeType = component.getPresetType(); if (compatibleTypes != null && compatibleTypes.size() > 0) { JCheckBox showAll = new JCheckBox(); showAll.setText(trans.get("ComponentPresetChooserDialog.checkbox.showAllCompatible")); panel.add(showAll, "wrap"); showAll.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { if (((JCheckBox) e.getItem()).isSelected()) { presets = Application.getComponentPresetDao().listForTypes(compatibleTypes); } else { presets = Application.getComponentPresetDao().listForType(nativeType); } componentSelectionTable.updateData(presets); } }); } /* * Add filter by fore diameter */ foreDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter")); sc = getPreviousSymmetricComponent(); if (sc != null && foreDiameterColumnIndex >= 0) { foreDiameterFilter = new ComponentPresetRowFilter(sc.getAftRadius() * 2.0, foreDiameterColumnIndex); panel.add(foreDiameterFilterCheckBox, "wrap"); foreDiameterFilterCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { updateFilters(); } }); } /* * Add filter by aft diameter */ aftDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter")); sc = getNextSymmetricComponent(); if (sc != null && aftDiameterColumnIndex >= 0) { aftDiameterFilter = new ComponentPresetRowFilter(sc.getForeRadius() * 2.0, aftDiameterColumnIndex); panel.add(aftDiameterFilterCheckBox, "wrap"); aftDiameterFilterCheckBox.addItemListener(new ItemListener() { @Override public void itemStateChanged(ItemEvent e) { updateFilters(); } }); } return panel; } /** * Return the motor selected by this chooser dialog, or <code>null</code> if the selection has been aborted. * * @return the selected motor, or <code>null</code> if no motor has been selected or the selection was canceled. */ public ComponentPreset getSelectedComponentPreset() { if (!okClicked) return null; int row = componentSelectionTable.getSelectedRow(); if (row < 0) { // Nothing selected. return null; } row = componentSelectionTable.convertRowIndexToModel(row); return presets.get(row); } public void close(boolean ok) { okClicked = ok; this.setVisible(false); } private void updateFilters() { List<RowFilter<TableModel, Object>> filters = new ArrayList<RowFilter<TableModel, Object>>(2); String filterTextRegex = filterText.getText(); if (filterTextRegex != null) { try { // The "(?iu)" magic turns on case insensitivity with unicode chars RowFilter<TableModel, Object> regexFilter = RowFilter.regexFilter("(?iu)" + filterTextRegex); filters.add(regexFilter); } catch (java.util.regex.PatternSyntaxException e) { } } if (aftDiameterFilterCheckBox.isSelected()) { filters.add(aftDiameterFilter); } if (foreDiameterFilterCheckBox.isSelected()) { filters.add(foreDiameterFilter); } componentSelectionTable.setRowFilter(RowFilter.andFilter(filters)); } private SymmetricComponent getPreviousSymmetricComponent() { RocketComponent c = component; while (c != null) { c = c.getPreviousComponent(); if (c instanceof SymmetricComponent) { return (SymmetricComponent) c; } } return null; } private SymmetricComponent getNextSymmetricComponent() { RocketComponent c = component; while (c != null) { c = c.getNextComponent(); if (c instanceof SymmetricComponent) { return (SymmetricComponent) c; } } return null; } }