diff --git a/core/.classpath b/core/.classpath
index 453ab552f..06a9ed43d 100644
--- a/core/.classpath
+++ b/core/.classpath
@@ -17,6 +17,7 @@
+
diff --git a/core/resources-src/pix/componenticons/pods-large.xcf.gz b/core/resources-src/pix/componenticons/pods-large.xcf.gz
index 0c28362eb..6d81c3d0e 100644
Binary files a/core/resources-src/pix/componenticons/pods-large.xcf.gz and b/core/resources-src/pix/componenticons/pods-large.xcf.gz differ
diff --git a/core/resources-src/pix/componenticons/pods-small.xcf.gz b/core/resources-src/pix/componenticons/pods-small.xcf.gz
index 3af13b5f1..17e95a14e 100644
Binary files a/core/resources-src/pix/componenticons/pods-small.xcf.gz and b/core/resources-src/pix/componenticons/pods-small.xcf.gz differ
diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties
index be9e2bbba..af0219e2d 100644
--- a/core/resources/l10n/messages.properties
+++ b/core/resources/l10n/messages.properties
@@ -2098,7 +2098,7 @@ ComponentPresetChooserDialog.menu.sortDesc = Sort Descending
ComponentPresetChooserDialog.menu.units = Units
ComponentPresetChooserDialog.checkbox.showAllCompatible = Show all compatible
ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = Show Legacy Database
-ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog
+ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog
Directly apply a preset by double-clicking it or by selecting it and closing this window.
table.column.Favorite = Favorite
table.column.Legacy = Legacy
table.column.Manufacturer = Manufacturer
diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties
index 7190965fc..9d1c2168f 100644
--- a/core/resources/l10n/messages_ru.properties
+++ b/core/resources/l10n/messages_ru.properties
@@ -293,8 +293,10 @@ pref.dlg.lbl.launchWarning.ttip = \u0412\u044B \u043D\u0435 \u043F\u0435\u0440\u
pref.dlg.lbl.Positiontoinsert = \u041C\u0435\u0441\u0442\u043E \u0434\u043B\u044F \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432:
pref.dlg.lbl.PositiontoinsertStages = \u041C\u0435\u0441\u0442\u043E \u0434\u043B\u044F \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u044F \u043D\u043E\u0432\u044B\u0445 \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439:
pref.dlg.lbl.Confirmdeletion = \u041F\u043E\u0434\u0442\u0432\u0435\u0440\u0436\u0434\u0435\u043D\u0438\u0435 \u0443\u0434\u0430\u043B\u0435\u043D\u0438\u044F \u0440\u0430\u0441\u0447\u0435\u0442\u0430:
-pref.dlg.checkbox.Runsimulations = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432.
+pref.dlg.checkbox.Runsimulations = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0435 \u0440\u0430\u0441\u0447\u0435\u0442\u044B \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u0432\u043A\u043B\u0430\u0434\u043A\u0438 \u0440\u0430\u0441\u0447\u0435\u0442\u043E\u0432
pref.dlg.checkbox.Updateestimates = \u041E\u0431\u043D\u043E\u0432\u043B\u044F\u0442\u044C \u043F\u0440\u0435\u0434\u043F\u043E\u043B\u0430\u0433\u0430\u0435\u043C\u044B\u0435 \u043F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u043F\u043E\u043B\u0435\u0442\u0430 \u0432 \u043E\u043A\u043D\u0435 \u043F\u0440\u043E\u0435\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F
+pref.dlg.checkbox.Markers = \u041F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u043E\u043F\u043E\u0440/\u0440\u0430\u0437\u0433\u043E\u043D\u043D\u044B\u0445 \u0431\u043B\u043E\u043A\u043E\u0432 \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0432\u044B\u0431\u0440\u0430\u043D\u044B
+pref.dlg.checkbox.Markers.ttip = \u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u043E\u043F\u043E\u0440/\u0440\u0430\u0437\u0433\u043E\u043D\u043D\u044B\u0445 \u0431\u043B\u043E\u043A\u043E\u0432 \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C\u0441\u044F, \u0442\u043E\u043B\u044C\u043A\u043E \u0435\u0441\u043B\u0438 \u043E\u043D\u0438 \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
\u0412 \u043F\u0440\u043E\u0442\u0438\u0432\u043D\u043E\u043C \u0441\u043B\u0443\u0447\u0430\u0435 \u043C\u0430\u0440\u043A\u0435\u0440\u044B \u0431\u0443\u0434\u0443\u0442 \u043F\u043E\u043A\u0430\u0437\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u0432\u0441\u0435\u0433\u0434\u0430.
pref.dlg.checkbox.AlwaysOpenLeftmost = \u0412\u0441\u0435\u0433\u0434\u0430 \u0432\u044B\u0431\u0438\u0440\u0430\u0442\u044C \u043F\u0435\u0440\u0432\u0443\u044E \u0432\u043A\u043B\u0430\u0434\u043A\u0443 \u043F\u0440\u0438 \u043E\u0442\u043A\u0440\u044B\u0442\u0438\u0438 \u043E\u043A\u043D\u0430 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430
pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = \u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u0435 \u043E\u043A\u043D\u043E \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 \u0432\u0441\u0435\u0433\u0434\u0430 \u0431\u0443\u0434\u0435\u0442 \u043E\u0442\u043A\u0440\u044B\u0432\u0430\u0442\u044C\u0441\u044F \u0441 \u043F\u0435\u0440\u0432\u043E\u0439 \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u043E\u0439 \u0432\u043A\u043B\u0430\u0434\u043A\u043E\u0439.
\u0415\u0441\u043B\u0438 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A \u043D\u0435 \u0443\u0441\u0442\u0430\u043D\u043E\u0432\u043B\u0435\u043D, \u0431\u0443\u0434\u0435\u0442 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C\u0441\u044F \u043F\u0440\u0435\u0434\u044B\u0434\u0443\u0449\u0430\u044F \u0432\u044B\u0431\u0440\u0430\u043D\u043D\u0430\u044F \u0432\u043A\u043B\u0430\u0434\u043A\u0430.
pref.dlg.lbl.User-definedthrust = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0435 \u043F\u0440\u043E\u0444\u0438\u043B\u0438 \u0442\u044F\u0433\u0438:
@@ -716,7 +718,7 @@ compaddbuttons.Launchlug = \u041D\u0430\u043F\u0440\u0430\u0432\u043B\u044F\u044
compaddbuttons.RailButton = \u0417\u0430\u0446\u0435\u043F\n\u043D\u0430\u043F\u0440\u0430\u0432\u043B\u044F\u044E\u0449\u0435\u0439
compaddbuttons.InnerComponent = \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u0438\u0435 \u0434\u0435\u0442\u0430\u043B\u0438
compaddbuttons.Innertube = \u0412\u043D\u0443\u0442\u0440\u0435\u043D\u043D\u044F\u044F\n\u0442\u0440\u0443\u0431\u0430
-compaddbuttons.Coupler = \u041C\u0443\u0444\u0442\u0430
+compaddbuttons.Coupler = \u0422\u0440\u0443\u0431\u0447\u0430\u0442\u0430\u044F\n\u043C\u0443\u0444\u0442\u0430
compaddbuttons.Centeringring = \u0426\u0435\u043D\u0442\u0440\u0438\u0440\u0443\u044E\u0449\u0435\u0435\n\u043A\u043E\u043B\u044C\u0446\u043E
compaddbuttons.Bulkhead = \u041F\u0435\u0440\u0435\u0431\u043E\u0440\u043A\u0430
compaddbuttons.Engineblock = \u0423\u043F\u043E\u0440\n\u0434\u0432\u0438\u0433\u0430\u0442\u0435\u043B\u044F
@@ -898,6 +900,7 @@ RocketCompCfg.tab.Figstyleopt = \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440
RocketCompCfg.tab.Comment = \u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435
RocketCompCfg.tab.Comment.ttip = \u0423\u043A\u0430\u0436\u0438\u0442\u0435 \u043F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 \u043A \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0443
RocketCompCfg.tab.Appearance = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0432\u0438\u0434
+RocketCompCfg.tab.Appearance.ttip = \u0412\u043D\u0435\u0448\u043D\u0438\u0439 \u0432\u0438\u0434 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430
RocketCompCfg.lbl.Mass = \u041C\u0430\u0441\u0441\u0430:
RocketCompCfg.lbl.Componentmass = \u041C\u0430\u0441\u0441\u0430 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430:
RocketCompCfg.lbl.overriddento = (\u043F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u043E \u043D\u0430
@@ -2074,8 +2077,10 @@ CustomFinImport.error.badimage = \u041D\u0435\u0432\u043E\u0437\u043C\u043E\u043
CustomFinImport.description = \u0418\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u0435 \u043F\u0440\u0435\u043E\u0431\u0440\u0430\u0437\u0443\u0435\u0442\u0441\u044F \u0432 \u0447\u0435\u0440\u043D\u043E-\u0431\u0435\u043B\u043E\u0435 (\u0433\u0434\u0435 \u0447\u0435\u0440\u043D\u044B\u0439 - \u0446\u0432\u0435\u0442 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430), \u0442\u0430\u043A \u0447\u0442\u043E \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0439\u0442\u0435 \u0447\u0435\u0440\u043D\u044B\u0439 \u0446\u0432\u0435\u0442 \u0434\u043B\u044F \u0440\u0438\u0441\u0443\u043D\u043A\u0430 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430 \u0438 \u0431\u0435\u043B\u044B\u0439 \u0438\u043B\u0438 \u0441\u0432\u0435\u0442\u043B\u044B\u0439 \u0446\u0432\u0435\u0442 \u0434\u043B\u044F \u0444\u043E\u043D\u0430. \u041E\u0441\u043D\u043E\u0432\u0430\u043D\u0438\u0435 \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u0430 \u0434\u043E\u043B\u0436\u043D\u043E \u043D\u0430\u0447\u0438\u043D\u0430\u0442\u044C\u0441\u044F \u0441\u043D\u0438\u0437\u0443 \u0438\u0437\u043E\u0431\u0440\u0430\u0436\u0435\u043D\u0438\u044F.
-PresetModel.lbl.custompreset = \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0020\u0434\u0435\u0442\u0430\u043b\u0435\u0439
-PresetModel.lbl.partsLib = \u0411\u0438\u0431\u043b\u0438\u043e\u0442\u0435\u043a\u0430\u0020\u0434\u0435\u0442\u0430\u043b\u0435\u0439
+PresetModel.combo.ttip = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0438\u0437 \u0441\u043F\u0438\u0441\u043A\u0430 \u0438\u0437\u0431\u0440\u0430\u043D\u043D\u044B\u0445 (\u0432\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u0445 \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C \u043E\u043A\u043D\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043E\u043A \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u043E\u0432)
\u0438\u043B\u0438 \u0432\u044B\u0431\u0435\u0440\u0438\u0442\u0435 "\u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u043A\u0438\u0439", \u0435\u0441\u043B\u0438 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0430 \u043D\u0435 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044F.
+PresetModel.lbl.custompreset = \u041F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u0435\u043B\u044C\u0441\u0438\u0439
+PresetModel.lbl.partsLib = \u0411\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0430 \u0434\u0435\u0442\u0430\u043B\u0435\u0439
+PresetModel.lbl.partsLib.ttip = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0434\u043B\u044F \u044D\u0442\u043E\u0433\u043E \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430 \u0440\u0430\u043A\u0435\u0442\u044B \u0438\u0437 \u0431\u0438\u0431\u043B\u0438\u043E\u0442\u0435\u043A\u0438 \u0434\u0435\u0442\u0430\u043B\u0435\u0439.
DecalModel.lbl.select = <\u043D\u0435\u0442>
DecalModel.lbl.choose = \u0418\u0437 \u0444\u0430\u0439\u043B\u0430...
@@ -2097,7 +2102,7 @@ ComponentPresetChooserDialog.menu.sortDesc = \u041F\u043E \u0443\u0431\u044B\u04
ComponentPresetChooserDialog.menu.units = \u0415\u0434\u0438\u043D\u0438\u0446\u044B \u0438\u0437\u043C\u0435\u0440\u0435\u043D\u0438\u044F
ComponentPresetChooserDialog.checkbox.showAllCompatible = \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0432\u0441\u0435 \u0441\u043E\u0432\u043C\u0435\u0441\u0442\u0438\u043C\u044B\u0435
ComponentPresetChooserDialog.checkbox.showLegacyCheckBox = \u041F\u043E\u043A\u0430\u0437\u0430\u0442\u044C \u0431\u0430\u0437\u0443 \u0434\u0430\u043D\u043D\u044B\u0445 \u0443\u0441\u0442\u0430\u0440\u0435\u0432\u0448\u0438\u0445
-ComponentPresetChooserDialog.lbl.favorites = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0434\u043B\u044F \u0434\u043E\u0431\u0430\u0432\u043B\u0435\u043D\u0438\u044F \u0432 \u0432\u044B\u043F\u0430\u0434\u0430\u044E\u0449\u0435\u0435 \u043C\u0435\u043D\u044E
+ComponentPresetChooserDialog.lbl.favorites = \u0423\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0435 \u044D\u0442\u043E\u0442 \u0444\u043B\u0430\u0436\u043E\u043A, \u0447\u0442\u043E\u0431\u044B \u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 \u0432 \u0440\u0430\u0441\u043A\u0440\u044B\u0432\u0430\u044E\u0449\u0435\u0435\u0441\u044F \u043C\u0435\u043D\u044E \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043E\u043A \u0432 \u0434\u0438\u0430\u043B\u043E\u0433\u043E\u0432\u043E\u043C \u043E\u043A\u043D\u0435 \u0440\u0435\u0434\u0430\u043A\u0442\u0438\u0440\u043E\u0432\u0430\u043D\u0438\u044F \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430.
table.column.Favorite = \u0418\u0437\u0431\u0440\u0430\u043D\u043D\u043E\u0435
table.column.Legacy = \u0423\u0441\u0442\u0430\u0440\u0435\u043B\u043E
table.column.Manufacturer = \u041F\u0440\u043E\u0438\u0437\u0432\u043E\u0434\u0438\u0442\u0435\u043B\u044C
diff --git a/core/resources/pix/componenticons/pods-large.png b/core/resources/pix/componenticons/pods-large.png
index a462c9193..f40055a4b 100644
Binary files a/core/resources/pix/componenticons/pods-large.png and b/core/resources/pix/componenticons/pods-large.png differ
diff --git a/core/resources/pix/componenticons/pods-small.png b/core/resources/pix/componenticons/pods-small.png
index 4deee6b90..17371e846 100644
Binary files a/core/resources/pix/componenticons/pods-small.png and b/core/resources/pix/componenticons/pods-small.png differ
diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java
index 3d750c651..90da3c320 100644
--- a/core/src/net/sf/openrocket/startup/Preferences.java
+++ b/core/src/net/sf/openrocket/startup/Preferences.java
@@ -63,6 +63,9 @@ public abstract class Preferences implements ChangeSource {
public static final String MOTOR_DIAMETER_FILTER = "MotorDiameterMatch";
public static final String MOTOR_HIDE_SIMILAR = "MotorHideSimilar";
public static final String MOTOR_HIDE_UNAVAILABLE = "MotorHideUnavailable";
+
+ public static final String MATCH_FORE_DIAMETER = "MatchForeDiameter";
+ public static final String MATCH_AFT_DIAMETER = "MatchAftDiameter";
// Node names
public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors";
@@ -490,6 +493,44 @@ public abstract class Preferences implements ChangeSource {
return this.getBoolean(SHOW_MARKERS, false);
}
+ /**
+ * Set whether the component preset chooser dialog should filter by fore diameter when the window is opened.
+ * @param enabled true if the fore diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final void setMatchForeDiameter(boolean enabled) {
+ this.putBoolean(MATCH_FORE_DIAMETER, enabled);
+ }
+
+ /**
+ * Answer if the component preset chooser dialog should filter by fore diameter when the window is opened.
+ *
+ * @return true if the fore diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final boolean isMatchForeDiameter() {
+ return this.getBoolean(MATCH_FORE_DIAMETER, true);
+ }
+
+ /**
+ * Set whether the component preset chooser dialog should filter by aft diameter when the window is opened.
+ * @param enabled true if the aft diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final void setMatchAftDiameter(boolean enabled) {
+ this.putBoolean(MATCH_AFT_DIAMETER, enabled);
+ }
+
+ /**
+ * Answer if the component preset chooser dialog should filter by aft diameter when the window is opened.
+ *
+ * @return true if the aft diameter filter should be enabled,
+ * false if it should be disabled.
+ */
+ public final boolean isMatchAftDiameter() {
+ return this.getBoolean(MATCH_AFT_DIAMETER, true);
+ }
+
/**
* Return the OpenRocket unique ID.
*
diff --git a/core/test/net/sf/openrocket/simulation/DisableStageTest.java b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
index 9b4b0f7df..d4b7d5a1d 100644
--- a/core/test/net/sf/openrocket/simulation/DisableStageTest.java
+++ b/core/test/net/sf/openrocket/simulation/DisableStageTest.java
@@ -19,6 +19,8 @@ import org.junit.Test;
* @author Sibo Van Gool
*/
public class DisableStageTest extends BaseTestCase {
+ private final double delta = 0.08; // 8 % error margin (simulations are not exact)
+
/**
* Tests that the simulation results are correct when a single stage is deactivated and re-activated.
*/
@@ -54,7 +56,6 @@ public class DisableStageTest extends BaseTestCase {
simDisabled.getActiveConfiguration().setAllStages(); // Re-enable all stages.
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simOriginal, simDisabled, simulationListener, delta);
}
@@ -84,7 +85,6 @@ public class DisableStageTest extends BaseTestCase {
SimulationListener simulationListener = new AbstractSimulationListener();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simRemoved, simDisabled, simulationListener, delta);
//// Test re-enableing the stage.
@@ -175,7 +175,6 @@ public class DisableStageTest extends BaseTestCase {
SimulationListener simulationListener = new AbstractSimulationListener();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simRemoved, simDisabled, simulationListener, delta);
//// Test re-enableing the stage.
@@ -243,7 +242,6 @@ public class DisableStageTest extends BaseTestCase {
simDisabled.getActiveConfiguration().setAllStages();
- double delta = 0.05; // 5 % error margin (simulations are not exact)
compareSims(simOriginal, simDisabled, simulationListener, delta);
}
diff --git a/snap/snapcraft.yaml b/snap/snapcraft.yaml
index aca598150..af6fe1e98 100644
--- a/snap/snapcraft.yaml
+++ b/snap/snapcraft.yaml
@@ -1,6 +1,6 @@
name: openrocket
adopt-info: openrocket
-grade: devel
+grade: stable
summary: A free, fully featured model rocket simulator.
description: |
OpenRocket is a free, fully featured model rocket simulator that allows you
@@ -77,6 +77,7 @@ parts:
prime:
- -usr/lib/jvm/java-*/lib/security/cacerts
- -usr/lib/jvm/java-*/jre/lib/security/cacerts
+ - -usr/lib/jvm/java-*/lib/security/blacklisted.certs
launcher:
plugin: dump
diff --git a/swing/.classpath b/swing/.classpath
index 7daea48ea..6878da13d 100644
--- a/swing/.classpath
+++ b/swing/.classpath
@@ -19,6 +19,7 @@
+
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
index 1733e942c..eaf0afa87 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetChooserDialog.java
@@ -28,7 +28,9 @@ import javax.swing.table.TableModel;
import net.miginfocom.swing.MigLayout;
import net.sf.openrocket.gui.adaptors.PresetModel;
+import net.sf.openrocket.gui.components.StyledLabel;
import net.sf.openrocket.gui.util.GUIUtil;
+import net.sf.openrocket.gui.util.SwingPreferences;
import net.sf.openrocket.l10n.Translator;
import net.sf.openrocket.preset.ComponentPreset;
import net.sf.openrocket.preset.TypedKey;
@@ -46,6 +48,8 @@ public class ComponentPresetChooserDialog extends JDialog {
private static final Translator trans = Application.getTranslator();
+ private final SwingPreferences preferences = (SwingPreferences) Application.getPreferences();
+
private final RocketComponent component;
private final ComponentPresetTable componentSelectionTable;
@@ -154,7 +158,7 @@ public class ComponentPresetChooserDialog extends JDialog {
scrollpane.setViewportView(componentSelectionTable);
panel.add(scrollpane, "grow, width 700lp, height 300lp, pushy, spanx, wrap rel");
- panel.add(new JLabel(Chars.UP_ARROW + " " + trans.get("lbl.favorites")), "spanx, gapleft 5px, wrap para");
+ panel.add(new StyledLabel(String.format("%s %s", Chars.UP_ARROW, trans.get("lbl.favorites")), -1), "spanx, gapleft 5px, wrap para");
// When double-clicking a preset row, apply the preset and close this dialog
componentSelectionTable.addMouseListener(new MouseAdapter() {
@@ -253,12 +257,14 @@ public class ComponentPresetChooserDialog extends JDialog {
foreDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterForeDiameter"));
final SymmetricComponent prevSym = curSym.getPreviousSymmetricComponent();
if (prevSym != null && foreDiameterColumnIndex >= 0) {
+ foreDiameterFilterCheckBox.setSelected(preferences.isMatchForeDiameter());
foreDiameterFilter = new ComponentPresetRowFilter(prevSym.getAftRadius() * 2.0, foreDiameterColumnIndex);
panel.add(foreDiameterFilterCheckBox, "wrap");
foreDiameterFilterCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateFilters();
+ preferences.setMatchForeDiameter(foreDiameterFilterCheckBox.isSelected());
}
});
}
@@ -269,12 +275,14 @@ public class ComponentPresetChooserDialog extends JDialog {
aftDiameterFilterCheckBox = new JCheckBox(trans.get("ComponentPresetChooserDialog.checkbox.filterAftDiameter"));
final SymmetricComponent nextSym = curSym.getNextSymmetricComponent();
if (nextSym != null && aftDiameterColumnIndex >= 0) {
+ aftDiameterFilterCheckBox.setSelected(preferences.isMatchAftDiameter());
aftDiameterFilter = new ComponentPresetRowFilter(nextSym.getForeRadius() * 2.0, aftDiameterColumnIndex);
panel.add(aftDiameterFilterCheckBox, "wrap");
aftDiameterFilterCheckBox.addItemListener(new ItemListener() {
@Override
public void itemStateChanged(ItemEvent e) {
updateFilters();
+ preferences.setMatchAftDiameter(aftDiameterFilterCheckBox.isSelected());
}
});
}
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
index c361baebf..62c1bb31b 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTable.java
@@ -77,9 +77,11 @@ public class ComponentPresetTable extends JTable {
if ( columnIndex != 0 ) {
return;
}
+ int selectedRow = ComponentPresetTable.this.getSelectedRow();
ComponentPreset preset = ComponentPresetTable.this.presets.get(rowIndex);
Application.getComponentPresetDao().setFavorite(preset, presetType, (Boolean) aValue);
ComponentPresetTable.this.updateFavorites();
+ ComponentPresetTable.this.setRowSelectionInterval(selectedRow, selectedRow);
}
@Override
diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
index 6bf137b28..04821f75d 100644
--- a/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
+++ b/swing/src/net/sf/openrocket/gui/dialogs/preset/ComponentPresetTableColumn.java
@@ -33,7 +33,7 @@ public abstract class ComponentPresetTableColumn extends TableColumn {
@Override
public Object getValueFromPreset( Set favorites, ComponentPreset preset ) {
- return Boolean.valueOf(favorites.contains(preset.preferenceKey()));
+ return favorites.contains(preset.preferenceKey());
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
index 0a51adacc..e9c10880e 100644
--- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
+++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java
@@ -418,18 +418,18 @@ public class BasicFrame extends JFrame {
button = new SelectColorButton(actions.getMoveDownAction());
panel.add(button, "sizegroup buttons, aligny 0%");
- button = new SelectColorButton(actions.getEditAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getEditAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons, gaptop 20%");
- button = new SelectColorButton(actions.getDuplicateAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getDuplicateAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons");
- button = new SelectColorButton(actions.getDeleteAction());
- button.setIcon(null);
+ button = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(button, actions.getDeleteAction());
button.setMnemonic(0);
panel.add(button, "sizegroup buttons");
diff --git a/swing/src/net/sf/openrocket/gui/main/RocketActions.java b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
index 86a3ea6bf..15ed1d597 100644
--- a/swing/src/net/sf/openrocket/gui/main/RocketActions.java
+++ b/swing/src/net/sf/openrocket/gui/main/RocketActions.java
@@ -12,6 +12,7 @@ import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.Action;
+import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
@@ -189,6 +190,35 @@ public class RocketActions {
return moveDownAction;
}
+ /**
+ * Tie an action to a JButton, without using the icon or text of the action for the button.
+ *
+ * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the
+ * icon to null and set the button text: this causes a bug where the text of the icon becomes much smaller than is intended.
+ *
+ * @param button button to tie the action to
+ * @param action action to tie to the button
+ * @param text text to display on the button
+ */
+ public static void tieActionToButtonNoIcon(JButton button, Action action, String text) {
+ button.setAction(action);
+ button.setIcon(null);
+ button.setText(text);
+ }
+
+ /**
+ * Tie an action to a JButton, without using the icon of the action for the button.
+ *
+ * For any smartass that wants to know why you don't just initialize the JButton with the action and then set the
+ * icon to null: this causes a bug where the text of the icon becomes much smaller than is intended.
+ *
+ * @param button button to tie the action to
+ * @param action action to tie to the button
+ */
+ public static void tieActionToButtonNoIcon(JButton button, Action action) {
+ button.setAction(action);
+ button.setIcon(null);
+ }
//////// Helper methods for the actions
diff --git a/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java b/swing/src/net/sf/openrocket/gui/main/SimulationPanel.java
index f8ac84ec5..efd353b90 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;
@@ -98,365 +97,81 @@ public class SimulationPanel extends JPanel {
private final JButton plotButton;
private final JPopupMenu pm;
+ private final SimulationAction editSimulationAction;
+ private final SimulationAction runSimulationAction;
+ private final SimulationAction plotSimulationAction;
+ private final SimulationAction duplicateSimulationAction;
+ private final SimulationAction deleteSimulationAction;
+
public SimulationPanel(OpenRocketDocument doc) {
super(new MigLayout("fill", "[grow][][][][][][grow]"));
this.document = doc;
+ // Simulation actions
+ SimulationAction newSimulationAction = new NewSimulationAction();
+ editSimulationAction = new EditSimulationAction();
+ runSimulationAction = new RunSimulationAction();
+ plotSimulationAction = new PlotSimulationAction();
+ duplicateSimulationAction = new DuplicateSimulationAction();
+ deleteSimulationAction = new DeleteSimulationAction();
- //////// The simulation action buttons
+ //////// The simulation action buttons ////////
//// New simulation button
- {
- JButton button = new SelectColorButton(trans.get("simpanel.but.newsimulation"));
- //// Add a new simulation
- button.setToolTipText(trans.get("simpanel.but.ttip.newsimulation"));
- button.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- Simulation sim = new Simulation(document.getRocket());
- sim.setName(document.getNextSimulationName());
-
- int n = document.getSimulationCount();
- document.addSimulation(sim);
- simulationTableModel.fireTableDataChanged();
- simulationTable.clearSelection();
- simulationTable.addRowSelectionInterval(n, n);
-
- openDialog(false, sim);
- }
- });
- this.add(button, "skip 1, gapright para");
- }
+ JButton newButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(newButton, newSimulationAction, trans.get("simpanel.but.newsimulation"));
+ newButton.setToolTipText(trans.get("simpanel.but.ttip.newsimulation"));
+ this.add(newButton, "skip 1, gapright para");
//// Edit simulation button
- editButton = new SelectColorButton(trans.get("simpanel.but.editsimulation"));
- //// Edit the selected simulation
+ editButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(editButton, editSimulationAction, trans.get("simpanel.but.editsimulation"));
editButton.setToolTipText(trans.get("simpanel.but.ttip.editsim"));
- editButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- editSimulation();
- }
- });
this.add(editButton, "gapright para");
//// Run simulations
- runButton = new SelectColorButton(trans.get("simpanel.but.runsimulations"));
- //// Re-run the selected simulations
+ runButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(runButton, runSimulationAction, trans.get("simpanel.but.runsimulations"));
runButton.setToolTipText(trans.get("simpanel.but.ttip.runsimu"));
- runButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- runSimulation();
- }
- });
this.add(runButton, "gapright para");
//// Delete simulations button
- deleteButton = new SelectColorButton(trans.get("simpanel.but.deletesimulations"));
- //// Delete the selected simulations
+ deleteButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(deleteButton, deleteSimulationAction, trans.get("simpanel.but.deletesimulations"));
deleteButton.setToolTipText(trans.get("simpanel.but.ttip.deletesim"));
- deleteButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- deleteSimulation();
- }
- });
this.add(deleteButton, "gapright para");
//// Plot / export button
- plotButton = new SelectColorButton(trans.get("simpanel.but.plotexport"));
- // button = new SelectColorButton("Plot flight");
- plotButton.addActionListener(new ActionListener() {
- @Override
- public void actionPerformed(ActionEvent e) {
- plotSimulation();
- }
- });
+ plotButton = new SelectColorButton();
+ RocketActions.tieActionToButtonNoIcon(plotButton, plotSimulationAction, trans.get("simpanel.but.plotexport"));
this.add(plotButton, "wrap para");
//////// The simulation table
-
- simulationTableModel = new ColumnTableModel(
-
- //// Status and warning column
- new Column("") {
- private JLabel label = null;
-
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- // Initialize the label
- if (label == null) {
- label = new StyledLabel(2f);
- label.setIconTextGap(1);
- // label.setFont(label.getFont().deriveFont(Font.BOLD));
- }
-
- // Set simulation status icon
- Simulation.Status status = document.getSimulation(row).getStatus();
- label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
-
-
- // Set warning marker
- if (status == Simulation.Status.NOT_SIMULATED ||
- status == Simulation.Status.EXTERNAL) {
-
- label.setText("");
-
- } else {
-
- WarningSet w = document.getSimulation(row).getSimulatedWarnings();
- if (w == null) {
- label.setText("");
- } else if (w.isEmpty()) {
- label.setForeground(OK_COLOR);
- label.setText(OK_TEXT);
- } else {
- label.setForeground(WARNING_COLOR);
- label.setText(WARNING_TEXT);
- }
- }
-
- return label;
- }
-
- @Override
- public int getExactWidth() {
- return 36;
- }
-
- @Override
- public Class> getColumnClass() {
- return JLabel.class;
- }
- },
-
- //// Simulation name
- //// Name
- new Column(trans.get("simpanel.col.Name")) {
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
- return document.getSimulation(row).getName();
- }
-
- @Override
- public int getDefaultWidth() {
- return 125;
- }
-
- @Override
- public Comparator getComparator() {
- return new AlphanumComparator();
- }
- },
-
- //// Simulation configuration
- new Column(trans.get("simpanel.col.Configuration")) {
- @Override
- public Object getValueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount()){
- return null;
- }
-
- Rocket rkt = document.getRocket();
- FlightConfigurationId fcid = document.getSimulation(row).getId();
- return descriptor.format( rkt, fcid);
- }
-
- @Override
- public int getDefaultWidth() {
- return 125;
- }
- },
-
- //// Launch rod velocity
- new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getLaunchRodVelocity();
- }
-
- },
-
- //// Apogee
- new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxAltitude();
- }
- },
-
- //// Velocity at deployment
- new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getDeploymentVelocity();
- }
- },
-
- //// Deployment Time from Apogee
- new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
- trans.get("simpanel.col.OptimumCoastTime.ttip"),
- UnitGroup.UNITS_SHORT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null || data.getBranchCount() == 0)
- return null;
-
- double val = data.getBranch(0).getOptimumDelay();
- if ( Double.isNaN(val) ) {
- return null;
- }
- return val;
- }
- },
-
- //// Maximum velocity
- new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxVelocity();
- }
- },
-
- //// Maximum acceleration
- new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getMaxAcceleration();
- }
- },
-
- //// Time to apogee
- new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getTimeToApogee();
- }
- },
-
- //// Flight time
- new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getFlightTime();
- }
- },
-
- //// Ground hit velocity
- new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) {
- @Override
- public Double valueAt(int row) {
- if (row < 0 || row >= document.getSimulationCount())
- return null;
-
- FlightData data = document.getSimulation(row).getSimulatedData();
- if (data == null)
- return null;
-
- return data.getGroundHitVelocity();
- }
- }
-
- ) {
-
- private static final long serialVersionUID = 8686456963492628476L;
-
- @Override
- public int getRowCount() {
- return document.getSimulationCount();
- }
- };
+ simulationTableModel = new SimulationTableModel();
// Override processKeyBinding so that the JTable does not catch
// key bindings used in menu accelerators
simulationTable = new ColumnTable(simulationTableModel) {
-
private static final long serialVersionUID = -5799340181229735630L;
-
- @Override
- protected boolean processKeyBinding(KeyStroke ks,
- KeyEvent e,
- int condition,
- boolean pressed) {
- return false;
- }
};
ColumnTableRowSorter simulationTableSorter = new ColumnTableRowSorter(simulationTableModel);
simulationTable.setRowSorter(simulationTableSorter);
simulationTable.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
simulationTable.setDefaultRenderer(Object.class, new JLabelRenderer());
simulationTableModel.setColumnWidths(simulationTable.getColumnModel());
-
- pm = new JPopupMenu();
- pm.add(new EditSimulationAction());
- pm.add(new DuplicateSimulationAction());
- pm.add(new DeleteSimulationAction());
- pm.addSeparator();
- pm.add(new RunSimulationAction());
- pm.add(new PlotSimulationAction());
+
+ // Context menu
+ pm = new JPopupMenu();
+ pm.add(editSimulationAction);
+ pm.add(duplicateSimulationAction);
+ pm.add(deleteSimulationAction);
+ pm.addSeparator();
+ pm.add(runSimulationAction);
+ pm.add(plotSimulationAction);
// Mouse listener to act on double-clicks
@@ -496,11 +211,14 @@ public class SimulationPanel extends JPanel {
});
simulationTable.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
- private int previousRow = -1;
+ private int previousSelectedRow = -1;
+ private int previousSelectedRowCount = 0;
public void valueChanged(ListSelectionEvent event) {
- if (simulationTable.getSelectedRow() != previousRow) {
+ if ((simulationTable.getSelectedRow() != previousSelectedRow) ||
+ (simulationTable.getSelectedRowCount() != previousSelectedRowCount)) {
updateButtonStates();
- previousRow = simulationTable.getSelectedRow();
+ previousSelectedRow = simulationTable.getSelectedRow();
+ previousSelectedRowCount = simulationTable.getSelectedRowCount();
}
}
});
@@ -532,6 +250,19 @@ public class SimulationPanel extends JPanel {
updateButtonStates();
}
+ private void newSimulation() {
+ Simulation sim = new Simulation(document.getRocket());
+ sim.setName(document.getNextSimulationName());
+
+ int n = document.getSimulationCount();
+ document.addSimulation(sim);
+ simulationTableModel.fireTableDataChanged();
+ simulationTable.clearSelection();
+ simulationTable.addRowSelectionInterval(n, n);
+
+ openDialog(false, sim);
+ }
+
private void plotSimulation() {
int selected = simulationTable.getSelectedRow();
if (selected < 0) {
@@ -690,23 +421,11 @@ public class SimulationPanel extends JPanel {
}
private void updateButtonStates() {
- int[] selection = simulationTable.getSelectedRows();
- if (selection.length == 0) {
- editButton.setEnabled(false);
- runButton.setEnabled(false);
- deleteButton.setEnabled(false);
- plotButton.setEnabled(false);
- } else {
- if (selection.length > 1) {
- plotButton.setEnabled(false);
- } else {
- plotButton.setEnabled(true);
- }
- editButton.setEnabled(true);
- runButton.setEnabled(true);
- deleteButton.setEnabled(true);
- }
-
+ editSimulationAction.updateEnabledState();
+ deleteSimulationAction.updateEnabledState();
+ runSimulationAction.updateEnabledState();
+ plotSimulationAction.updateEnabledState();
+ duplicateSimulationAction.updateEnabledState();
}
/// when the simulation tab is selected this run outdated simulated if appropriate.
@@ -778,7 +497,31 @@ public class SimulationPanel extends JPanel {
}
}
- class EditSimulationAction extends AbstractAction {
+ private abstract static class SimulationAction extends AbstractAction {
+ private static final long serialVersionUID = 1L;
+
+ public abstract void updateEnabledState();
+ }
+
+ class NewSimulationAction extends SimulationAction {
+ public NewSimulationAction() {
+ putValue(NAME, trans.get("simpanel.but.newsimulation"));
+ this.putValue(MNEMONIC_KEY, KeyEvent.VK_N);
+ this.putValue(SMALL_ICON, Icons.FILE_NEW);
+ }
+
+ @Override
+ public void actionPerformed(ActionEvent e) {
+ newSimulation();
+ }
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(true);
+ }
+ }
+
+ class EditSimulationAction extends SimulationAction {
public EditSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.edit"));
this.putValue(MNEMONIC_KEY, KeyEvent.VK_E);
@@ -790,9 +533,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
editSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() == 1);
+ }
}
- class RunSimulationAction extends AbstractAction {
+ class RunSimulationAction extends SimulationAction {
public RunSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.run"));
putValue(SMALL_ICON, Icons.SIM_RUN);
@@ -802,9 +550,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
runSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
}
- class DeleteSimulationAction extends AbstractAction {
+ class DeleteSimulationAction extends SimulationAction {
public DeleteSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.delete"));
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
@@ -816,9 +569,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
deleteSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
}
- class PlotSimulationAction extends AbstractAction {
+ class PlotSimulationAction extends SimulationAction {
public PlotSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.plot"));
putValue(SMALL_ICON, Icons.SIM_PLOT);
@@ -828,9 +586,14 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
plotSimulation();
}
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() == 1);
+ }
}
- class DuplicateSimulationAction extends AbstractAction {
+ class DuplicateSimulationAction extends SimulationAction {
public DuplicateSimulationAction() {
putValue(NAME, trans.get("simpanel.pop.duplicate"));
putValue(MNEMONIC_KEY, KeyEvent.VK_D);
@@ -842,7 +605,12 @@ public class SimulationPanel extends JPanel {
public void actionPerformed(ActionEvent e) {
duplicateSimulation();
}
- }
+
+ @Override
+ public void updateEnabledState() {
+ setEnabled(simulationTable.getSelectedRowCount() > 0);
+ }
+ }
public static class CellTransferable implements Transferable {
@@ -955,6 +723,254 @@ public class SimulationPanel extends JPanel {
return tip;
}
+ }
+ private class SimulationTableModel extends ColumnTableModel {
+ private static final long serialVersionUID = 8686456963492628476L;
+
+ public SimulationTableModel() {
+ super(
+ //// Status and warning column
+ new Column("") {
+ private JLabel label = null;
+
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ // Initialize the label
+ if (label == null) {
+ label = new StyledLabel(2f);
+ label.setIconTextGap(1);
+ // label.setFont(label.getFont().deriveFont(Font.BOLD));
+ }
+
+ // Set simulation status icon
+ Simulation.Status status = document.getSimulation(row).getStatus();
+ label.setIcon(Icons.SIMULATION_STATUS_ICON_MAP.get(status));
+
+
+ // Set warning marker
+ if (status == Simulation.Status.NOT_SIMULATED ||
+ status == Simulation.Status.EXTERNAL) {
+
+ label.setText("");
+
+ } else {
+
+ WarningSet w = document.getSimulation(row).getSimulatedWarnings();
+ if (w == null) {
+ label.setText("");
+ } else if (w.isEmpty()) {
+ label.setForeground(OK_COLOR);
+ label.setText(OK_TEXT);
+ } else {
+ label.setForeground(WARNING_COLOR);
+ label.setText(WARNING_TEXT);
+ }
+ }
+
+ return label;
+ }
+
+ @Override
+ public int getExactWidth() {
+ return 36;
+ }
+
+ @Override
+ public Class> getColumnClass() {
+ return JLabel.class;
+ }
+ },
+
+ //// Simulation name
+ //// Name
+ new Column(trans.get("simpanel.col.Name")) {
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+ return document.getSimulation(row).getName();
+ }
+
+ @Override
+ public int getDefaultWidth() {
+ return 125;
+ }
+
+ @Override
+ public Comparator getComparator() {
+ return new AlphanumComparator();
+ }
+ },
+
+ //// Simulation configuration
+ new Column(trans.get("simpanel.col.Configuration")) {
+ @Override
+ public Object getValueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount()) {
+ return null;
+ }
+
+ Rocket rkt = document.getRocket();
+ FlightConfigurationId fcid = document.getSimulation(row).getId();
+ return descriptor.format(rkt, fcid);
+ }
+
+ @Override
+ public int getDefaultWidth() {
+ return 125;
+ }
+ },
+
+ //// Launch rod velocity
+ new ValueColumn(trans.get("simpanel.col.Velocityoffrod"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getLaunchRodVelocity();
+ }
+
+ },
+
+ //// Apogee
+ new ValueColumn(trans.get("simpanel.col.Apogee"), UnitGroup.UNITS_DISTANCE) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxAltitude();
+ }
+ },
+
+ //// Velocity at deployment
+ new ValueColumn(trans.get("simpanel.col.Velocityatdeploy"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getDeploymentVelocity();
+ }
+ },
+
+ //// Deployment Time from Apogee
+ new ValueColumn(trans.get("simpanel.col.OptimumCoastTime"),
+ trans.get("simpanel.col.OptimumCoastTime.ttip"), UnitGroup.UNITS_SHORT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null || data.getBranchCount() == 0)
+ return null;
+
+ double val = data.getBranch(0).getOptimumDelay();
+ if (Double.isNaN(val)) {
+ return null;
+ }
+ return val;
+ }
+ },
+
+ //// Maximum velocity
+ new ValueColumn(trans.get("simpanel.col.Maxvelocity"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxVelocity();
+ }
+ },
+
+ //// Maximum acceleration
+ new ValueColumn(trans.get("simpanel.col.Maxacceleration"), UnitGroup.UNITS_ACCELERATION) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getMaxAcceleration();
+ }
+ },
+
+ //// Time to apogee
+ new ValueColumn(trans.get("simpanel.col.Timetoapogee"), UnitGroup.UNITS_FLIGHT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getTimeToApogee();
+ }
+ },
+
+ //// Flight time
+ new ValueColumn(trans.get("simpanel.col.Flighttime"), UnitGroup.UNITS_FLIGHT_TIME) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getFlightTime();
+ }
+ },
+
+ //// Ground hit velocity
+ new ValueColumn(trans.get("simpanel.col.Groundhitvelocity"), UnitGroup.UNITS_VELOCITY) {
+ @Override
+ public Double valueAt(int row) {
+ if (row < 0 || row >= document.getSimulationCount())
+ return null;
+
+ FlightData data = document.getSimulation(row).getSimulatedData();
+ if (data == null)
+ return null;
+
+ return data.getGroundHitVelocity();
+ }
+ }
+ );
+ }
+
+ @Override
+ public int getRowCount() {
+ return document.getSimulationCount();
+ }
}
}
diff --git a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
index a02e626f9..a638ba8d0 100644
--- a/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
+++ b/swing/src/net/sf/openrocket/gui/main/componenttree/ComponentTree.java
@@ -6,6 +6,9 @@ import javax.swing.ToolTipManager;
import net.sf.openrocket.document.OpenRocketDocument;
import net.sf.openrocket.gui.components.BasicTree;
+import java.awt.event.KeyEvent;
+import java.awt.event.KeyListener;
+
@SuppressWarnings("serial")
public class ComponentTree extends BasicTree {
@@ -13,7 +16,21 @@ public class ComponentTree extends BasicTree {
public ComponentTree(OpenRocketDocument document) {
super();
this.setModel(new ComponentTreeModel(document.getRocket(), this));
-
+
+ addKeyListener(new KeyListener() {
+ @Override
+ public void keyTyped(KeyEvent e) { }
+
+ @Override
+ public void keyPressed(KeyEvent e) {
+ if ((e.getKeyCode() == KeyEvent.VK_A) && ((e.getModifiersEx() & KeyEvent.META_DOWN_MASK) != 0)) {
+ setSelectionInterval(1, getRowCount()); // Don't select the rocket (row 0)
+ }
+ }
+
+ @Override
+ public void keyReleased(KeyEvent e) { }
+ });
this.setCellRenderer(new ComponentTreeRenderer());
this.setDragEnabled(true);