From 96f3e671b040ca7c60478b554d02827fc0587c83 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 28 Jul 2022 00:48:50 +0200 Subject: [PATCH 01/21] [#1558] Restore table focus --- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 1 + .../gui/main/flightconfigpanel/FlightConfigurablePanel.java | 1 + .../gui/main/flightconfigpanel/MotorConfigurationPanel.java | 6 ++++++ .../main/flightconfigpanel/RecoveryConfigurationPanel.java | 4 ++++ .../flightconfigpanel/SeparationConfigurationPanel.java | 4 ++++ 5 files changed, 16 insertions(+) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index f8ac84ec5..8c946d682 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -776,6 +776,7 @@ public class SimulationPanel extends JPanel { break; simulationTable.addRowSelectionInterval(row, row); } + simulationTable.requestFocusInWindow(); } class EditSimulationAction extends AbstractAction { diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 593cbdc8a..fb2856194 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -134,6 +134,7 @@ public abstract class FlightConfigurablePanel if (update) { fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } else { + table.requestFocusInWindow(); } } @@ -420,6 +422,8 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel if (update) { fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } else { + table.requestFocusInWindow(); } } @@ -448,6 +452,8 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel if (update) { fireTableDataChanged(ComponentChangeEvent.MOTOR_CHANGE); + } else { + table.requestFocusInWindow(); } } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index 11c991e1f..768ccb057 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -235,6 +235,8 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel Date: Thu, 28 Jul 2022 01:06:55 +0200 Subject: [PATCH 02/21] Clean up structure a bit (I'm doing this for a later commit I will push in this PR) --- .../sf/openrocket/gui/main/BasicFrame.java | 2 +- .../FlightConfigurationPanel.java | 6 +- .../FlightConfigurablePanel.java | 666 +++++++++--------- .../MotorConfigurationPanel.java | 5 +- .../RecoveryConfigurationPanel.java | 3 +- .../SeparationConfigurationPanel.java | 3 +- 6 files changed, 345 insertions(+), 340 deletions(-) rename swing/src/net/sf/openrocket/gui/main/{flightconfigpanel => }/FlightConfigurationPanel.java (97%) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 6f76a838d..3989a2295 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -60,7 +60,6 @@ import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.figure3d.photo.PhotoFrame; import net.sf.openrocket.gui.help.tours.GuidedTourSelectionDialog; import net.sf.openrocket.gui.main.componenttree.ComponentTree; -import net.sf.openrocket.gui.main.flightconfigpanel.FlightConfigurationPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.DummyFrameMenuOSX; import net.sf.openrocket.gui.util.FileHelper; @@ -276,6 +275,7 @@ public class BasicFrame extends JFrame { * with the left component the design tree and the right component buttons * for adding components. */ + // TODO LOW: Put this in a separate file... private JComponent designTab() { JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); horizontal.setResizeWeight(0.5); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java similarity index 97% rename from swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java rename to swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index 2e37fb14d..5c176c52b 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -1,4 +1,4 @@ -package net.sf.openrocket.gui.main.flightconfigpanel; +package net.sf.openrocket.gui.main; import java.awt.event.ActionEvent; import java.awt.event.MouseEvent; @@ -20,7 +20,9 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.dialogs.flightconfiguration.RenameConfigDialog; -import net.sf.openrocket.gui.main.BasicFrame; +import net.sf.openrocket.gui.main.flightconfigpanel.MotorConfigurationPanel; +import net.sf.openrocket.gui.main.flightconfigpanel.RecoveryConfigurationPanel; +import net.sf.openrocket.gui.main.flightconfigpanel.SeparationConfigurationPanel; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index fb2856194..6e5f2de84 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -1,334 +1,334 @@ -package net.sf.openrocket.gui.main.flightconfigpanel; - -import java.awt.Color; -import java.awt.Component; -import java.awt.Font; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; - -import javax.swing.JComponent; -import javax.swing.JLabel; -import javax.swing.JPanel; -import javax.swing.JTable; -import javax.swing.ListSelectionModel; -import javax.swing.UIManager; -import javax.swing.border.Border; -import javax.swing.border.EmptyBorder; -import javax.swing.event.ListSelectionEvent; -import javax.swing.event.ListSelectionListener; -import javax.swing.table.AbstractTableModel; -import javax.swing.table.DefaultTableCellRenderer; - -import net.sf.openrocket.util.ArrayList; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.miginfocom.swing.MigLayout; -import net.sf.openrocket.formatting.RocketDescriptor; -import net.sf.openrocket.gui.util.GUIUtil; -import net.sf.openrocket.l10n.Translator; -import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; -import net.sf.openrocket.rocketcomponent.ComponentChangeListener; -import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; -import net.sf.openrocket.rocketcomponent.FlightConfigurationId; -import net.sf.openrocket.rocketcomponent.Rocket; -import net.sf.openrocket.startup.Application; -import net.sf.openrocket.util.Pair; - - -@SuppressWarnings("serial") -public abstract class FlightConfigurablePanel extends JPanel implements ComponentChangeListener { - - protected static final Translator trans = Application.getTranslator(); - private static final Logger log = LoggerFactory.getLogger(FlightConfigurablePanel.class); - protected RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); - - protected final FlightConfigurationPanel flightConfigurationPanel; - protected final Rocket rocket; - protected final JTable table; - - public FlightConfigurablePanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { - super(new MigLayout("fill")); - this.flightConfigurationPanel = flightConfigurationPanel; - this.rocket = rocket; - table = doTableInitialization(); - - installTableListener(); - synchronizeConfigurationSelection(); - } - - /** - * Update the data in the table, with component change event type {cce} - * @param cce index of the ComponentChangeEvent to use (e.g. ComponentChangeEvent.NONFUNCTIONAL_CHANGE) - */ - public void fireTableDataChanged(int cce) { - int selectedRow = table.getSelectedRow(); - int selectedColumn = table.getSelectedColumn(); - this.rocket.fireComponentChangeEvent(cce); - ((AbstractTableModel)table.getModel()).fireTableDataChanged(); - restoreSelection(selectedRow,selectedColumn); - updateButtonState(); - } - - protected abstract void updateButtonState(); - - @Override - public void componentChanged(ComponentChangeEvent e) { - this.synchronizeConfigurationSelection(); - } - - /** - * Initialize the table using the specific implementation's initializeTable - * method and then select the row to match what the rocket's current selected - * configuration is. - * - * @return the JTable created - */ - private final JTable doTableInitialization() { - JTable table = this.initializeTable(); - FlightConfigurationId current = this.rocket.getSelectedConfiguration().getFlightConfigurationID(); - int col = (table.getColumnCount() > 1) ? table.getColumnCount() - 1 : 0; - for (int row = 0; row < table.getRowCount(); row++) { - FlightConfigurationId rowFCID = rocket.getId(row); - if (rowFCID.equals(current)) { - table.changeSelection(row, col, false, false); - break; - } - } - return table; - } - - protected final void synchronizeConfigurationSelection() { - FlightConfigurationId currentRocketFCID = rocket.getSelectedConfiguration().getFlightConfigurationID(); - FlightConfigurationId selectedFCID = getSelectedConfigurationId(); - - if ( currentRocketFCID == FlightConfigurationId.DEFAULT_VALUE_FCID ) { - // need to unselect - table.clearSelection(); - } else if ( !currentRocketFCID.equals(selectedFCID)){ - // Need to change selection - // We'll select the correct row, in the currently selected column. - int col = table.getSelectedColumn(); - if ( col < 0 ) { - col = (table.getColumnCount() > 1) ? table.getColumnCount() - 1 : 0; - } - - for( int rowNum = 0; rowNum < table.getRowCount(); rowNum++ ) { - FlightConfigurationId rowFCID = rocket.getId(rowNum ); - if ( rowFCID.equals(currentRocketFCID) ) { - table.changeSelection(rowNum, col, false, false); - break; - } - } - } - } - - protected void restoreSelection( int row, int col ) { - if ( row <= 0 || col <= 0 ) { - synchronizeConfigurationSelection(); - return; - } - if ( row >= table.getRowCount() || col >= table.getColumnCount() ) { - synchronizeConfigurationSelection(); - return; - } - table.changeSelection(row, col, true, false); - table.requestFocusInWindow(); - } - - protected void installTableListener() { - table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { - - @Override - public void valueChanged(ListSelectionEvent e) { - if ( e.getValueIsAdjusting() ) { - return; - } - - /* Find the selected row and set it as the current selected configuration - * for the rocket. This will propagate the event to ensure that other - * pieces of the UI are updated and match the table selection. - */ - int firstrow = e.getFirstIndex(); - int lastrow = e.getLastIndex(); - ListSelectionModel model = (ListSelectionModel) e.getSource(); - for( int row = firstrow; row <= lastrow; row ++) { - if ( model.isSelectedIndex(row) ) { - FlightConfigurationId fcid = (FlightConfigurationId) table.getValueAt(row, table.convertColumnIndexToView(0)); - rocket.setSelectedConfiguration(fcid); - return; - } - } - } - - }); - } - - /** - * Override this method to create the embedded JTable and it's backing Model. - * - * @return - */ - protected abstract JTable initializeTable(); - - protected T getSelectedComponent() { - - int col = table.convertColumnIndexToModel(table.getSelectedColumn()); - int row = table.convertRowIndexToModel(table.getSelectedRow()); - if ( row < 0 || col < 0 ) { - return null; - } - Object tableValue = table.getModel().getValueAt(row, col); - if ( tableValue instanceof Pair ) { - @SuppressWarnings("unchecked") - Pair selectedComponent = (Pair) tableValue; - return selectedComponent.getV(); - } - return null; - } - - protected List getSelectedComponents() { - int[] cols = Arrays.stream(table.getSelectedColumns()).map(table::convertRowIndexToModel).toArray(); - int[] rows = Arrays.stream(table.getSelectedRows()).map(table::convertRowIndexToModel).toArray(); - if (Arrays.stream(cols).min().isEmpty() || Arrays.stream(rows).min().isEmpty() || - Arrays.stream(cols).min().getAsInt() < 0 || Arrays.stream(rows).min().getAsInt() < 0) { - return null; - } - List components = new ArrayList<>(); - for (int row : rows) { - for (int col : cols) { - Object tableValue = table.getModel().getValueAt(row, col); - if (tableValue instanceof Pair) { - @SuppressWarnings("unchecked") - Pair selectedComponent = (Pair) tableValue; - components.add(selectedComponent.getV()); - } - } - } - return components; - } - - protected FlightConfigurationId getSelectedConfigurationId() { - int col = table.convertColumnIndexToModel(table.getSelectedColumn()); - int row = table.convertRowIndexToModel(table.getSelectedRow()); - if ( row < 0 || col < 0 || row >= table.getRowCount() || col >= table.getColumnCount() ) { - return null; - } - Object tableValue = table.getModel().getValueAt(row, col); - if ( tableValue instanceof Pair ) { - @SuppressWarnings("unchecked") - Pair selectedComponent = (Pair) tableValue; - FlightConfigurationId fcid = selectedComponent.getU(); - return fcid; - } else if ( tableValue instanceof FlightConfigurationId ){ - return (FlightConfigurationId) tableValue; - } - return FlightConfigurationId.ERROR_FCID; - } - - protected List getSelectedConfigurationIds() { - int col = table.convertColumnIndexToModel(table.getSelectedColumn()); - int[] rows = Arrays.stream(table.getSelectedRows()).map(table::convertRowIndexToModel).toArray(); - if (Arrays.stream(rows).min().isEmpty() || Arrays.stream(rows).min().getAsInt() < 0 || col < 0 || - Arrays.stream(rows).max().getAsInt() >= table.getRowCount() || col >= table.getColumnCount() ) { - return null; - } - Object[] tableValues = Arrays.stream(rows).mapToObj(c -> table.getModel().getValueAt(c, col)).toArray(); - List Ids = new ArrayList<>(); - for (Object tableValue : tableValues) { - if (tableValue instanceof Pair) { - @SuppressWarnings("unchecked") - Pair selectedComponent = (Pair) tableValue; - FlightConfigurationId fcid = selectedComponent.getU(); - Ids.add(fcid); - } else if (tableValue instanceof FlightConfigurationId) { - Ids.add((FlightConfigurationId) tableValue); - } else { - Ids.add(FlightConfigurationId.ERROR_FCID); - } - } - - return Ids; - } - - protected abstract class FlightConfigurableCellRenderer extends DefaultTableCellRenderer { - - @Override - public Component getTableCellRendererComponent(JTable table, Object newValue, boolean isSelected, boolean hasFocus, int row, int column) { - JLabel label = (JLabel) super.getTableCellRendererComponent(table, newValue, isSelected, hasFocus, row, column); - Object oldValue = table.getModel().getValueAt(row, column); - - // this block is more for the benefit of the reader than the computer -- - // this assignment is technically redundant, but useful to point out that the new value here is often null, - // while the old value seems to always be valid. - if( null == newValue ){ - log.warn("Detected null newValue to render... (oldValue: "+oldValue+")"); - newValue = oldValue; - } - - column = table.convertColumnIndexToModel(column); - switch (column) { - case 0: { - label.setText(descriptor.format(rocket, (FlightConfigurationId) oldValue)); - regular(label); - setSelected(label, table, isSelected, hasFocus); - return label; - } - default: { - @SuppressWarnings("unchecked") - Pair v = (Pair) oldValue; - - if(v!=null){ - FlightConfigurationId fcid = v.getU(); - T component = v.getV(); - label = format(component, fcid, label ); - } - for (Component c : label.getComponents()) { - if (c instanceof JLabel) { - setSelected((JLabel)c, table, isSelected, hasFocus); - } - } - setSelected(label, table, isSelected, hasFocus); - return label; - } - } - } - - private final void setSelected( JComponent c, JTable table, boolean isSelected, boolean hasFocus ) { - c.setOpaque(true); - if ( isSelected) { - c.setBackground(table.getSelectionBackground()); - c.setForeground((Color)UIManager.get("Table.selectionForeground")); - } else { - c.setBackground(table.getBackground()); - c.setForeground(c.getForeground()); - } - Border b = null; - if ( hasFocus ) { - if (isSelected) { - b = UIManager.getBorder("Table.focusSelectedCellHighlightBorder"); - } else { - b = UIManager.getBorder("Table.focusCellHighligtBorder"); - } - } else { - b = new EmptyBorder(1,1,1,1); - } - c.setBorder(b); - } - - protected final void shaded(JLabel label) { - GUIUtil.changeFontStyle(label, Font.ITALIC); - label.setForeground(Color.GRAY); - } - - protected final void regular(JLabel label) { - GUIUtil.changeFontStyle(label, Font.PLAIN); - label.setForeground(Color.BLACK); - } - - protected abstract JLabel format( T component, FlightConfigurationId configId, JLabel label ); - - } - +package net.sf.openrocket.gui.main.flightconfigpanel; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Font; +import java.util.Arrays; +import java.util.List; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JTable; +import javax.swing.ListSelectionModel; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import javax.swing.table.AbstractTableModel; +import javax.swing.table.DefaultTableCellRenderer; + +import net.sf.openrocket.gui.main.FlightConfigurationPanel; +import net.sf.openrocket.util.ArrayList; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.formatting.RocketDescriptor; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; +import net.sf.openrocket.rocketcomponent.ComponentChangeListener; +import net.sf.openrocket.rocketcomponent.FlightConfigurableComponent; +import net.sf.openrocket.rocketcomponent.FlightConfigurationId; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.startup.Application; +import net.sf.openrocket.util.Pair; + + +@SuppressWarnings("serial") +public abstract class FlightConfigurablePanel extends JPanel implements ComponentChangeListener { + + protected static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(FlightConfigurablePanel.class); + protected RocketDescriptor descriptor = Application.getInjector().getInstance(RocketDescriptor.class); + + protected final FlightConfigurationPanel flightConfigurationPanel; + protected final Rocket rocket; + protected final JTable table; + + public FlightConfigurablePanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + super(new MigLayout("fill")); + this.flightConfigurationPanel = flightConfigurationPanel; + this.rocket = rocket; + table = doTableInitialization(); + + installTableListener(); + synchronizeConfigurationSelection(); + } + + /** + * Update the data in the table, with component change event type {cce} + * @param cce index of the ComponentChangeEvent to use (e.g. ComponentChangeEvent.NONFUNCTIONAL_CHANGE) + */ + public void fireTableDataChanged(int cce) { + int selectedRow = table.getSelectedRow(); + int selectedColumn = table.getSelectedColumn(); + this.rocket.fireComponentChangeEvent(cce); + ((AbstractTableModel)table.getModel()).fireTableDataChanged(); + restoreSelection(selectedRow,selectedColumn); + updateButtonState(); + } + + public abstract void updateButtonState(); + + @Override + public void componentChanged(ComponentChangeEvent e) { + this.synchronizeConfigurationSelection(); + } + + /** + * Initialize the table using the specific implementation's initializeTable + * method and then select the row to match what the rocket's current selected + * configuration is. + * + * @return the JTable created + */ + private final JTable doTableInitialization() { + JTable table = this.initializeTable(); + FlightConfigurationId current = this.rocket.getSelectedConfiguration().getFlightConfigurationID(); + int col = (table.getColumnCount() > 1) ? table.getColumnCount() - 1 : 0; + for (int row = 0; row < table.getRowCount(); row++) { + FlightConfigurationId rowFCID = rocket.getId(row); + if (rowFCID.equals(current)) { + table.changeSelection(row, col, false, false); + break; + } + } + return table; + } + + public final void synchronizeConfigurationSelection() { + FlightConfigurationId currentRocketFCID = rocket.getSelectedConfiguration().getFlightConfigurationID(); + FlightConfigurationId selectedFCID = getSelectedConfigurationId(); + + if ( currentRocketFCID == FlightConfigurationId.DEFAULT_VALUE_FCID ) { + // need to unselect + table.clearSelection(); + } else if ( !currentRocketFCID.equals(selectedFCID)){ + // Need to change selection + // We'll select the correct row, in the currently selected column. + int col = table.getSelectedColumn(); + if ( col < 0 ) { + col = (table.getColumnCount() > 1) ? table.getColumnCount() - 1 : 0; + } + + for( int rowNum = 0; rowNum < table.getRowCount(); rowNum++ ) { + FlightConfigurationId rowFCID = rocket.getId(rowNum ); + if ( rowFCID.equals(currentRocketFCID) ) { + table.changeSelection(rowNum, col, false, false); + break; + } + } + } + } + + protected void restoreSelection( int row, int col ) { + if ( row <= 0 || col <= 0 ) { + synchronizeConfigurationSelection(); + return; + } + if ( row >= table.getRowCount() || col >= table.getColumnCount() ) { + synchronizeConfigurationSelection(); + return; + } + table.changeSelection(row, col, true, false); + table.requestFocusInWindow(); + } + + protected void installTableListener() { + table.getSelectionModel().addListSelectionListener(new ListSelectionListener() { + + @Override + public void valueChanged(ListSelectionEvent e) { + if ( e.getValueIsAdjusting() ) { + return; + } + + /* Find the selected row and set it as the current selected configuration + * for the rocket. This will propagate the event to ensure that other + * pieces of the UI are updated and match the table selection. + */ + int firstrow = e.getFirstIndex(); + int lastrow = e.getLastIndex(); + ListSelectionModel model = (ListSelectionModel) e.getSource(); + for( int row = firstrow; row <= lastrow; row ++) { + if ( model.isSelectedIndex(row) ) { + FlightConfigurationId fcid = (FlightConfigurationId) table.getValueAt(row, table.convertColumnIndexToView(0)); + rocket.setSelectedConfiguration(fcid); + return; + } + } + } + + }); + } + + /** + * Override this method to create the embedded JTable and it's backing Model. + * + * @return + */ + protected abstract JTable initializeTable(); + + protected T getSelectedComponent() { + + int col = table.convertColumnIndexToModel(table.getSelectedColumn()); + int row = table.convertRowIndexToModel(table.getSelectedRow()); + if ( row < 0 || col < 0 ) { + return null; + } + Object tableValue = table.getModel().getValueAt(row, col); + if ( tableValue instanceof Pair ) { + @SuppressWarnings("unchecked") + Pair selectedComponent = (Pair) tableValue; + return selectedComponent.getV(); + } + return null; + } + + protected List getSelectedComponents() { + int[] cols = Arrays.stream(table.getSelectedColumns()).map(table::convertRowIndexToModel).toArray(); + int[] rows = Arrays.stream(table.getSelectedRows()).map(table::convertRowIndexToModel).toArray(); + if (Arrays.stream(cols).min().isEmpty() || Arrays.stream(rows).min().isEmpty() || + Arrays.stream(cols).min().getAsInt() < 0 || Arrays.stream(rows).min().getAsInt() < 0) { + return null; + } + List components = new ArrayList<>(); + for (int row : rows) { + for (int col : cols) { + Object tableValue = table.getModel().getValueAt(row, col); + if (tableValue instanceof Pair) { + @SuppressWarnings("unchecked") + Pair selectedComponent = (Pair) tableValue; + components.add(selectedComponent.getV()); + } + } + } + return components; + } + + protected FlightConfigurationId getSelectedConfigurationId() { + int col = table.convertColumnIndexToModel(table.getSelectedColumn()); + int row = table.convertRowIndexToModel(table.getSelectedRow()); + if ( row < 0 || col < 0 || row >= table.getRowCount() || col >= table.getColumnCount() ) { + return null; + } + Object tableValue = table.getModel().getValueAt(row, col); + if ( tableValue instanceof Pair ) { + @SuppressWarnings("unchecked") + Pair selectedComponent = (Pair) tableValue; + FlightConfigurationId fcid = selectedComponent.getU(); + return fcid; + } else if ( tableValue instanceof FlightConfigurationId ){ + return (FlightConfigurationId) tableValue; + } + return FlightConfigurationId.ERROR_FCID; + } + + public List getSelectedConfigurationIds() { + int col = table.convertColumnIndexToModel(table.getSelectedColumn()); + int[] rows = Arrays.stream(table.getSelectedRows()).map(table::convertRowIndexToModel).toArray(); + if (Arrays.stream(rows).min().isEmpty() || Arrays.stream(rows).min().getAsInt() < 0 || col < 0 || + Arrays.stream(rows).max().getAsInt() >= table.getRowCount() || col >= table.getColumnCount() ) { + return null; + } + Object[] tableValues = Arrays.stream(rows).mapToObj(c -> table.getModel().getValueAt(c, col)).toArray(); + List Ids = new ArrayList<>(); + for (Object tableValue : tableValues) { + if (tableValue instanceof Pair) { + @SuppressWarnings("unchecked") + Pair selectedComponent = (Pair) tableValue; + FlightConfigurationId fcid = selectedComponent.getU(); + Ids.add(fcid); + } else if (tableValue instanceof FlightConfigurationId) { + Ids.add((FlightConfigurationId) tableValue); + } else { + Ids.add(FlightConfigurationId.ERROR_FCID); + } + } + + return Ids; + } + + protected abstract class FlightConfigurableCellRenderer extends DefaultTableCellRenderer { + + @Override + public Component getTableCellRendererComponent(JTable table, Object newValue, boolean isSelected, boolean hasFocus, int row, int column) { + JLabel label = (JLabel) super.getTableCellRendererComponent(table, newValue, isSelected, hasFocus, row, column); + Object oldValue = table.getModel().getValueAt(row, column); + + // this block is more for the benefit of the reader than the computer -- + // this assignment is technically redundant, but useful to point out that the new value here is often null, + // while the old value seems to always be valid. + if( null == newValue ){ + log.warn("Detected null newValue to render... (oldValue: "+oldValue+")"); + newValue = oldValue; + } + + column = table.convertColumnIndexToModel(column); + switch (column) { + case 0: { + label.setText(descriptor.format(rocket, (FlightConfigurationId) oldValue)); + regular(label); + setSelected(label, table, isSelected, hasFocus); + return label; + } + default: { + @SuppressWarnings("unchecked") + Pair v = (Pair) oldValue; + + if(v!=null){ + FlightConfigurationId fcid = v.getU(); + T component = v.getV(); + label = format(component, fcid, label ); + } + for (Component c : label.getComponents()) { + if (c instanceof JLabel) { + setSelected((JLabel)c, table, isSelected, hasFocus); + } + } + setSelected(label, table, isSelected, hasFocus); + return label; + } + } + } + + private final void setSelected( JComponent c, JTable table, boolean isSelected, boolean hasFocus ) { + c.setOpaque(true); + if ( isSelected) { + c.setBackground(table.getSelectionBackground()); + c.setForeground((Color)UIManager.get("Table.selectionForeground")); + } else { + c.setBackground(table.getBackground()); + c.setForeground(c.getForeground()); + } + Border b = null; + if ( hasFocus ) { + if (isSelected) { + b = UIManager.getBorder("Table.focusSelectedCellHighlightBorder"); + } else { + b = UIManager.getBorder("Table.focusCellHighligtBorder"); + } + } else { + b = new EmptyBorder(1,1,1,1); + } + c.setBorder(b); + } + + protected final void shaded(JLabel label) { + GUIUtil.changeFontStyle(label, Font.ITALIC); + label.setForeground(Color.GRAY); + } + + protected final void regular(JLabel label) { + GUIUtil.changeFontStyle(label, Font.PLAIN); + label.setForeground(Color.BLACK); + } + + protected abstract JLabel format( T component, FlightConfigurationId configId, JLabel label ); + + } + } \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java index 0974f9556..588f51041 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/MotorConfigurationPanel.java @@ -33,6 +33,7 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.dialogs.flightconfiguration.IgnitionSelectionDialog; import net.sf.openrocket.gui.dialogs.flightconfiguration.MotorMountConfigurationPanel; import net.sf.openrocket.gui.dialogs.motor.MotorChooserDialog; +import net.sf.openrocket.gui.main.FlightConfigurationPanel; import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.motor.IgnitionEvent; import net.sf.openrocket.motor.Motor; @@ -65,7 +66,7 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel private final JPopupMenu popupMenuFull; // popup menu containing all the options - MotorConfigurationPanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { + public MotorConfigurationPanel(final FlightConfigurationPanel flightConfigurationPanel, Rocket rocket) { super(flightConfigurationPanel, rocket); motorChooserDialog = new MotorChooserDialog(SwingUtilities.getWindowAncestor(flightConfigurationPanel)); @@ -294,7 +295,7 @@ public class MotorConfigurationPanel extends FlightConfigurablePanel flightConfigurationPanel.setSelectedComponents(components); } - protected void updateButtonState() { + public void updateButtonState() { if( configurationTableModel.getColumnCount() > 1 ) { showContent(); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index 768ccb057..57f83a7e5 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -25,6 +25,7 @@ import javax.swing.event.ListSelectionListener; import net.sf.openrocket.formatting.RocketDescriptor; import net.sf.openrocket.gui.dialogs.flightconfiguration.DeploymentSelectionDialog; +import net.sf.openrocket.gui.main.FlightConfigurationPanel; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.*; import net.sf.openrocket.rocketcomponent.DeploymentConfiguration.DeployEvent; @@ -43,7 +44,7 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel Date: Thu, 28 Jul 2022 01:33:49 +0200 Subject: [PATCH 03/21] Refactor BasicFrame Move DesignPanel into separate file --- .../sf/openrocket/gui/main/BasicFrame.java | 280 ++++-------------- .../sf/openrocket/gui/main/DesignPanel.java | 228 ++++++++++++++ 2 files changed, 279 insertions(+), 229 deletions(-) create mode 100644 swing/src/net/sf/openrocket/gui/main/DesignPanel.java diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 3989a2295..8fe487faf 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -141,7 +141,9 @@ public class BasicFrame extends JFrame { /** Actions available for rocket modifications */ private final RocketActions actions; - private SimulationPanel simulationPanel; + private final DesignPanel designPanel; + private final FlightConfigurationPanel flightConfigurationPanel; + private final SimulationPanel simulationPanel; public static BasicFrame lastFrameInstance = null; // Latest BasicFrame that was created private static boolean quitCalled = false; // Keeps track whether the quit action has been called @@ -165,57 +167,62 @@ public class BasicFrame extends JFrame { componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); - // Obtain the simulation selection model that will be used - simulationPanel = new SimulationPanel(document); - simulationSelectionModel = simulationPanel.getSimulationListSelectionModel(); - - // Combine into a DocumentSelectionModel - selectionModel = new DocumentSelectionModel(document); - selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); - selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); - - actions = new RocketActions(document, selectionModel, this, simulationPanel); - - // Populate the popup menu - popupMenu = new JPopupMenu(); - popupMenu.add(actions.getEditAction()); - popupMenu.add(actions.getCutAction()); - popupMenu.add(actions.getCopyAction()); - popupMenu.add(actions.getPasteAction()); - popupMenu.add(actions.getDuplicateAction()); - popupMenu.add(actions.getDeleteAction()); - popupMenu.addSeparator(); - popupMenu.add(actions.getScaleAction()); + // Create the component tree + tree = new ComponentTree(document); + tree.setSelectionModel(componentSelectionModel); + // ----- Create the different BasicFrame panels ----- log.debug("Constructing the BasicFrame UI"); - // The main vertical split pane + //// Bottom segment, rocket figure + rocketpanel = new RocketPanel(document, this); + rocketpanel.setSelectionModel(tree.getSelectionModel()); + + //// Top segment, tabbed pane + simulationPanel = new SimulationPanel(document); + { + // Obtain the simulation selection model that will be used + simulationSelectionModel = simulationPanel.getSimulationListSelectionModel(); + + // Combine into a DocumentSelectionModel + selectionModel = new DocumentSelectionModel(document); + selectionModel.attachComponentTreeSelectionModel(componentSelectionModel); + selectionModel.attachSimulationListSelectionModel(simulationSelectionModel); + + // Create RocketActions + actions = new RocketActions(document, selectionModel, this, simulationPanel); + } + designPanel = new DesignPanel(this, rocketpanel, document, tree); + flightConfigurationPanel = new FlightConfigurationPanel(this, document); + tabbedPane = new JTabbedPane(); + tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designPanel); + tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, flightConfigurationPanel); + tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); + + //// The main vertical split pane JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true); vertical.setResizeWeight(0.5); + vertical.setTopComponent(tabbedPane); + vertical.setBottomComponent(rocketpanel); this.add(vertical); - // The top tabbed pane - tabbedPane = new JTabbedPane(); - //// Rocket design - tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designTab()); - //// Flight configurations - tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, new FlightConfigurationPanel(this, document)); - //// Flight simulations - tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); + // Populate the popup menu + { + popupMenu = new JPopupMenu(); + popupMenu.add(actions.getEditAction()); + popupMenu.add(actions.getCutAction()); + popupMenu.add(actions.getCopyAction()); + popupMenu.add(actions.getPasteAction()); + popupMenu.add(actions.getDuplicateAction()); + popupMenu.add(actions.getDeleteAction()); + popupMenu.addSeparator(); + popupMenu.add(actions.getScaleAction()); + } // Add change listener to catch when the tabs are changed. This is to run simulations // automatically when the simulation tab is selected. tabbedPane.addChangeListener(new BasicFrame_changeAdapter(this)); - vertical.setTopComponent(tabbedPane); - - // Bottom segment, rocket figure - - rocketpanel = new RocketPanel(document, this); - vertical.setBottomComponent(rocketpanel); - - rocketpanel.setSelectionModel(tree.getSelectionModel()); - createMenu(); @@ -270,195 +277,6 @@ public class BasicFrame extends JFrame { } - /** - * Construct the "Rocket design" tab. This contains a horizontal split pane - * with the left component the design tree and the right component buttons - * for adding components. - */ - // TODO LOW: Put this in a separate file... - private JComponent designTab() { - JSplitPane horizontal = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, true); - horizontal.setResizeWeight(0.5); - - - // Upper-left segment, component tree - - JPanel panel = new JPanel(new MigLayout("fill, flowy", "[grow][grow 0]","[grow]")); - - tree = new ComponentTree(document); - tree.setSelectionModel(componentSelectionModel); - - // Remove JTree key events that interfere with menu accelerators - InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), null); - im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), null); - - // Visually select all child components of a stage/rocket/podset when it is selected - componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { - @Override - public void valueChanged(TreeSelectionEvent e) { - if (tree == null || tree.getSelectionPaths() == null || tree.getSelectionPaths().length == 0 - || rocketpanel == null) return; - - // Get all the components that need to be selected = currently selected components + children of stages/boosters/podsets - List children = new ArrayList<>(Arrays.asList(rocketpanel.getFigure().getSelection())); - for (TreePath p : tree.getSelectionPaths()) { - if (p != null) { - RocketComponent c = (RocketComponent) p.getLastPathComponent(); - if (c instanceof AxialStage || c instanceof Rocket || c instanceof PodSet) { - Iterator iter = c.iterator(false); - while (iter.hasNext()) { - RocketComponent child = iter.next(); - children.add(child); - } - } - } - } - - // Select all the child components - if (rocketpanel.getFigure() != null && rocketpanel.getFigure3d() != null) { - rocketpanel.getFigure().setSelection(children.toArray(new RocketComponent[0])); - rocketpanel.getFigure3d().setSelection(children.toArray(new RocketComponent[0])); - } - } - }); - - // Double-click opens config dialog - MouseListener ml = new MouseAdapter() { - @Override - public void mouseClicked(MouseEvent e) { - int selRow = tree.getRowForLocation(e.getX(), e.getY()); - TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); - if (selRow != -1) { - if (selPath == null) return; - - // Double-click - if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2) && !ComponentConfigDialog.isDialogVisible()) { - RocketComponent component = (RocketComponent) selPath.getLastPathComponent(); - - // Multi-component edit if shift/meta key is pressed - if ((e.isShiftDown() || e.isMetaDown()) && tree.getSelectionPaths() != null) { - // Add the other selected components as listeners to the last selected component - for (TreePath p : tree.getSelectionPaths()) { - if (p != null) { - if (p.getLastPathComponent() == component) continue; - RocketComponent c = (RocketComponent) p.getLastPathComponent(); - c.clearConfigListeners(); - component.addConfigListener(c); - } - } - - // Add the selection path to the tree selection - List paths = new LinkedList<>(Arrays.asList(tree.getSelectionPaths())); - paths.add(selPath); - tree.setSelectionPaths(paths.toArray(new TreePath[0])); - } - - ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, component); - } - // Context menu - else if ((e.getButton() == MouseEvent.BUTTON3) && (e.getClickCount() == 1)) { - if (!tree.isPathSelected(selPath)) { - // Select new path - tree.setSelectionPath(selPath); - } - - doComponentTreePopup(e); - } - } else { // Clicked on blank space - tree.clearSelection(); - } - } - }; - tree.addMouseListener(ml); - - // Update dialog when selection is changed - componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { - @Override - public void valueChanged(TreeSelectionEvent e) { - // Scroll tree to the selected item - TreePath[] paths = componentSelectionModel.getSelectionPaths(); - if (paths == null || paths.length == 0) - return; - - for (TreePath path : paths) { - tree.scrollPathToVisible(path); - } - - if (!ComponentConfigDialog.isDialogVisible()) - return; - else - ComponentConfigDialog.disposeDialog(); - - RocketComponent c = (RocketComponent) paths[0].getLastPathComponent(); - c.clearConfigListeners(); - for (int i = 1; i < paths.length; i++) { - RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent(); - listener.clearConfigListeners(); - c.addConfigListener(listener); - } - ComponentConfigDialog.showDialog(BasicFrame.this, BasicFrame.this.document, c); - } - }); - - // Place tree inside scroll pane - JScrollPane scroll = new JScrollPane(tree); - panel.add(scroll, "spany, grow, wrap"); - - - // Buttons - JButton button = new SelectColorButton(actions.getMoveUpAction()); - panel.add(button, "sizegroup buttons, aligny 65%"); - - button = new SelectColorButton(actions.getMoveDownAction()); - panel.add(button, "sizegroup buttons, aligny 0%"); - - button = new SelectColorButton(actions.getEditAction()); - button.setIcon(null); - button.setMnemonic(0); - panel.add(button, "sizegroup buttons, gaptop 20%"); - - button = new SelectColorButton(actions.getDuplicateAction()); - button.setIcon(null); - button.setMnemonic(0); - panel.add(button, "sizegroup buttons"); - - button = new SelectColorButton(actions.getDeleteAction()); - button.setIcon(null); - button.setMnemonic(0); - panel.add(button, "sizegroup buttons"); - - horizontal.setLeftComponent(panel); - - - // Upper-right segment, component addition buttons - - panel = new JPanel(new MigLayout("fill, insets 0", "[0::]")); - - scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, - ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); - scroll.setViewportView(new ComponentAddButtons(document, componentSelectionModel, - scroll.getViewport())); - scroll.setBorder(null); - scroll.setViewportBorder(null); - - TitledBorder border = BorderFactory.createTitledBorder(trans.get("BasicFrame.title.Addnewcomp")); - GUIUtil.changeFontStyle(border, Font.BOLD); - scroll.setBorder(border); - - panel.add(scroll, "grow"); - - horizontal.setRightComponent(panel); - - return horizontal; - } - - /** * Return the currently selected rocket component, or null if none selected. */ @@ -988,6 +806,10 @@ public class BasicFrame extends JFrame { this.setJMenuBar(menubar); } + public RocketActions getRocketActions() { + return actions; + } + public void doComponentTreePopup(MouseEvent e) { popupMenu.show(e.getComponent(), e.getX(), e.getY()); } diff --git a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java new file mode 100644 index 000000000..5dec75145 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java @@ -0,0 +1,228 @@ +package net.sf.openrocket.gui.main; + +import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.configdialog.ComponentConfigDialog; +import net.sf.openrocket.gui.main.componenttree.ComponentTree; +import net.sf.openrocket.gui.scalefigure.RocketPanel; +import net.sf.openrocket.gui.util.GUIUtil; +import net.sf.openrocket.gui.widgets.SelectColorButton; +import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.PodSet; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.startup.Application; + +import javax.swing.BorderFactory; +import javax.swing.InputMap; +import javax.swing.JButton; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JSplitPane; +import javax.swing.KeyStroke; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; +import javax.swing.border.TitledBorder; +import javax.swing.event.TreeSelectionEvent; +import javax.swing.event.TreeSelectionListener; +import javax.swing.tree.TreePath; +import java.awt.Font; +import java.awt.event.KeyEvent; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; + +import static net.sf.openrocket.gui.main.BasicFrame.SHORTCUT_KEY; + +/** + * Construct the "Rocket design" tab. This contains a horizontal split pane + * with the left component the design tree and the right component buttons + * for adding components. + */ +public class DesignPanel extends JSplitPane { + private static final Translator trans = Application.getTranslator(); + + public DesignPanel(final BasicFrame parent, final RocketPanel rocketpanel, final OpenRocketDocument document, + final ComponentTree tree) { + super(JSplitPane.HORIZONTAL_SPLIT, true); + setResizeWeight(0.5); + + // Upper-left segment, component tree + JPanel panel = new JPanel(new MigLayout("fill, flowy", "[grow][grow 0]","[grow]")); + + // Remove JTree key events that interfere with menu accelerators + InputMap im = SwingUtilities.getUIInputMap(tree, JComponent.WHEN_FOCUSED); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_X, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_C, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_V, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_A, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_S, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_O, SHORTCUT_KEY), null); + im.put(KeyStroke.getKeyStroke(KeyEvent.VK_N, SHORTCUT_KEY), null); + + // Visually select all child components of a stage/rocket/podset when it is selected + tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + if (tree == null || tree.getSelectionPaths() == null || tree.getSelectionPaths().length == 0 + || rocketpanel == null) return; + + // Get all the components that need to be selected = currently selected components + children of stages/boosters/podsets + List children = new ArrayList<>(Arrays.asList(rocketpanel.getFigure().getSelection())); + for (TreePath p : tree.getSelectionPaths()) { + if (p != null) { + RocketComponent c = (RocketComponent) p.getLastPathComponent(); + if (c instanceof AxialStage || c instanceof Rocket || c instanceof PodSet) { + Iterator iter = c.iterator(false); + while (iter.hasNext()) { + RocketComponent child = iter.next(); + children.add(child); + } + } + } + } + + // Select all the child components + if (rocketpanel.getFigure() != null && rocketpanel.getFigure3d() != null) { + rocketpanel.getFigure().setSelection(children.toArray(new RocketComponent[0])); + rocketpanel.getFigure3d().setSelection(children.toArray(new RocketComponent[0])); + } + } + }); + + // Double-click opens config dialog + MouseListener ml = new MouseAdapter() { + @Override + public void mouseClicked(MouseEvent e) { + int selRow = tree.getRowForLocation(e.getX(), e.getY()); + TreePath selPath = tree.getPathForLocation(e.getX(), e.getY()); + if (selRow != -1) { + if (selPath == null) return; + + // Double-click + if ((e.getButton() == MouseEvent.BUTTON1) && (e.getClickCount() == 2) && !ComponentConfigDialog.isDialogVisible()) { + RocketComponent component = (RocketComponent) selPath.getLastPathComponent(); + + // Multi-component edit if shift/meta key is pressed + if ((e.isShiftDown() || e.isMetaDown()) && tree.getSelectionPaths() != null) { + // Add the other selected components as listeners to the last selected component + for (TreePath p : tree.getSelectionPaths()) { + if (p != null) { + if (p.getLastPathComponent() == component) continue; + RocketComponent c = (RocketComponent) p.getLastPathComponent(); + c.clearConfigListeners(); + component.addConfigListener(c); + } + } + + // Add the selection path to the tree selection + List paths = new LinkedList<>(Arrays.asList(tree.getSelectionPaths())); + paths.add(selPath); + tree.setSelectionPaths(paths.toArray(new TreePath[0])); + } + + ComponentConfigDialog.showDialog(parent, document, component); + } + // Context menu + else if ((e.getButton() == MouseEvent.BUTTON3) && (e.getClickCount() == 1)) { + if (!tree.isPathSelected(selPath)) { + // Select new path + tree.setSelectionPath(selPath); + } + + parent.doComponentTreePopup(e); + } + } else { // Clicked on blank space + tree.clearSelection(); + } + } + }; + tree.addMouseListener(ml); + + // Update dialog when selection is changed + tree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() { + @Override + public void valueChanged(TreeSelectionEvent e) { + // Scroll tree to the selected item + TreePath[] paths = tree.getSelectionModel().getSelectionPaths(); + if (paths == null || paths.length == 0) + return; + + for (TreePath path : paths) { + tree.scrollPathToVisible(path); + } + + if (!ComponentConfigDialog.isDialogVisible()) + return; + else + ComponentConfigDialog.disposeDialog(); + + RocketComponent c = (RocketComponent) paths[0].getLastPathComponent(); + c.clearConfigListeners(); + for (int i = 1; i < paths.length; i++) { + RocketComponent listener = (RocketComponent) paths[i].getLastPathComponent(); + listener.clearConfigListeners(); + c.addConfigListener(listener); + } + ComponentConfigDialog.showDialog(parent, document, c); + } + }); + + // Place tree inside scroll pane + JScrollPane scroll = new JScrollPane(tree); + panel.add(scroll, "spany, grow, wrap"); + + + // Buttons + JButton button = new SelectColorButton(parent.getRocketActions().getMoveUpAction()); + panel.add(button, "sizegroup buttons, aligny 65%"); + + button = new SelectColorButton(parent.getRocketActions().getMoveDownAction()); + panel.add(button, "sizegroup buttons, aligny 0%"); + + button = new SelectColorButton(parent.getRocketActions().getEditAction()); + button.setIcon(null); + button.setMnemonic(0); + panel.add(button, "sizegroup buttons, gaptop 20%"); + + button = new SelectColorButton(parent.getRocketActions().getDuplicateAction()); + button.setIcon(null); + button.setMnemonic(0); + panel.add(button, "sizegroup buttons"); + + button = new SelectColorButton(parent.getRocketActions().getDeleteAction()); + button.setIcon(null); + button.setMnemonic(0); + panel.add(button, "sizegroup buttons"); + + this.setLeftComponent(panel); + + + // Upper-right segment, component addition buttons + + panel = new JPanel(new MigLayout("fill, insets 0", "[0::]")); + + scroll = new JScrollPane(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED, + ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scroll.setViewportView(new ComponentAddButtons(document, tree.getSelectionModel(), + scroll.getViewport())); + scroll.setBorder(null); + scroll.setViewportBorder(null); + + TitledBorder border = BorderFactory.createTitledBorder(trans.get("BasicFrame.title.Addnewcomp")); + GUIUtil.changeFontStyle(border, Font.BOLD); + scroll.setBorder(border); + + panel.add(scroll, "grow"); + + this.setRightComponent(panel); + } + +} From 65b824adcb90ec60464427b05c10284d3d07d2a9 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Thu, 28 Jul 2022 20:03:29 +0200 Subject: [PATCH 04/21] Focus on design tables when tab switches --- .../sf/openrocket/gui/main/BasicFrame.java | 27 ++++++++++--------- .../sf/openrocket/gui/main/DesignPanel.java | 10 +++++++ .../gui/main/FlightConfigurationPanel.java | 20 ++++++++++++++ .../sf/openrocket/gui/main/RocketActions.java | 10 +++---- .../openrocket/gui/main/SimulationPanel.java | 10 +++++++ .../FlightConfigurablePanel.java | 7 +++++ 6 files changed, 66 insertions(+), 18 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 8fe487faf..533d0c45d 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -4,9 +4,7 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; @@ -19,16 +17,12 @@ import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.*; import javax.swing.border.BevelBorder; -import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; @@ -68,13 +62,11 @@ import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.OpenFileWorker; import net.sf.openrocket.gui.util.SaveFileWorker; import net.sf.openrocket.gui.util.SwingPreferences; -import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; -import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; @@ -107,8 +99,8 @@ public class BasicFrame extends JFrame { public static final int SHIFT_SHORTCUT_KEY = Toolkit.getDefaultToolkit().getMenuShortcutKeyMaskEx() | SHIFT_DOWN_MASK; - public static final int COMPONENT_TAB = 0; - public static final int CONFIGURATION_TAB = 1; + public static final int DESIGN_TAB = 0; + public static final int FLIGHT_CONFIGURATION_TAB = 1; public static final int SIMULATION_TAB = 2; @@ -1075,7 +1067,7 @@ public class BasicFrame extends JFrame { /** * Select the tab on the main pane. * - * @param tab one of {@link #COMPONENT_TAB} or {@link #SIMULATION_TAB}. + * @param tab one of {@link #DESIGN_TAB}, {@link #FLIGHT_CONFIGURATION_TAB} or {@link #SIMULATION_TAB}. */ public void selectTab(int tab) { tabbedPane.setSelectedIndex(tab); @@ -1759,8 +1751,17 @@ public class BasicFrame extends JFrame { public void stateChanged(ChangeEvent e) { JTabbedPane tabSource = (JTabbedPane) e.getSource(); int tab = tabSource.getSelectedIndex(); - if (tab == SIMULATION_TAB) { - simulationPanel.activating(); + switch (tab) { + case DESIGN_TAB: + designPanel.takeTheSpotlight(); + break; + case FLIGHT_CONFIGURATION_TAB: + flightConfigurationPanel.takeTheSpotlight(); + break; + case SIMULATION_TAB: + simulationPanel.takeTheSpotlight(); + simulationPanel.activating(); + break; } } diff --git a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java index 5dec75145..fda6449b6 100644 --- a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java @@ -28,6 +28,7 @@ import javax.swing.border.TitledBorder; import javax.swing.event.TreeSelectionEvent; import javax.swing.event.TreeSelectionListener; import javax.swing.tree.TreePath; +import java.awt.Component; import java.awt.Font; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; @@ -48,11 +49,13 @@ import static net.sf.openrocket.gui.main.BasicFrame.SHORTCUT_KEY; */ public class DesignPanel extends JSplitPane { private static final Translator trans = Application.getTranslator(); + private final Component tree; public DesignPanel(final BasicFrame parent, final RocketPanel rocketpanel, final OpenRocketDocument document, final ComponentTree tree) { super(JSplitPane.HORIZONTAL_SPLIT, true); setResizeWeight(0.5); + this.tree = tree; // Upper-left segment, component tree JPanel panel = new JPanel(new MigLayout("fill, flowy", "[grow][grow 0]","[grow]")); @@ -225,4 +228,11 @@ public class DesignPanel extends JSplitPane { this.setRightComponent(panel); } + /** + * Focus on the component tree. + */ + public void takeTheSpotlight() { + tree.requestFocusInWindow(); + } + } diff --git a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index 5c176c52b..605c020fe 100644 --- a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -119,12 +119,15 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe switch (tabs.getSelectedIndex()) { case MOTOR_TAB_INDEX: motorConfigurationPanel.updateButtonState(); + motorConfigurationPanel.takeTheSpotlight(); break; case RECOVERY_TAB_INDEX: recoveryConfigurationPanel.updateButtonState(); + motorConfigurationPanel.takeTheSpotlight(); break; case SEPARATION_TAB_INDEX: separationConfigurationPanel.updateButtonState(); + motorConfigurationPanel.takeTheSpotlight(); break; } } @@ -368,4 +371,21 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe newOrDuplicateConfigAction(true); } } + + /** + * Focus on the table of the config panel that is currently opened. + */ + public void takeTheSpotlight() { + switch (tabs.getSelectedIndex()) { + case MOTOR_TAB_INDEX: + motorConfigurationPanel.takeTheSpotlight(); + break; + case RECOVERY_TAB_INDEX: + recoveryConfigurationPanel.takeTheSpotlight(); + break; + case SEPARATION_TAB_INDEX: + separationConfigurationPanel.takeTheSpotlight(); + break; + } + } } diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java index 86a3ea6bf..ebf6aa1c8 100644 --- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java +++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java @@ -578,7 +578,7 @@ public class RocketActions { parentFrame.selectTab(BasicFrame.SIMULATION_TAB); } else { deleteComponentAction.actionPerformed(e); - parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } } @@ -632,7 +632,7 @@ public class RocketActions { OpenRocketClipboard.setClipboard(copiedComponents); delete(components); - parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } else if (isSimulationSelected()) { Simulation[] simsCopy = new Simulation[sims.length]; @@ -689,7 +689,7 @@ public class RocketActions { List copiedComponents = new LinkedList<>(copyComponentsMaintainParent(components)); OpenRocketClipboard.setClipboard(copiedComponents); - parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } else if (sims != null && sims.length > 0) { Simulation[] simsCopy = new Simulation[sims.length]; for (int i=0; i < sims.length; i++) { @@ -769,7 +769,7 @@ public class RocketActions { selectionModel.setSelectedComponents(successfullyPasted); - parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } else if (sims != null) { @@ -866,7 +866,7 @@ public class RocketActions { selectionModel.setSelectedComponents(duplicateComponents); - parentFrame.selectTab(BasicFrame.COMPONENT_TAB); + parentFrame.selectTab(BasicFrame.DESIGN_TAB); } else if (sims != null && sims.length > 0) { ArrayList copySims = new ArrayList(); diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 8c946d682..e3f458394 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -958,4 +958,14 @@ public class SimulationPanel extends JPanel { } } + + /** + * Focus on the simulation table + */ + public void takeTheSpotlight() { + simulationTable.requestFocusInWindow(); + if (simulationTable.getRowCount() > 0) { + simulationTable.setRowSelectionInterval(0, 0); + } + } } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 6e5f2de84..7fa9ca984 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -331,4 +331,11 @@ public abstract class FlightConfigurablePanel Date: Thu, 28 Jul 2022 21:41:05 +0200 Subject: [PATCH 05/21] Reset table selection when clicked on empty space --- .../openrocket/gui/main/SimulationPanel.java | 40 +++++++++++++------ .../FlightConfigurablePanel.java | 17 ++++++++ 2 files changed, 45 insertions(+), 12 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index e3f458394..22b2a62c6 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -449,6 +449,7 @@ public class SimulationPanel extends JPanel { simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF); simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer()); simulationTableModel.setColumnWidths(simulationTable.getColumnModel()); + simulationTable.setFillsViewportHeight(true); pm = new JPopupMenu(); pm.add(new EditSimulationAction()); @@ -465,19 +466,33 @@ public class SimulationPanel extends JPanel { public void mouseClicked(MouseEvent e) { int selectedRow = simulationTable.getSelectedRow(); - if (e.getButton() == MouseEvent.BUTTON1 && e.getClickCount() == 2) { - int selected = simulationTable.convertRowIndexToModel(selectedRow); + if (e.getButton() == MouseEvent.BUTTON1) { + // Clear the table selection when clicked outside the table rows. + if (e.getClickCount() == 1) { + int row = simulationTable.rowAtPoint(e.getPoint()); + int column = simulationTable.columnAtPoint(e.getPoint()); - int column = simulationTable.columnAtPoint(e.getPoint()); - if (column == 0) { - SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected)); - } else { - simulationTable.clearSelection(); - simulationTable.addRowSelectionInterval(selectedRow, selectedRow); - - openDialog(document.getSimulations().get(selected)); + if (row == -1 || column == -1) { + simulationTable.clearSelection(); + } } - } else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { + // Edit the simulation or plot/export + else if (e.getClickCount() == 2) { + int selected = simulationTable.convertRowIndexToModel(selectedRow); + + int column = simulationTable.columnAtPoint(e.getPoint()); + if (column == 0) { + SimulationWarningDialog.showWarningDialog(SimulationPanel.this, document.getSimulations().get(selected)); + } else { + simulationTable.clearSelection(); + simulationTable.addRowSelectionInterval(selectedRow, selectedRow); + + openDialog(document.getSimulations().get(selected)); + } + } + } + // Show context menu + else if (e.getButton() == MouseEvent.BUTTON3 && e.getClickCount() == 1) { // Get the row that the right-click action happened on int r = simulationTable.rowAtPoint(e.getPoint()); @@ -762,7 +777,8 @@ public class SimulationPanel extends JPanel { private void openDialog(final Simulation sim) { boolean plotMode = false; - if (sim.hasSimulationData() && (sim.getStatus() == Status.UPTODATE || sim.getStatus() == Status.EXTERNAL)) { + if (sim.hasSimulationData() && (sim.getStatus() == Status.UPTODATE || sim.getStatus() == Status.LOADED + || sim.getStatus() == Status.EXTERNAL)) { plotMode = true; } openDialog(plotMode, sim); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 7fa9ca984..d6677e0e0 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -3,6 +3,8 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.Color; import java.awt.Component; import java.awt.Font; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; import java.util.Arrays; import java.util.List; @@ -87,6 +89,7 @@ public abstract class FlightConfigurablePanel 1) ? table.getColumnCount() - 1 : 0; for (int row = 0; row < table.getRowCount(); row++) { @@ -163,6 +166,20 @@ public abstract class FlightConfigurablePanel Date: Thu, 28 Jul 2022 23:36:47 +0200 Subject: [PATCH 06/21] Fix merge conflict --- swing/src/net/sf/openrocket/gui/main/BasicFrame.java | 9 --------- 1 file changed, 9 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 4c69274fa..533d0c45d 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -4,9 +4,7 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; -import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.io.File; @@ -19,16 +17,12 @@ import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; import javax.swing.*; import javax.swing.border.BevelBorder; -import javax.swing.border.TitledBorder; import javax.swing.event.ChangeEvent; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; import javax.swing.tree.DefaultTreeSelectionModel; import javax.swing.tree.TreePath; import javax.swing.tree.TreeSelectionModel; @@ -60,7 +54,6 @@ import net.sf.openrocket.gui.dialogs.preferences.PreferencesDialog; import net.sf.openrocket.gui.figure3d.photo.PhotoFrame; import net.sf.openrocket.gui.help.tours.GuidedTourSelectionDialog; import net.sf.openrocket.gui.main.componenttree.ComponentTree; -import net.sf.openrocket.gui.main.flightconfigpanel.FlightConfigurationPanel; import net.sf.openrocket.gui.scalefigure.RocketPanel; import net.sf.openrocket.gui.util.DummyFrameMenuOSX; import net.sf.openrocket.gui.util.FileHelper; @@ -69,13 +62,11 @@ import net.sf.openrocket.gui.util.Icons; import net.sf.openrocket.gui.util.OpenFileWorker; import net.sf.openrocket.gui.util.SaveFileWorker; import net.sf.openrocket.gui.util.SwingPreferences; -import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.ComponentChangeListener; -import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.Rocket; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.startup.Application; From d1914fe7f2e59055c69783e613c00e04586c4bcf Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 29 Jul 2022 00:20:38 +0200 Subject: [PATCH 07/21] Take the spotlight for remove config --- .../src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index 605c020fe..60f0372d7 100644 --- a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -235,6 +235,7 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe } configurationChanged(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); + takeTheSpotlight(); } public void doPopupConfig(MouseEvent e) { From dc06792ea3b51f267c78745b4e689c73abce8661 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 29 Jul 2022 00:27:07 +0200 Subject: [PATCH 08/21] Take the spotlight for delete sim --- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index e4260e479..3b128fb69 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -346,6 +346,7 @@ public class SimulationPanel extends JPanel { document.removeSimulation(selection[i]); } simulationTableModel.fireTableDataChanged(); + takeTheSpotlight(); } private void runSimulation() { From 485f1ad0104cae70f7db398934964174e8a40bde Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 00:32:49 +0200 Subject: [PATCH 09/21] Fix sim table focus after actions --- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 3b128fb69..6a72f8982 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -513,6 +513,7 @@ public class SimulationPanel extends JPanel { break; simulationTable.addRowSelectionInterval(row, row); } + takeTheSpotlight(); } private abstract static class SimulationAction extends AbstractAction { @@ -997,7 +998,7 @@ public class SimulationPanel extends JPanel { */ public void takeTheSpotlight() { simulationTable.requestFocusInWindow(); - if (simulationTable.getRowCount() > 0) { + if (simulationTable.getSelectedRowCount() == 0 && simulationTable.getRowCount() > 0) { simulationTable.setRowSelectionInterval(0, 0); } } From 687fb6f6e39db3755667374839b2200cd5ca15bc Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 00:51:51 +0200 Subject: [PATCH 10/21] Fix sim table keep selection after tab switch --- .../net/sf/openrocket/gui/main/SimulationPanel.java | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 6a72f8982..e219d6146 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -998,8 +998,14 @@ public class SimulationPanel extends JPanel { */ public void takeTheSpotlight() { simulationTable.requestFocusInWindow(); - if (simulationTable.getSelectedRowCount() == 0 && simulationTable.getRowCount() > 0) { - simulationTable.setRowSelectionInterval(0, 0); + int selection = simulationTable.getSelectionModel().getAnchorSelectionIndex(); + if (selection == -1) { + if (simulationTable.getRowCount() > 0) { + selection = 0; + } else { + return; + } } + simulationTable.setRowSelectionInterval(selection, selection); } } From a210661ccde1f108b5b17d1583b3cd8d788dfeb6 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 02:46:38 +0200 Subject: [PATCH 11/21] Synchronize table selection across motors, recovery & stage --- .../gui/main/FlightConfigurationPanel.java | 29 +++++++++++++++---- .../FlightConfigurablePanel.java | 25 +++++++++++++++- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index 60f0372d7..393a2f224 100644 --- a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -20,6 +20,7 @@ import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.document.Simulation; import net.sf.openrocket.gui.dialogs.flightconfiguration.RenameConfigDialog; +import net.sf.openrocket.gui.main.flightconfigpanel.FlightConfigurablePanel; import net.sf.openrocket.gui.main.flightconfigpanel.MotorConfigurationPanel; import net.sf.openrocket.gui.main.flightconfigpanel.RecoveryConfigurationPanel; import net.sf.openrocket.gui.main.flightconfigpanel.SeparationConfigurationPanel; @@ -113,23 +114,29 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe this.add(duplicateConfButton, "wrap"); tabs.addChangeListener(new ChangeListener() { + private FlightConfigurablePanel previousPanel = null; @Override public void stateChanged(ChangeEvent e) { // Trigger a selection of the motor/recovery/configuration item + FlightConfigurablePanel panel = null; switch (tabs.getSelectedIndex()) { case MOTOR_TAB_INDEX: - motorConfigurationPanel.updateButtonState(); - motorConfigurationPanel.takeTheSpotlight(); + panel = motorConfigurationPanel; break; case RECOVERY_TAB_INDEX: - recoveryConfigurationPanel.updateButtonState(); - motorConfigurationPanel.takeTheSpotlight(); + panel = recoveryConfigurationPanel; break; case SEPARATION_TAB_INDEX: - separationConfigurationPanel.updateButtonState(); - motorConfigurationPanel.takeTheSpotlight(); + panel = separationConfigurationPanel; break; } + + // Update the panel selection, focus, and button state + if (panel == null) return; + synchronizePanelSelection(previousPanel, panel); + panel.updateButtonState(); + panel.takeTheSpotlight(); + previousPanel = panel; } }); @@ -300,6 +307,16 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe } + private void synchronizePanelSelection(FlightConfigurablePanel source, FlightConfigurablePanel target) { + if (source == null || target == null) return; + List fids = source.getSelectedConfigurationIds(); + if (fids == null || fids.isEmpty()) { + target.clearSelection(); + } else { + target.setSelectedConfigurationIds(fids); + } + } + private List getSelectedConfigurationIds() { switch (tabs.getSelectedIndex()) { case MOTOR_TAB_INDEX: diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index d6677e0e0..160490d1d 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -175,7 +175,7 @@ public abstract class FlightConfigurablePanel fids) { + if (fids == null || fids.isEmpty() || table.getColumnCount() == 0) return; + + for (FlightConfigurationId id : fids) { + if (id == FlightConfigurationId.DEFAULT_VALUE_FCID) continue; + for (int rowNum = 0; rowNum < table.getRowCount(); rowNum++) { + FlightConfigurationId rowFCID = rocket.getId(rowNum ); + if (rowFCID.equals(id) && !table.isRowSelected(rowNum)) { + table.changeSelection(rowNum, 1, false, false); + } + } + } + } + protected abstract class FlightConfigurableCellRenderer extends DefaultTableCellRenderer { @Override From 68d22366549d8b67357979d5a7986477ac3913cf Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 16:38:17 +0200 Subject: [PATCH 12/21] Fix sim table highlight after tab switch --- .../sf/openrocket/gui/main/BasicFrame.java | 5 +++++ .../openrocket/gui/main/SimulationPanel.java | 22 +++++++++++-------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 533d0c45d..5ea14b2ed 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -102,6 +102,7 @@ public class BasicFrame extends JFrame { public static final int DESIGN_TAB = 0; public static final int FLIGHT_CONFIGURATION_TAB = 1; public static final int SIMULATION_TAB = 2; + private int previousTab = DESIGN_TAB; /** @@ -1751,6 +1752,10 @@ public class BasicFrame extends JFrame { public void stateChanged(ChangeEvent e) { JTabbedPane tabSource = (JTabbedPane) e.getSource(); int tab = tabSource.getSelectedIndex(); + if (previousTab == SIMULATION_TAB) { + simulationPanel.updatePreviousSelection(); + } + previousTab = tab; switch (tab) { case DESIGN_TAB: designPanel.takeTheSpotlight(); diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index e219d6146..3dc3b7e1a 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -10,7 +10,6 @@ import java.awt.datatransfer.StringSelection; import java.awt.datatransfer.Transferable; import java.awt.datatransfer.UnsupportedFlavorException; import java.awt.event.ActionEvent; -import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -104,6 +103,8 @@ public class SimulationPanel extends JPanel { private final SimulationAction duplicateSimulationAction; private final SimulationAction deleteSimulationAction; + private int[] previousSelection = null; + public SimulationPanel(OpenRocketDocument doc) { super(new MigLayout("fill", "[grow][][][][][][grow]")); @@ -266,6 +267,10 @@ public class SimulationPanel extends JPanel { updateButtonStates(); } + public void updatePreviousSelection() { + this.previousSelection = simulationTable.getSelectedRows(); + } + private void newSimulation() { Simulation sim = new Simulation(document.getRocket()); sim.setName(document.getNextSimulationName()); @@ -994,18 +999,17 @@ public class SimulationPanel extends JPanel { } /** - * Focus on the simulation table + * Focus on the simulation table and maintain the previous row selection(s). */ public void takeTheSpotlight() { simulationTable.requestFocusInWindow(); - int selection = simulationTable.getSelectionModel().getAnchorSelectionIndex(); - if (selection == -1) { - if (simulationTable.getRowCount() > 0) { - selection = 0; - } else { - return; + if (previousSelection == null || previousSelection.length == 0) { + simulationTable.setRowSelectionInterval(0, 0); + } else { + simulationTable.clearSelection(); + for (int row : previousSelection) { + simulationTable.addRowSelectionInterval(row, row); } } - simulationTable.setRowSelectionInterval(selection, selection); } } From 9a0bec1370973a0ae6b575ccc397f9615d790f65 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 18:27:18 +0200 Subject: [PATCH 13/21] Clean up code --- .../src/net/sf/openrocket/gui/main/BasicFrame.java | 2 +- .../src/net/sf/openrocket/gui/main/DesignPanel.java | 13 ++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index 5ea14b2ed..e67146cad 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -185,7 +185,7 @@ public class BasicFrame extends JFrame { // Create RocketActions actions = new RocketActions(document, selectionModel, this, simulationPanel); } - designPanel = new DesignPanel(this, rocketpanel, document, tree); + designPanel = new DesignPanel(this, document, tree); flightConfigurationPanel = new FlightConfigurationPanel(this, document); tabbedPane = new JTabbedPane(); tabbedPane.addTab(trans.get("BasicFrame.tab.Rocketdesign"), null, designPanel); diff --git a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java index fda6449b6..72065c9ed 100644 --- a/swing/src/net/sf/openrocket/gui/main/DesignPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/DesignPanel.java @@ -51,8 +51,7 @@ public class DesignPanel extends JSplitPane { private static final Translator trans = Application.getTranslator(); private final Component tree; - public DesignPanel(final BasicFrame parent, final RocketPanel rocketpanel, final OpenRocketDocument document, - final ComponentTree tree) { + public DesignPanel(final BasicFrame parent, final OpenRocketDocument document, final ComponentTree tree) { super(JSplitPane.HORIZONTAL_SPLIT, true); setResizeWeight(0.5); this.tree = tree; @@ -75,10 +74,10 @@ public class DesignPanel extends JSplitPane { @Override public void valueChanged(TreeSelectionEvent e) { if (tree == null || tree.getSelectionPaths() == null || tree.getSelectionPaths().length == 0 - || rocketpanel == null) return; + || parent.getRocketPanel() == null) return; // Get all the components that need to be selected = currently selected components + children of stages/boosters/podsets - List children = new ArrayList<>(Arrays.asList(rocketpanel.getFigure().getSelection())); + List children = new ArrayList<>(Arrays.asList(parent.getRocketPanel().getFigure().getSelection())); for (TreePath p : tree.getSelectionPaths()) { if (p != null) { RocketComponent c = (RocketComponent) p.getLastPathComponent(); @@ -93,9 +92,9 @@ public class DesignPanel extends JSplitPane { } // Select all the child components - if (rocketpanel.getFigure() != null && rocketpanel.getFigure3d() != null) { - rocketpanel.getFigure().setSelection(children.toArray(new RocketComponent[0])); - rocketpanel.getFigure3d().setSelection(children.toArray(new RocketComponent[0])); + if (parent.getRocketPanel().getFigure() != null && parent.getRocketPanel().getFigure3d() != null) { + parent.getRocketPanel().getFigure().setSelection(children.toArray(new RocketComponent[0])); + parent.getRocketPanel().getFigure3d().setSelection(children.toArray(new RocketComponent[0])); } } }); From 4ea5a6ade500e992370df115f46841e7b2a8a8d4 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 21:49:42 +0200 Subject: [PATCH 14/21] Fix stage highlight not working --- .../sf/openrocket/gui/main/BasicFrame.java | 26 ++++++++++--------- 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index e67146cad..e982d3563 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -160,17 +160,9 @@ public class BasicFrame extends JFrame { componentSelectionModel = new DefaultTreeSelectionModel(); componentSelectionModel.setSelectionMode(TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION); - // Create the component tree - tree = new ComponentTree(document); - tree.setSelectionModel(componentSelectionModel); - // ----- Create the different BasicFrame panels ----- log.debug("Constructing the BasicFrame UI"); - //// Bottom segment, rocket figure - rocketpanel = new RocketPanel(document, this); - rocketpanel.setSelectionModel(tree.getSelectionModel()); - //// Top segment, tabbed pane simulationPanel = new SimulationPanel(document); { @@ -185,6 +177,12 @@ public class BasicFrame extends JFrame { // Create RocketActions actions = new RocketActions(document, selectionModel, this, simulationPanel); } + { + // Create the component tree + tree = new ComponentTree(document); + tree.setSelectionModel(componentSelectionModel); + } + designPanel = new DesignPanel(this, document, tree); flightConfigurationPanel = new FlightConfigurationPanel(this, document); tabbedPane = new JTabbedPane(); @@ -192,6 +190,14 @@ public class BasicFrame extends JFrame { tabbedPane.addTab(trans.get("BasicFrame.tab.Flightconfig"), null, flightConfigurationPanel); tabbedPane.addTab(trans.get("BasicFrame.tab.Flightsim"), null, simulationPanel); + // Add change listener to catch when the tabs are changed. This is to run simulations + // automatically when the simulation tab is selected. + tabbedPane.addChangeListener(new BasicFrame_changeAdapter(this)); + + //// Bottom segment, rocket figure + rocketpanel = new RocketPanel(document, this); + rocketpanel.setSelectionModel(tree.getSelectionModel()); + //// The main vertical split pane JSplitPane vertical = new JSplitPane(JSplitPane.VERTICAL_SPLIT, true); vertical.setResizeWeight(0.5); @@ -212,10 +218,6 @@ public class BasicFrame extends JFrame { popupMenu.add(actions.getScaleAction()); } - // Add change listener to catch when the tabs are changed. This is to run simulations - // automatically when the simulation tab is selected. - tabbedPane.addChangeListener(new BasicFrame_changeAdapter(this)); - createMenu(); From 71e9315ec956eb57252ad9532369944b466dc2a2 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sat, 30 Jul 2022 22:29:41 +0200 Subject: [PATCH 15/21] Clean up --- .../FlightConfigurablePanel.java | 26 +++++++++++++++++++ .../MotorConfigurationPanel.java | 26 ------------------- .../RecoveryConfigurationPanel.java | 26 ------------------- .../SeparationConfigurationPanel.java | 26 ------------------- 4 files changed, 26 insertions(+), 78 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 160490d1d..9075a208f 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -3,6 +3,8 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.Color; import java.awt.Component; import java.awt.Font; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; @@ -180,6 +182,25 @@ public abstract class FlightConfigurablePanel return configurationTable; } - @Override - protected void installTableListener() { - super.installTableListener(); - - table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { - @Override - public void valueChanged(ListSelectionEvent e) { - updateComponentSelection(e); - } - }); - - table.addFocusListener(new FocusListener() { - @Override - public void focusGained(FocusEvent e) { - updateComponentSelection(new ListSelectionEvent(this, 0, 0, false)); - } - - @Override - public void focusLost(FocusEvent e) { - - } - }); - } - private void doPopupFull(MouseEvent e) { popupMenuFull.show(e.getComponent(), e.getX(), e.getY()); } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index 57f83a7e5..530d98f23 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -1,8 +1,6 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -166,30 +164,6 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel devices = getSelectedComponents(); List fcIds = getSelectedConfigurationIds(); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java index b4cd6fd84..8b8c31ab9 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java @@ -1,8 +1,6 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.event.ActionEvent; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; @@ -175,30 +173,6 @@ public class SeparationConfigurationPanel extends FlightConfigurablePanel stages = getSelectedComponents(); List fcIds = getSelectedConfigurationIds(); From 6ff6265c3adbd8f08df4d659d42d1c8a120bc44e Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sun, 31 Jul 2022 10:33:23 +0200 Subject: [PATCH 16/21] Fix motors, recovery & stage not properly highlighting --- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 3dc3b7e1a..c8c08b870 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -302,6 +302,7 @@ public class SimulationPanel extends JPanel { } fireMaintainSelection(); + takeTheSpotlight(); openDialog(true, sim); } @@ -363,6 +364,7 @@ public class SimulationPanel extends JPanel { SimulationPanel.this), document, sims).setVisible(true); log.info("Running simulations took " + (System.currentTimeMillis() - t) + " ms"); fireMaintainSelection(); + takeTheSpotlight(); } public void editSimulation() { @@ -499,6 +501,7 @@ public class SimulationPanel extends JPanel { } d.setVisible(true); fireMaintainSelection(); + takeTheSpotlight(); } private void openDialog(final Simulation sim) { @@ -518,7 +521,6 @@ public class SimulationPanel extends JPanel { break; simulationTable.addRowSelectionInterval(row, row); } - takeTheSpotlight(); } private abstract static class SimulationAction extends AbstractAction { @@ -1003,6 +1005,9 @@ public class SimulationPanel extends JPanel { */ public void takeTheSpotlight() { simulationTable.requestFocusInWindow(); + if (simulationTable.getSelectedRows().length > 0) { + return; + } if (previousSelection == null || previousSelection.length == 0) { simulationTable.setRowSelectionInterval(0, 0); } else { From e13c4baf25f79af76a824acad290d0a41e84c5c2 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Sun, 31 Jul 2022 11:05:52 +0200 Subject: [PATCH 17/21] Fix focus lost motors, recovery & stage --- .../gui/main/FlightConfigurationPanel.java | 4 ++++ .../FlightConfigurablePanel.java | 23 ++++++------------- .../MotorConfigurationPanel.java | 2 +- .../RecoveryConfigurationPanel.java | 2 +- .../SeparationConfigurationPanel.java | 2 +- 5 files changed, 14 insertions(+), 19 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index 393a2f224..a2526dc40 100644 --- a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -136,6 +136,7 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe synchronizePanelSelection(previousPanel, panel); panel.updateButtonState(); panel.takeTheSpotlight(); + panel.updateRocketViewSelection(); previousPanel = panel; } }); @@ -397,12 +398,15 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe switch (tabs.getSelectedIndex()) { case MOTOR_TAB_INDEX: motorConfigurationPanel.takeTheSpotlight(); + motorConfigurationPanel.updateRocketViewSelection(); break; case RECOVERY_TAB_INDEX: recoveryConfigurationPanel.takeTheSpotlight(); + recoveryConfigurationPanel.updateRocketViewSelection(); break; case SEPARATION_TAB_INDEX: separationConfigurationPanel.takeTheSpotlight(); + separationConfigurationPanel.updateRocketViewSelection(); break; } } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 9075a208f..5d3e23a62 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -3,8 +3,6 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.Color; import java.awt.Component; import java.awt.Font; -import java.awt.event.FocusEvent; -import java.awt.event.FocusListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; @@ -186,19 +184,7 @@ public abstract class FlightConfigurablePanel popupMenuFull.show(e.getComponent(), e.getX(), e.getY()); } - public void updateComponentSelection(ListSelectionEvent e) { + public void updateRocketViewSelection(ListSelectionEvent e) { if (e.getValueIsAdjusting()) { return; } diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index 530d98f23..0ecbea16d 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -245,7 +245,7 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel Date: Mon, 1 Aug 2022 01:16:52 +0200 Subject: [PATCH 18/21] Add multi-config select sync between flight config tables --- .../sf/openrocket/gui/main/FlightConfigurationPanel.java | 5 ++++- .../main/flightconfigpanel/FlightConfigurablePanel.java | 9 +++++++-- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java index a2526dc40..b4fc279a0 100644 --- a/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/FlightConfigurationPanel.java @@ -114,7 +114,7 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe this.add(duplicateConfButton, "wrap"); tabs.addChangeListener(new ChangeListener() { - private FlightConfigurablePanel previousPanel = null; + private FlightConfigurablePanel previousPanel = motorConfigurationPanel; @Override public void stateChanged(ChangeEvent e) { // Trigger a selection of the motor/recovery/configuration item @@ -308,6 +308,9 @@ public class FlightConfigurationPanel extends JPanel implements StateChangeListe } + /** + * Synchronize the table row selection of a target panel with the selection in the source panel. + */ private void synchronizePanelSelection(FlightConfigurablePanel source, FlightConfigurablePanel target) { if (source == null || target == null) return; List fids = source.getSelectedConfigurationIds(); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index 5d3e23a62..e6a404680 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -6,6 +6,7 @@ import java.awt.Font; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.util.Arrays; +import java.util.HashSet; import java.util.List; import javax.swing.JComponent; @@ -298,12 +299,16 @@ public abstract class FlightConfigurablePanel fids) { if (fids == null || fids.isEmpty() || table.getColumnCount() == 0) return; + if (new HashSet<>(getSelectedConfigurationIds()).containsAll(fids)) return; + + table.clearSelection(); for (FlightConfigurationId id : fids) { if (id == FlightConfigurationId.DEFAULT_VALUE_FCID) continue; for (int rowNum = 0; rowNum < table.getRowCount(); rowNum++) { FlightConfigurationId rowFCID = rocket.getId(rowNum ); - if (rowFCID.equals(id) && !table.isRowSelected(rowNum)) { - table.changeSelection(rowNum, 1, false, false); + if (rowFCID.equals(id)) { + table.changeSelection(rowNum, 1, true, false); + break; } } } From bc8a8eb0d0f95df943793959a1262e76563da64e Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 1 Aug 2022 02:24:23 +0200 Subject: [PATCH 19/21] Don't update flight config on multi-selection in table --- .../main/flightconfigpanel/FlightConfigurablePanel.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index e6a404680..d1d1eba4d 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -149,7 +149,12 @@ public abstract class FlightConfigurablePanel 1) { + return; + } + /* Find the selected row and set it as the current selected configuration * for the rocket. This will propagate the event to ensure that other * pieces of the UI are updated and match the table selection. From 3d908a056f9c6ca3bab61bc7cb8595b26860b0fc Mon Sep 17 00:00:00 2001 From: SiboVG Date: Mon, 1 Aug 2022 23:10:28 +0200 Subject: [PATCH 20/21] Fix NullPointerException --- .../gui/main/flightconfigpanel/FlightConfigurablePanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java index d1d1eba4d..69dc1d532 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/FlightConfigurablePanel.java @@ -304,7 +304,7 @@ public abstract class FlightConfigurablePanel fids) { if (fids == null || fids.isEmpty() || table.getColumnCount() == 0) return; - if (new HashSet<>(getSelectedConfigurationIds()).containsAll(fids)) return; + if (getSelectedConfigurationIds() != null && new HashSet<>(getSelectedConfigurationIds()).containsAll(fids)) return; table.clearSelection(); for (FlightConfigurationId id : fids) { From b7677073e7124b615e679dd712884cc8a39d7c78 Mon Sep 17 00:00:00 2001 From: SiboVG Date: Fri, 12 Aug 2022 21:14:44 +0200 Subject: [PATCH 21/21] Fix merge conflict --- swing/src/net/sf/openrocket/gui/main/SimulationPanel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java index 6d9ed2f48..d287d26fa 100644 --- a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java @@ -1029,6 +1029,7 @@ public class SimulationPanel extends JPanel { simulationTable.addRowSelectionInterval(row, row); } } + } private static class NextRowAction extends AbstractAction { private final JTable table;