+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;
+
+/**
+ * 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 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;
+ }
+}