Merge remote-tracking branch 'origin/unstable' into unstable
This commit is contained in:
commit
48d9532e2f
BIN
.github/getting-started.png
vendored
Normal file
BIN
.github/getting-started.png
vendored
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 MiB |
102
README.md
102
README.md
@ -1,65 +1,76 @@
|
|||||||
OpenRocket
|
# OpenRocket 🚀
|
||||||
==========
|
|
||||||
|
OpenRocket is a free, fully featured model rocket simulator that allows you to design and simulate your rockets before actually building and flying them.
|
||||||
|
|
||||||

|

|
||||||
[](https://www.gnu.org/licenses/gpl-3.0)
|
[](https://www.gnu.org/licenses/gpl-3.0)
|
||||||
|
|
||||||

|

|
||||||
[](https://GitHub.com/openrocket/openrocket/releases/)
|
[](https://GitHub.com/openrocket/openrocket/releases/)
|
||||||
[](https://discord.gg/qD2G5v2FAw)
|
[](https://discord.gg/qD2G5v2FAw)
|
||||||
|
|
||||||
--------
|
--------
|
||||||
|
|
||||||
OpenRocket is a free, fully featured model rocket simulator that allows you to design and simulate your rockets before actually building and flying them.
|
## 🛠️ Design, Visualize, and Analyze
|
||||||
|
|
||||||

|
1. **Design** your rockets using a rich selection of built-in components:
|
||||||

|

|
||||||

|
|
||||||
|
|
||||||
The main features include:
|
2. **Visualize** your masterpiece in 3D:
|
||||||
|

|
||||||
|
|
||||||
* Six-degree-of-freedom flight simulation
|
3. **Plot & Analyze** your simulation results for precision and improvements:
|
||||||
* Automatic design optimization
|

|
||||||
* Realtime simulated altitude, velocity and acceleration display
|
|
||||||
* Staging and clustering support
|
|
||||||
* Cross-platform (Java-based)
|
|
||||||
|
|
||||||
Read more about it on the [OpenRocket Wiki](http://wiki.openrocket.info).
|
## 🌟 Features
|
||||||
|
|
||||||
Installers
|
- **Six-degree-of-freedom flight simulation**
|
||||||
----------
|
- **Automatic design optimization**
|
||||||
OpenRocket maintains an installer for installing the software and Java runtime. You can find the installers on [our
|
- **Realtime simulated altitude, velocity, and acceleration display**
|
||||||
website](https://openrocket.info/downloads.html).
|
- **Staging and clustering support**
|
||||||
|
- **Cross-platform (Java-based)**
|
||||||
|
|
||||||
|
... plus many more
|
||||||
|
|
||||||
|
📖 Read more on the [OpenRocket Wiki](http://wiki.openrocket.info).
|
||||||
|
|
||||||
|
## 💾 Installers
|
||||||
|
|
||||||
|
Find the OpenRocket installers [here](https://openrocket.info/downloads.html)
|
||||||
|
|
||||||
|
## 📝 Release Notes
|
||||||
|
|
||||||
Release Notes
|
|
||||||
-------------
|
|
||||||
Release notes are available on each [release's page](https://github.com/openrocket/openrocket/releases) or on [our website](https://openrocket.info/release_notes.html).
|
Release notes are available on each [release's page](https://github.com/openrocket/openrocket/releases) or on [our website](https://openrocket.info/release_notes.html).
|
||||||
|
|
||||||
License
|
## 🚀 Getting started
|
||||||
-------
|
|
||||||
|
|
||||||
OpenRocket is an Open Source project licensed under the [GNU GPL](https://www.gnu.org/licenses/gpl-3.0.en.html). This means that the software is free to use for whatever purposes, and the source code is also available for studying and extending.
|
The easiest way to get started is to open one of our in-program example designs:
|
||||||
|
|
||||||
Contributing
|

|
||||||
------------
|
|
||||||
OpenRocket needs help to become even better. Implementing features, writing documentation and creating example designs are just a few ways of helping. If you are interested in helping make OpenRocket the best rocket simulator out there, please [click here for information on how to get involved](http://openrocket.sourceforge.net/getinvolved.html) and [read the practicalities of contributing here](CONTRIBUTING.md).
|
|
||||||
|
|
||||||
**Contributors**
|
Dive into the essentials: adjust component dimensions, plot a simulation, swap out motors, ... Explore the impact of your changes and, most importantly, enjoy the process! 😊
|
||||||
- Sampo Niskanen, main developer
|
|
||||||
- Doug Pedrick, support for RockSim designs, printing
|
|
||||||
- Kevin Ruland, Android version
|
|
||||||
- Bill Kuker, 3D visualization
|
|
||||||
- Richard Graham, geodetic computations
|
|
||||||
- Jason Blood, freeform fin set import
|
|
||||||
- Boris du Reau, internationalization
|
|
||||||
- Daniel Williams, pod support, maintainer
|
|
||||||
- Joe Pfeiffer (maintainer)
|
|
||||||
- Billy Olsen (maintainer)
|
|
||||||
- Sibo Van Gool (RASAero file format, 3D OBJ export, dark theme, maintainer)
|
|
||||||
- Neil Weinstock (tester, icons, forum support)
|
|
||||||
- H. Craig Miller (tester)
|
|
||||||
|
|
||||||
|
## 💪 Contribute
|
||||||
|
|
||||||
**Translators**
|
Help us soar higher! Whether it's implementing features, writing documentation, or creating design examples, every contribution matters. Interested? Check out [how to get involved](http://openrocket.sourceforge.net/getinvolved.html) and the [practicalities of contributing](CONTRIBUTING.md).
|
||||||
|
|
||||||
|
### ✨ Contributors
|
||||||
|
- [Sampo Niskanen](https://github.com/plaa) - Original developer
|
||||||
|
- [Doug Pedrick](https://github.com/rodinia814) - RockSim designs, printing
|
||||||
|
- [Kevin Ruland](https://github.com/kruland2607) - Android version
|
||||||
|
- [Bill Kuker](https://github.com/bkuker) - 3D visualization
|
||||||
|
- [Richard Graham](https://github.com/rdgraham) - Geodetic computations
|
||||||
|
- Jason Blood - Freeform fin set import
|
||||||
|
- [Boris du Reau](https://github.com/bdureau) - Internationalization
|
||||||
|
- [Daniel Williams](https://github.com/teyrana) - Pod support, maintainer
|
||||||
|
- [Joe Pfeiffer](https://github.com/JoePfeiffer) - Maintainer
|
||||||
|
- [Billy Olsen](https://github.com/wolsen) - Maintainer
|
||||||
|
- [Sibo Van Gool](https://github.com/SiboVG) - RASAero file format, 3D OBJ export, dark theme, maintainer
|
||||||
|
- [Neil Weinstock](https://github.com/neilweinstock) - Tester, icons, forum support
|
||||||
|
- [H. Craig Miller](https://github.com/hcraigmiller) - Tester
|
||||||
|
|
||||||
|
You can view the full list of contributors [here](https://github.com/openrocket/openrocket/graphs/contributors).
|
||||||
|
|
||||||
|
### 🌍Translators
|
||||||
- Tripoli France
|
- Tripoli France
|
||||||
- Tripoli Spain
|
- Tripoli Spain
|
||||||
- Stefan Lobas / ERIG
|
- Stefan Lobas / ERIG
|
||||||
@ -69,3 +80,12 @@ OpenRocket needs help to become even better. Implementing features, writing docu
|
|||||||
- Polish Rocketry Society / Łukasz & Alex Kazanski
|
- Polish Rocketry Society / Łukasz & Alex Kazanski
|
||||||
- Sibo Van Gool
|
- Sibo Van Gool
|
||||||
- Mohamed Amin Elkebsi
|
- Mohamed Amin Elkebsi
|
||||||
|
|
||||||
|
## 📜 License
|
||||||
|
|
||||||
|
OpenRocket is proudly open-source under the [GNU GPL](https://www.gnu.org/licenses/gpl-3.0.en.html) license. Feel free to use, study, and extend.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
⭐ Please give us a star if you find OpenRocket useful, and spread the word! ⭐
|
||||||
|
[](https://star-history.com/#openrocket/openrocket&Date)
|
||||||
|
@ -75,7 +75,7 @@ This is the first beta release of version 23.09.
|
|||||||
* Show calculated values in override tab (fixes #1629)
|
* Show calculated values in override tab (fixes #1629)
|
||||||
* Decrease minimum FoV to 10 degrees in Photo Studio
|
* Decrease minimum FoV to 10 degrees in Photo Studio
|
||||||
* Increase resolution of launch temperature and pressure to 2 decimal places (fixes #2003)
|
* Increase resolution of launch temperature and pressure to 2 decimal places (fixes #2003)
|
||||||
* Display Cd oerride with 3 decimal places
|
* Display Cd override with 3 decimal places
|
||||||
* Add wiki button to help menu (fixes #2046)
|
* Add wiki button to help menu (fixes #2046)
|
||||||
* Eliminate option to save "some" sim data (fixes #2024)
|
* Eliminate option to save "some" sim data (fixes #2024)
|
||||||
* Add OK/Cancel buttons when editing simulations (fixes #2158)
|
* Add OK/Cancel buttons when editing simulations (fixes #2158)
|
||||||
|
@ -303,14 +303,21 @@ public class RocketComponentConfig extends JPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Yes/No dialog: Are you sure you want to discard your changes?
|
// Yes/No dialog: Are you sure you want to discard your changes?
|
||||||
JPanel msg = createCancelOperationContent();
|
SwingUtilities.invokeLater(() -> {
|
||||||
int resultYesNo = JOptionPane.showConfirmDialog(RocketComponentConfig.this, msg,
|
JPanel msg = createCancelOperationContent();
|
||||||
trans.get("RocketCompCfg.CancelOperation.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
int resultYesNo = JOptionPane.showConfirmDialog(RocketComponentConfig.this, msg,
|
||||||
if (resultYesNo == JOptionPane.YES_OPTION) {
|
trans.get("RocketCompCfg.CancelOperation.title"), JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE);
|
||||||
ComponentConfigDialog.clearConfigListeners = false; // Undo action => config listeners of new component will be cleared
|
|
||||||
disposeDialog();
|
if (resultYesNo == JOptionPane.YES_OPTION) {
|
||||||
document.undo();
|
ComponentConfigDialog.clearConfigListeners = false;
|
||||||
}
|
|
||||||
|
// Need to execute after delay, otherwise the dialog will not be disposed
|
||||||
|
GUIUtil.executeAfterDelay(100, () -> {
|
||||||
|
disposeDialog();
|
||||||
|
document.undo();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
buttonPanel.add(cancelButton, "split 2, right, gapleft 30lp");
|
buttonPanel.add(cancelButton, "split 2, right, gapleft 30lp");
|
||||||
|
@ -52,6 +52,7 @@ import javax.swing.tree.DefaultMutableTreeNode;
|
|||||||
import javax.swing.tree.TreePath;
|
import javax.swing.tree.TreePath;
|
||||||
|
|
||||||
import net.sf.openrocket.arch.SystemInfo;
|
import net.sf.openrocket.arch.SystemInfo;
|
||||||
|
import net.sf.openrocket.gui.util.UITheme;
|
||||||
import net.sf.openrocket.gui.widgets.SaveFileChooser;
|
import net.sf.openrocket.gui.widgets.SaveFileChooser;
|
||||||
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
import net.sf.openrocket.rocketcomponent.FlightConfiguration;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
@ -1162,7 +1163,7 @@ public class GeneralOptimizationDialog extends JDialog {
|
|||||||
|
|
||||||
// TODO: update this dynamically instead of hard-coded values
|
// TODO: update this dynamically instead of hard-coded values
|
||||||
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
||||||
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
|
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS && UITheme.isLightTheme(GUIUtil.getUITheme())) {
|
||||||
Dimension currentSize = chooser.getPreferredSize();
|
Dimension currentSize = chooser.getPreferredSize();
|
||||||
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
||||||
chooser.setPreferredSize(newSize);
|
chooser.setPreferredSize(newSize);
|
||||||
|
@ -354,15 +354,10 @@ public class GeneralPreferencesPanel extends PreferencesPanel {
|
|||||||
trans.get("generalprefs.ImportWarning.title"),
|
trans.get("generalprefs.ImportWarning.title"),
|
||||||
JOptionPane.WARNING_MESSAGE);
|
JOptionPane.WARNING_MESSAGE);
|
||||||
|
|
||||||
// Introduce a small delay before showing the PreferencesDialog
|
// Need to execute after delay, otherwise the dialog will not be disposed
|
||||||
Timer timer = new Timer(100, new ActionListener() { // 100ms delay
|
GUIUtil.executeAfterDelay(100, () -> {
|
||||||
@Override
|
PreferencesDialog.showPreferences(parent.getParentFrame()); // Refresh the preferences dialog
|
||||||
public void actionPerformed(ActionEvent arg0) {
|
|
||||||
PreferencesDialog.showPreferences(parent.getParentFrame()); // Refresh the preferences dialog
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
timer.setRepeats(false); // Only execute once
|
|
||||||
timer.start(); // Start the timer
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -36,6 +36,7 @@ import net.sf.openrocket.gui.adaptors.PresetModel;
|
|||||||
import net.sf.openrocket.gui.components.StyledLabel;
|
import net.sf.openrocket.gui.components.StyledLabel;
|
||||||
import net.sf.openrocket.gui.util.GUIUtil;
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
import net.sf.openrocket.gui.util.SwingPreferences;
|
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||||
|
import net.sf.openrocket.gui.util.TableUIPreferences;
|
||||||
import net.sf.openrocket.l10n.Translator;
|
import net.sf.openrocket.l10n.Translator;
|
||||||
import net.sf.openrocket.preset.ComponentPreset;
|
import net.sf.openrocket.preset.ComponentPreset;
|
||||||
import net.sf.openrocket.preset.TypedKey;
|
import net.sf.openrocket.preset.TypedKey;
|
||||||
@ -52,6 +53,7 @@ import net.sf.openrocket.utils.TableRowTraversalPolicy;
|
|||||||
*/
|
*/
|
||||||
@SuppressWarnings("serial")
|
@SuppressWarnings("serial")
|
||||||
public class ComponentPresetChooserDialog extends JDialog {
|
public class ComponentPresetChooserDialog extends JDialog {
|
||||||
|
private static final String TABLE_ID = "CmpPrst.";
|
||||||
|
|
||||||
private static final Translator trans = Application.getTranslator();
|
private static final Translator trans = Application.getTranslator();
|
||||||
|
|
||||||
@ -154,10 +156,10 @@ public class ComponentPresetChooserDialog extends JDialog {
|
|||||||
// need to create componentSelectionTable before filter checkboxes,
|
// need to create componentSelectionTable before filter checkboxes,
|
||||||
// but add to panel after
|
// but add to panel after
|
||||||
componentSelectionTable = new ComponentPresetTable(presetType, presets, displayedColumnKeys);
|
componentSelectionTable = new ComponentPresetTable(presetType, presets, displayedColumnKeys);
|
||||||
GUIUtil.setAutomaticColumnTableWidths(componentSelectionTable, 20);
|
|
||||||
|
// Make the first column (the favorite column) as small as possible
|
||||||
int w = componentSelectionTable.getRowHeight() + 4;
|
int w = componentSelectionTable.getRowHeight() + 4;
|
||||||
XTableColumnModel tm = componentSelectionTable.getXColumnModel();
|
XTableColumnModel tm = componentSelectionTable.getXColumnModel();
|
||||||
//TableColumn tc = componentSelectionTable.getColumnModel().getColumn(0);
|
|
||||||
TableColumn tc = tm.getColumn(0);
|
TableColumn tc = tm.getColumn(0);
|
||||||
tc.setPreferredWidth(w);
|
tc.setPreferredWidth(w);
|
||||||
tc.setMaxWidth(w);
|
tc.setMaxWidth(w);
|
||||||
@ -166,7 +168,14 @@ public class ComponentPresetChooserDialog extends JDialog {
|
|||||||
// The normal left/right and tab/shift-tab key action traverses each cell/column of the table instead of going to the next row.
|
// The normal left/right and tab/shift-tab key action traverses each cell/column of the table instead of going to the next row.
|
||||||
TableRowTraversalPolicy.setTableRowTraversalPolicy(componentSelectionTable);
|
TableRowTraversalPolicy.setTableRowTraversalPolicy(componentSelectionTable);
|
||||||
|
|
||||||
|
// Add checkboxes (legacy, match fore diameter, ...)
|
||||||
panel.add(getFilterCheckboxes(tm, legacyColumnIndex), "wrap para");
|
panel.add(getFilterCheckboxes(tm, legacyColumnIndex), "wrap para");
|
||||||
|
|
||||||
|
// Load the table UI settings from the preferences
|
||||||
|
boolean legacySelected = showLegacyCheckBox.isSelected();
|
||||||
|
TableUIPreferences.loadTableUIPreferences(componentSelectionTable, TABLE_ID + component.getComponentName(),
|
||||||
|
preferences.getTablePreferences());
|
||||||
|
showLegacyCheckBox.setSelected(legacySelected); // Restore legacy state (may change during UI preference loading)
|
||||||
|
|
||||||
JScrollPane scrollpane = new JScrollPane();
|
JScrollPane scrollpane = new JScrollPane();
|
||||||
scrollpane.setViewportView(componentSelectionTable);
|
scrollpane.setViewportView(componentSelectionTable);
|
||||||
@ -202,6 +211,8 @@ public class ComponentPresetChooserDialog extends JDialog {
|
|||||||
closeButton.addActionListener(new ActionListener() {
|
closeButton.addActionListener(new ActionListener() {
|
||||||
@Override
|
@Override
|
||||||
public void actionPerformed(ActionEvent e) {
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
TableUIPreferences.storeTableUIPreferences(componentSelectionTable, TABLE_ID + component.getComponentName(),
|
||||||
|
preferences.getTablePreferences());
|
||||||
ComponentPresetChooserDialog.this.setVisible(false);
|
ComponentPresetChooserDialog.this.setVisible(false);
|
||||||
applySelectedPreset();
|
applySelectedPreset();
|
||||||
}
|
}
|
||||||
@ -214,7 +225,6 @@ public class ComponentPresetChooserDialog extends JDialog {
|
|||||||
GUIUtil.rememberWindowSize(this);
|
GUIUtil.rememberWindowSize(this);
|
||||||
this.setLocationByPlatform(true);
|
this.setLocationByPlatform(true);
|
||||||
GUIUtil.rememberWindowPosition(this);
|
GUIUtil.rememberWindowPosition(this);
|
||||||
GUIUtil.rememberTableColumnWidths(componentSelectionTable, "Presets" + component.getClass().getCanonicalName());
|
|
||||||
|
|
||||||
updateFilters();
|
updateFilters();
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,10 @@ public class ComponentPresetTable extends JTable {
|
|||||||
ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex);
|
ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex);
|
||||||
Application.getComponentPresetDao().setFavorite(preset, presetType, (Boolean) aValue);
|
Application.getComponentPresetDao().setFavorite(preset, presetType, (Boolean) aValue);
|
||||||
ComponentPresetTable.this.updateFavorites();
|
ComponentPresetTable.this.updateFavorites();
|
||||||
ComponentPresetTable.this.setRowSelectionInterval(rowIndex, rowIndex);
|
int viewIndex = ComponentPresetTable.this.convertRowIndexToView(rowIndex);
|
||||||
|
if (viewIndex != -1) {
|
||||||
|
ComponentPresetTable.this.setRowSelectionInterval(viewIndex, viewIndex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -163,7 +166,7 @@ public class ComponentPresetTable extends JTable {
|
|||||||
this.sorter.toggleSortOrder(2); // Sort by the first column (manufacturer) by default
|
this.sorter.toggleSortOrder(2); // Sort by the first column (manufacturer) by default
|
||||||
|
|
||||||
for (TableColumn hiddenColumn : this.hiddenColumns) {
|
for (TableColumn hiddenColumn : this.hiddenColumns) {
|
||||||
this.tableColumnModel.setColumnVisible(hiddenColumn, false);
|
this.tableColumnModel.removeColumn(hiddenColumn);
|
||||||
}
|
}
|
||||||
|
|
||||||
JTableHeader header = this.getTableHeader();
|
JTableHeader header = this.getTableHeader();
|
||||||
|
@ -239,4 +239,13 @@ public class XTableColumnModel extends DefaultTableColumnModel {
|
|||||||
public TableColumn getColumn(int columnIndex, boolean onlyVisible) {
|
public TableColumn getColumn(int columnIndex, boolean onlyVisible) {
|
||||||
return tableColumns.elementAt(columnIndex);
|
return tableColumns.elementAt(columnIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an Enumeration of all columns, regardless of their visibility.
|
||||||
|
*
|
||||||
|
* @return an Enumeration of all TableColumn objects in this model
|
||||||
|
*/
|
||||||
|
public Enumeration<TableColumn> getAllColumns() {
|
||||||
|
return allTableColumns.elements();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,7 +44,9 @@ import javax.swing.table.DefaultTableCellRenderer;
|
|||||||
import net.sf.openrocket.arch.SystemInfo;
|
import net.sf.openrocket.arch.SystemInfo;
|
||||||
import net.sf.openrocket.gui.components.CsvOptionPanel;
|
import net.sf.openrocket.gui.components.CsvOptionPanel;
|
||||||
import net.sf.openrocket.gui.util.FileHelper;
|
import net.sf.openrocket.gui.util.FileHelper;
|
||||||
|
import net.sf.openrocket.gui.util.GUIUtil;
|
||||||
import net.sf.openrocket.gui.util.SwingPreferences;
|
import net.sf.openrocket.gui.util.SwingPreferences;
|
||||||
|
import net.sf.openrocket.gui.util.UITheme;
|
||||||
import net.sf.openrocket.gui.widgets.SaveFileChooser;
|
import net.sf.openrocket.gui.widgets.SaveFileChooser;
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
@ -534,7 +536,7 @@ public class SimulationPanel extends JPanel {
|
|||||||
|
|
||||||
// TODO: update this dynamically instead of hard-coded values
|
// TODO: update this dynamically instead of hard-coded values
|
||||||
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
||||||
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
|
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS && UITheme.isLightTheme(GUIUtil.getUITheme())) {
|
||||||
Dimension currentSize = chooser.getPreferredSize();
|
Dimension currentSize = chooser.getPreferredSize();
|
||||||
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
Dimension newSize = new Dimension((int) (1.5 * currentSize.width), (int) (1.3 * currentSize.height));
|
||||||
chooser.setPreferredSize(newSize);
|
chooser.setPreferredSize(newSize);
|
||||||
|
@ -51,12 +51,14 @@ import javax.swing.RootPaneContainer;
|
|||||||
import javax.swing.SpinnerModel;
|
import javax.swing.SpinnerModel;
|
||||||
import javax.swing.SpinnerNumberModel;
|
import javax.swing.SpinnerNumberModel;
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
import javax.swing.Timer;
|
||||||
import javax.swing.UIManager;
|
import javax.swing.UIManager;
|
||||||
import javax.swing.border.TitledBorder;
|
import javax.swing.border.TitledBorder;
|
||||||
import javax.swing.event.ChangeListener;
|
import javax.swing.event.ChangeListener;
|
||||||
import javax.swing.table.AbstractTableModel;
|
import javax.swing.table.AbstractTableModel;
|
||||||
import javax.swing.table.DefaultTableColumnModel;
|
import javax.swing.table.DefaultTableColumnModel;
|
||||||
import javax.swing.table.DefaultTableModel;
|
import javax.swing.table.DefaultTableModel;
|
||||||
|
import javax.swing.table.TableCellRenderer;
|
||||||
import javax.swing.table.TableColumn;
|
import javax.swing.table.TableColumn;
|
||||||
import javax.swing.table.TableColumnModel;
|
import javax.swing.table.TableColumnModel;
|
||||||
import javax.swing.table.TableModel;
|
import javax.swing.table.TableModel;
|
||||||
@ -404,36 +406,6 @@ public class GUIUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void rememberTableColumnWidths(final JTable table, String keyName) {
|
|
||||||
final String key = keyName == null ? table.getClass().getName() : keyName;
|
|
||||||
Enumeration<TableColumn> columns = table.getColumnModel().getColumns();
|
|
||||||
while (columns.hasMoreElements()) {
|
|
||||||
TableColumn column = columns.nextElement();
|
|
||||||
column.addPropertyChangeListener(new PropertyChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void propertyChange(PropertyChangeEvent evt) {
|
|
||||||
if (evt.getPropertyName().equals("width")) {
|
|
||||||
log.debug("Storing width of " + table.getName() + "-" + column + ": " + column.getWidth());
|
|
||||||
((SwingPreferences) Application.getPreferences()).setTableColumnWidth(
|
|
||||||
key, column.getModelIndex(), column.getWidth());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
final Integer width = ((SwingPreferences) Application.getPreferences()).getTableColumnWidth(
|
|
||||||
key, column.getModelIndex());
|
|
||||||
if (width != null) {
|
|
||||||
column.setPreferredWidth(width);
|
|
||||||
} else {
|
|
||||||
column.setPreferredWidth(getOptimalColumnWidth(table, column.getModelIndex()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void rememberTableColumnWidths(final JTable table) {
|
|
||||||
rememberTableColumnWidths(table, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int getOptimalColumnWidth(JTable table, int columnIndex) {
|
public static int getOptimalColumnWidth(JTable table, int columnIndex) {
|
||||||
if (columnIndex >= table.getColumnModel().getColumnCount()) {
|
if (columnIndex >= table.getColumnModel().getColumnCount()) {
|
||||||
return -1;
|
return -1;
|
||||||
@ -476,26 +448,46 @@ public class GUIUtil {
|
|||||||
window.setLocation(position);
|
window.setLocation(position);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
public static void setAutomaticColumnTableWidths(JTable table, int max) {
|
* Computes the optimal column widths for the specified table, based on the content in all rows for that column,
|
||||||
|
* and optionally also the column header content.
|
||||||
|
* @param table the table
|
||||||
|
* @param max the maximum width for a column
|
||||||
|
* @param includeHeaderWidth whether to include the width of the column header
|
||||||
|
* @return an array of column widths
|
||||||
|
*/
|
||||||
|
public static int[] computeOptimalColumnWidths(JTable table, int max, boolean includeHeaderWidth) {
|
||||||
int columns = table.getColumnCount();
|
int columns = table.getColumnCount();
|
||||||
int widths[] = new int[columns];
|
int[] widths = new int[columns];
|
||||||
Arrays.fill(widths, 1);
|
Arrays.fill(widths, 1);
|
||||||
|
|
||||||
|
// Consider the width required by the header text if includeHeaderWidth is true
|
||||||
|
if (includeHeaderWidth) {
|
||||||
|
TableCellRenderer headerRenderer = table.getTableHeader().getDefaultRenderer();
|
||||||
|
for (int col = 0; col < columns; col++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(col);
|
||||||
|
Component headerComp = headerRenderer.getTableCellRendererComponent(table, column.getHeaderValue(), false, false, 0, col);
|
||||||
|
widths[col] = headerComp.getPreferredSize().width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare header width to the width required by the cell data
|
||||||
for (int row = 0; row < table.getRowCount(); row++) {
|
for (int row = 0; row < table.getRowCount(); row++) {
|
||||||
for (int col = 0; col < columns; col++) {
|
for (int col = 0; col < columns; col++) {
|
||||||
Object value = table.getValueAt(row, col);
|
Object value = table.getValueAt(row, col);
|
||||||
//System.out.println("row=" + row + " col=" + col + " : " + value);
|
TableCellRenderer cellRenderer = table.getCellRenderer(row, col);
|
||||||
widths[col] = Math.max(widths[col], value == null ? 0 : value.toString().length());
|
Component cellComp = cellRenderer.getTableCellRendererComponent(table, value, false, false, row, col);
|
||||||
|
int cellWidth = cellComp.getPreferredSize().width;
|
||||||
|
widths[col] = Math.max(widths[col], cellWidth);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
for (int col = 0; col < columns; col++) {
|
for (int col = 0; col < columns; col++) {
|
||||||
//System.err.println("Setting column " + col + " to width " + widths[col]);
|
widths[col] = Math.min(widths[col], max * 100); // Adjusting for your max value and scaling
|
||||||
table.getColumnModel().getColumn(col).setPreferredWidth(Math.min(widths[col], max) * 100);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return widths;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Window getWindowAncestor(Component c) {
|
public static Window getWindowAncestor(Component c) {
|
||||||
@ -761,5 +753,22 @@ public class GUIUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Executes the given code after a specified delay.
|
||||||
|
*
|
||||||
|
* @param delayMillis the delay in milliseconds.
|
||||||
|
* @param runnable the code to be executed after the delay.
|
||||||
|
*/
|
||||||
|
public static void executeAfterDelay(int delayMillis, Runnable runnable) {
|
||||||
|
Timer timer = new Timer(delayMillis, new ActionListener() {
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent arg0) {
|
||||||
|
runnable.run();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
timer.setRepeats(false);
|
||||||
|
timer.start();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -55,7 +55,7 @@ public abstract class PreferencesExporter {
|
|||||||
|
|
||||||
// TODO: update this dynamically instead of hard-coded values
|
// TODO: update this dynamically instead of hard-coded values
|
||||||
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
// The macOS file chooser has an issue where it does not update its size when the accessory is added.
|
||||||
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS) {
|
if (SystemInfo.getPlatform() == SystemInfo.Platform.MAC_OS && UITheme.isLightTheme(GUIUtil.getUITheme())) {
|
||||||
Dimension currentSize = chooser.getPreferredSize();
|
Dimension currentSize = chooser.getPreferredSize();
|
||||||
Dimension newSize = new Dimension((int) (1.35 * currentSize.width), (int) (1.2 * currentSize.height));
|
Dimension newSize = new Dimension((int) (1.35 * currentSize.width), (int) (1.2 * currentSize.height));
|
||||||
chooser.setPreferredSize(newSize);
|
chooser.setPreferredSize(newSize);
|
||||||
|
@ -136,6 +136,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
public Preferences getPreferences() {
|
public Preferences getPreferences() {
|
||||||
return PREFNODE;
|
return PREFNODE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the preference node responsible for saving UI window information (position, size...)
|
||||||
|
* @return the preference node for window information
|
||||||
|
*/
|
||||||
|
public Preferences getWindowsPreferences() {
|
||||||
|
return PREFNODE.node(NODE_WINDOWS);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the preference node responsible for saving table information (column widths, order...)
|
||||||
|
* @return the preference node for table information
|
||||||
|
*/
|
||||||
|
public Preferences getTablePreferences() {
|
||||||
|
return PREFNODE.node(NODE_TABLES);
|
||||||
|
}
|
||||||
|
|
||||||
public void clearPreferences() {
|
public void clearPreferences() {
|
||||||
try {
|
try {
|
||||||
@ -550,7 +566,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
|
|
||||||
public Point getWindowPosition(Class<?> c) {
|
public Point getWindowPosition(Class<?> c) {
|
||||||
int x, y;
|
int x, y;
|
||||||
String pref = PREFNODE.node(NODE_WINDOWS).get("position." + c.getCanonicalName(), null);
|
String pref = getWindowsPreferences().get("position." + c.getCanonicalName(), null);
|
||||||
|
|
||||||
if (pref == null)
|
if (pref == null)
|
||||||
return null;
|
return null;
|
||||||
@ -575,7 +591,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setWindowPosition(Class<?> c, Point p) {
|
public void setWindowPosition(Class<?> c, Point p) {
|
||||||
PREFNODE.node(NODE_WINDOWS).put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
|
getWindowsPreferences().put("position." + c.getCanonicalName(), "" + p.x + "," + p.y);
|
||||||
storeVersion();
|
storeVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +619,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
|
|
||||||
public Dimension getWindowSize(Class<?> c) {
|
public Dimension getWindowSize(Class<?> c) {
|
||||||
int x, y;
|
int x, y;
|
||||||
String pref = PREFNODE.node(NODE_WINDOWS).get("size." + c.getCanonicalName(), null);
|
String pref = getWindowsPreferences().get("size." + c.getCanonicalName(), null);
|
||||||
|
|
||||||
if (pref == null)
|
if (pref == null)
|
||||||
return null;
|
return null;
|
||||||
@ -622,22 +638,22 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
|
|
||||||
|
|
||||||
public boolean isWindowMaximized(Class<?> c) {
|
public boolean isWindowMaximized(Class<?> c) {
|
||||||
String pref = PREFNODE.node(NODE_WINDOWS).get("size." + c.getCanonicalName(), null);
|
String pref = getWindowsPreferences().get("size." + c.getCanonicalName(), null);
|
||||||
return "max".equals(pref);
|
return "max".equals(pref);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWindowSize(Class<?> c, Dimension d) {
|
public void setWindowSize(Class<?> c, Dimension d) {
|
||||||
PREFNODE.node(NODE_WINDOWS).put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
|
getWindowsPreferences().put("size." + c.getCanonicalName(), "" + d.width + "," + d.height);
|
||||||
storeVersion();
|
storeVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setWindowMaximized(Class<?> c) {
|
public void setWindowMaximized(Class<?> c) {
|
||||||
PREFNODE.node(NODE_WINDOWS).put("size." + c.getCanonicalName(), "max");
|
getWindowsPreferences().put("size." + c.getCanonicalName(), "max");
|
||||||
storeVersion();
|
storeVersion();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Integer getTableColumnWidth(String keyName, int columnIdx) {
|
public Integer getTableColumnWidth(String keyName, int columnIdx) {
|
||||||
String pref = PREFNODE.node(NODE_TABLES).get(
|
String pref = getTablePreferences().get(
|
||||||
"cw." + keyName + "." + columnIdx, null);
|
"cw." + keyName + "." + columnIdx, null);
|
||||||
if (pref == null)
|
if (pref == null)
|
||||||
return null;
|
return null;
|
||||||
@ -655,7 +671,7 @@ public class SwingPreferences extends net.sf.openrocket.startup.Preferences {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setTableColumnWidth(String keyName, int columnIdx, Integer width) {
|
public void setTableColumnWidth(String keyName, int columnIdx, Integer width) {
|
||||||
PREFNODE.node(NODE_TABLES).put(
|
getTablePreferences().put(
|
||||||
"cw." + keyName + "." + columnIdx, width.toString());
|
"cw." + keyName + "." + columnIdx, width.toString());
|
||||||
storeVersion();
|
storeVersion();
|
||||||
}
|
}
|
||||||
|
107
swing/src/net/sf/openrocket/gui/util/TableUIPreferences.java
Normal file
107
swing/src/net/sf/openrocket/gui/util/TableUIPreferences.java
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
package net.sf.openrocket.gui.util;
|
||||||
|
|
||||||
|
import net.sf.openrocket.gui.dialogs.preset.XTableColumnModel;
|
||||||
|
|
||||||
|
import javax.swing.JTable;
|
||||||
|
import javax.swing.table.TableColumn;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Enumeration;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.prefs.Preferences;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class for storing and loading the following table UI preferences:
|
||||||
|
* - column width
|
||||||
|
* - column order
|
||||||
|
* - column visibility
|
||||||
|
*/
|
||||||
|
public class TableUIPreferences {
|
||||||
|
|
||||||
|
private static final String TABLE_COLUMN_WIDTH_PREFIX = ".cw.";
|
||||||
|
private static final String TABLE_COLUMN_ORDER_PREFIX = ".co.";
|
||||||
|
private static final String TABLE_COLUMN_VISIBILITY_PREFIX = ".cv.";
|
||||||
|
|
||||||
|
public static void storeTableUIPreferences(JTable table, String tableName, Preferences preferences) {
|
||||||
|
// Store column widths
|
||||||
|
for (int i = 0; i < table.getColumnCount(); i++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(i);
|
||||||
|
int width = column.getWidth();
|
||||||
|
preferences.putInt(tableName + TABLE_COLUMN_WIDTH_PREFIX + column.getIdentifier(), width);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store column order
|
||||||
|
for (int i = 0; i < table.getColumnCount(); i++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(i);
|
||||||
|
preferences.putInt(tableName + TABLE_COLUMN_ORDER_PREFIX + column.getIdentifier(), i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store column visibility
|
||||||
|
if (table.getColumnModel() instanceof XTableColumnModel customModel) {
|
||||||
|
Enumeration<TableColumn> columns = customModel.getAllColumns();
|
||||||
|
while (columns.hasMoreElements()) {
|
||||||
|
TableColumn column = columns.nextElement();
|
||||||
|
boolean isVisible = customModel.isColumnVisible(column);
|
||||||
|
preferences.putBoolean(tableName + TABLE_COLUMN_VISIBILITY_PREFIX + column.getIdentifier(), isVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void loadTableUIPreferences(JTable table, String tableName, Preferences preferences) {
|
||||||
|
// Ensure all columns are visible
|
||||||
|
Enumeration<TableColumn> allColumns = table.getColumnModel().getColumns();
|
||||||
|
List<TableColumn> removedColumns = new ArrayList<>();
|
||||||
|
while (allColumns.hasMoreElements()) {
|
||||||
|
TableColumn column = allColumns.nextElement();
|
||||||
|
if (table.convertColumnIndexToView(column.getModelIndex()) == -1) {
|
||||||
|
removedColumns.add(column);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (TableColumn col : removedColumns) {
|
||||||
|
table.addColumn(col);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get all columns from the table's column model and restore visibility
|
||||||
|
if (table.getColumnModel() instanceof XTableColumnModel customModel) {
|
||||||
|
Enumeration<TableColumn> columns = customModel.getAllColumns(); // Use getAllColumns to get all columns, including invisible ones
|
||||||
|
while (columns.hasMoreElements()) {
|
||||||
|
TableColumn column = columns.nextElement();
|
||||||
|
String identifier = column.getIdentifier().toString();
|
||||||
|
// Default to true if the preference is not found
|
||||||
|
boolean isVisible = preferences.getBoolean(tableName + TABLE_COLUMN_VISIBILITY_PREFIX + identifier, true);
|
||||||
|
customModel.setColumnVisible(column, isVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now, restore column order
|
||||||
|
for (int i = 0; i < table.getColumnCount(); i++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(i);
|
||||||
|
int storedOrder = preferences.getInt(tableName + TABLE_COLUMN_ORDER_PREFIX + column.getIdentifier(), i);
|
||||||
|
if (storedOrder != i && storedOrder < table.getColumnCount()) {
|
||||||
|
table.moveColumn(table.convertColumnIndexToView(column.getModelIndex()), storedOrder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if any column width is missing from preferences
|
||||||
|
boolean computeOptimalWidths = false;
|
||||||
|
for (int i = 0; i < table.getColumnCount() && !computeOptimalWidths; i++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(i);
|
||||||
|
if (preferences.get(tableName + TABLE_COLUMN_WIDTH_PREFIX + column.getIdentifier(), null) == null) {
|
||||||
|
computeOptimalWidths = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If any column width is missing, compute optimal widths for all columns
|
||||||
|
int[] optimalWidths = null;
|
||||||
|
if (computeOptimalWidths) {
|
||||||
|
optimalWidths = GUIUtil.computeOptimalColumnWidths(table, 20, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Restore column widths
|
||||||
|
for (int i = 0; i < table.getColumnCount(); i++) {
|
||||||
|
TableColumn column = table.getColumnModel().getColumn(i);
|
||||||
|
int defaultWidth = (optimalWidths != null) ? optimalWidths[i] : column.getWidth();
|
||||||
|
int width = preferences.getInt(tableName + TABLE_COLUMN_WIDTH_PREFIX + column.getIdentifier(), defaultWidth);
|
||||||
|
column.setPreferredWidth(width);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user