diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index 5d605f0ce..6019f66f5 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -1153,16 +1153,20 @@ ComponentCfgDlg.MultiComponentEdit.ttip = You are editing the following co ComponentCfgDlg.Modify = Modify ComponentCfgDlg.ModifyComponents = Modify components -!StageConfig -StageConfig.tab.Separation = Separation -StageConfig.tab.Separation.ttip = Stage separation options -StageConfig.separation.lbl.title = Select when this stage separates: -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = seconds -StageConfig.parallel.radius = Radial Distance: -StageConfig.parallel.angle = Angle: -StageConfig.parallel.count = Number of Copies: -StageConfig.parallel.plus = plus +!ComponentAssemblyConfig +ComponentAssemblyConfig.tab.Separation = Separation +ComponentAssemblyConfig.tab.Separation.ttip = Stage separation options +ComponentAssemblyConfig.separation.lbl.title = Select when this stage separates: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = seconds +ComponentAssemblyConfig.parallel.radius = Radial Distance: +ComponentAssemblyConfig.parallel.angle = Angle: +ComponentAssemblyConfig.parallel.count = Number of Copies: +ComponentAssemblyConfig.parallel.plus = plus +ComponentAssemblyConfig.but.splitPods = Split Pods +ComponentAssemblyConfig.but.splitPods.ttip = Split the pod set into separate pod components. +ComponentAssemblyConfig.but.splitBoosters = Split Boosters +ComponentAssemblyConfig.but.splitBoosters.ttip = Split the booster into separate booster components. ! FinSetConfig FinSetCfg.lbl.FinRotation = Fin rotation: diff --git a/core/resources/l10n/messages_ar.properties b/core/resources/l10n/messages_ar.properties index 6f5416662..7bbebd0af 100644 --- a/core/resources/l10n/messages_ar.properties +++ b/core/resources/l10n/messages_ar.properties @@ -989,15 +989,15 @@ ComponentCfgDlg.Modify = تعديل ComponentCfgDlg.ModifyComponents = تعديل المكونات !StageConfig -StageConfig.tab.Separation = فصل -StageConfig.tab.Separation.ttip = خيارات فصل المرحلة -StageConfig.separation.lbl.title = :حدد متى تنفصل هذه المرحلة -StageConfig.separation.lbl.plus = زائد -StageConfig.separation.lbl.seconds = ثواني -StageConfig.parallel.radius = :المسافة الشعاعية -StageConfig.parallel.angle = :الزاوية -StageConfig.parallel.count = :عدد النسخ -StageConfig.parallel.plus = زائد +ComponentAssemblyConfig.tab.Separation = فصل +ComponentAssemblyConfig.tab.Separation.ttip = خيارات فصل المرحلة +ComponentAssemblyConfig.separation.lbl.title = :حدد متى تنفصل هذه المرحلة +ComponentAssemblyConfig.separation.lbl.plus = زائد +ComponentAssemblyConfig.separation.lbl.seconds = ثواني +ComponentAssemblyConfig.parallel.radius = :المسافة الشعاعية +ComponentAssemblyConfig.parallel.angle = :الزاوية +ComponentAssemblyConfig.parallel.count = :عدد النسخ +ComponentAssemblyConfig.parallel.plus = زائد !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = :عدد الزعانف diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index 04106b5b8..99a8b8c79 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -676,11 +676,11 @@ ComponentCfgDlg.configuration = konfigurace ComponentCfgDlg.Modify = Uprav !StageConfig -StageConfig.tab.Separation = Oddelen -StageConfig.tab.Separation.ttip = Vlastnosti oddelen stupne -StageConfig.separation.lbl.title = Oznac kdy se m tento stupn oddelit: -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = sekundy +ComponentAssemblyConfig.tab.Separation = Oddelen +ComponentAssemblyConfig.tab.Separation.ttip = Vlastnosti oddelen stupne +ComponentAssemblyConfig.separation.lbl.title = Oznac kdy se m tento stupn oddelit: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = sekundy !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Pocet stabiliztoru: diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index e888f8944..033a8ff48 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -733,11 +733,11 @@ ComponentCfgDlg.configuration = Konfiguration ComponentCfgDlg.Modify = Verndern !StageConfig -StageConfig.tab.Separation = Stufentrennung -StageConfig.tab.Separation.ttip = Stufentrennungs-Optionen -StageConfig.separation.lbl.title = Auswhlen, wenn diese Stufe getrennt wird: -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = Sekunden +ComponentAssemblyConfig.tab.Separation = Stufentrennung +ComponentAssemblyConfig.tab.Separation.ttip = Stufentrennungs-Optionen +ComponentAssemblyConfig.separation.lbl.title = Auswhlen, wenn diese Stufe getrennt wird: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = Sekunden !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Anzahl der Leitwerke diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 49c52554e..6e01fa2a1 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -1051,12 +1051,12 @@ Stage.Stage = Etapa ! StageAction StageAction.Stage = Etapa -StageConfig.separation.lbl.plus = m\u00e1s -StageConfig.separation.lbl.seconds = segundos -StageConfig.separation.lbl.title = Seleccione el instante de separaci\u00f3n de esta etapa: +ComponentAssemblyConfig.separation.lbl.plus = m\u00e1s +ComponentAssemblyConfig.separation.lbl.seconds = segundos +ComponentAssemblyConfig.separation.lbl.title = Seleccione el instante de separaci\u00f3n de esta etapa: !StageConfig -StageConfig.tab.Separation = Separaci\u00f3n -StageConfig.tab.Separation.ttip = Opciones de separaci\u00f3n de etapa +ComponentAssemblyConfig.tab.Separation = Separaci\u00f3n +ComponentAssemblyConfig.tab.Separation.ttip = Opciones de separaci\u00f3n de etapa StorageOptChooser.checkbox.Compfile = Archivo comprimido StorageOptChooser.lbl.Saveopt = Guardar opciones diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index 65686aca6..1041d5686 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -1044,12 +1044,12 @@ Stage.Stage = Etage ! StageAction StageAction.Stage = Etage -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = secondes -StageConfig.separation.lbl.title = Choisir lorsque cet \u00E9tage se s\u00E9pare: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = secondes +ComponentAssemblyConfig.separation.lbl.title = Choisir lorsque cet \u00E9tage se s\u00E9pare: !StageConfig -StageConfig.tab.Separation = S\u00E9paration -StageConfig.tab.Separation.ttip = Options de s\u00E9paration de l'\u00E9tage +ComponentAssemblyConfig.tab.Separation = S\u00E9paration +ComponentAssemblyConfig.tab.Separation.ttip = Options de s\u00E9paration de l'\u00E9tage StorageOptChooser.checkbox.Compfile = Compresse le fichier StorageOptChooser.lbl.Saveopt = Options de sauvegarde diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index 14fa090db..ff1ddd384 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -734,11 +734,11 @@ ComponentCfgDlg.configuration = (configurazione) ComponentCfgDlg.Modify = Modifica !StageConfig -StageConfig.tab.Separation = Separazione -StageConfig.tab.Separation.ttip = Opzioni della separazione dello stadio -StageConfig.separation.lbl.title = Seleziona quando questo stadio separa: -StageConfig.separation.lbl.plus = pi\u00f9 -StageConfig.separation.lbl.seconds = secondi +ComponentAssemblyConfig.tab.Separation = Separazione +ComponentAssemblyConfig.tab.Separation.ttip = Opzioni della separazione dello stadio +ComponentAssemblyConfig.separation.lbl.title = Seleziona quando questo stadio separa: +ComponentAssemblyConfig.separation.lbl.plus = pi\u00f9 +ComponentAssemblyConfig.separation.lbl.seconds = secondi !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Numero di pinne: diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index bba01aa9b..c2db3200c 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -764,11 +764,11 @@ ComponentCfgDlg.configuration = \u30B3\u30F3\u30D5\u30A3\u30AE\u30E5\u30EC\u30F ComponentCfgDlg.Modify = \u5909\u66F4 !StageConfig -StageConfig.tab.Separation = \u5206\u96E2 -StageConfig.tab.Separation.ttip = \u30B9\u30C6\u30FC\u30B8\u5206\u96E2\u30AA\u30D7\u30B7\u30E7\u30F3 -StageConfig.separation.lbl.title = \u30B9\u30C6\u30FC\u30B8\u304C\u5206\u96E2\u3059\u308B\u6642\u523B\u306E\u9078\u629E\uFF1A -StageConfig.separation.lbl.plus = \u30D7\u30E9\u30B9 -StageConfig.separation.lbl.seconds = \u79D2 +ComponentAssemblyConfig.tab.Separation = \u5206\u96E2 +ComponentAssemblyConfig.tab.Separation.ttip = \u30B9\u30C6\u30FC\u30B8\u5206\u96E2\u30AA\u30D7\u30B7\u30E7\u30F3 +ComponentAssemblyConfig.separation.lbl.title = \u30B9\u30C6\u30FC\u30B8\u304C\u5206\u96E2\u3059\u308B\u6642\u523B\u306E\u9078\u629E\uFF1A +ComponentAssemblyConfig.separation.lbl.plus = \u30D7\u30E9\u30B9 +ComponentAssemblyConfig.separation.lbl.seconds = \u79D2 !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = \u30D5\u30A3\u30F3\u306E\u6570\uFF1A diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index 534b25dce..8b973a025 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -940,14 +940,14 @@ ComponentCfgDlg.configuration = configuratie ComponentCfgDlg.Modify = Wijzigen !StageConfig -StageConfig.tab.Separation = Afscheiding -StageConfig.tab.Separation.ttip = Etape afscheidingsopties -StageConfig.separation.lbl.title = Selecteer wanneer deze etape afscheidt: -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = seconden -StageConfig.parallel.radius = Radiale Afstand -StageConfig.parallel.angle = Hoek -StageConfig.parallel.count = Aantal kopieën +ComponentAssemblyConfig.tab.Separation = Afscheiding +ComponentAssemblyConfig.tab.Separation.ttip = Etape afscheidingsopties +ComponentAssemblyConfig.separation.lbl.title = Selecteer wanneer deze etape afscheidt: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = seconden +ComponentAssemblyConfig.parallel.radius = Radiale Afstand +ComponentAssemblyConfig.parallel.angle = Hoek +ComponentAssemblyConfig.parallel.count = Aantal kopieën StageConfig.parallel.offset = Offset-waarde !EllipticalFinSetConfig diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index f75ac3382..473ce1aa2 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -679,11 +679,11 @@ ComponentInfo.EngineBlock = Blokada silnika unieruchamia silnik wewn\u01 ComponentCfgDlg.Modify = Zmodyfikuj !StageConfig - StageConfig.tab.Separation = Separacja - StageConfig.tab.Separation.ttip = Opcje oddzielenia cz\u0142onu - StageConfig.separation.lbl.title = Ustal moment oddzielenia cz\u0142onu: - StageConfig.separation.lbl.plus = plus - StageConfig.separation.lbl.seconds = sek. + ComponentAssemblyConfig.tab.Separation = Separacja + ComponentAssemblyConfig.tab.Separation.ttip = Opcje oddzielenia cz\u0142onu + ComponentAssemblyConfig.separation.lbl.title = Ustal moment oddzielenia cz\u0142onu: + ComponentAssemblyConfig.separation.lbl.plus = plus + ComponentAssemblyConfig.separation.lbl.seconds = sek. !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Liczba statecznikw: diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties index f4984a407..a0f843f7e 100644 --- a/core/resources/l10n/messages_pt.properties +++ b/core/resources/l10n/messages_pt.properties @@ -1020,12 +1020,12 @@ Stage.Stage = Etapa # StageAction StageAction.Stage = Est\u00e1gio -StageConfig.separation.lbl.plus = mais -StageConfig.separation.lbl.seconds = segundos -StageConfig.separation.lbl.title = Selecione quando este est\u00e1gio separa: +ComponentAssemblyConfig.separation.lbl.plus = mais +ComponentAssemblyConfig.separation.lbl.seconds = segundos +ComponentAssemblyConfig.separation.lbl.title = Selecione quando este est\u00e1gio separa: # StageConfig -StageConfig.tab.Separation = Separa\u00e7\u00e3o -StageConfig.tab.Separation.ttip = Op\u00e7\u00f5es de separa\u00e7\u00e3o de est\u00e1gio +ComponentAssemblyConfig.tab.Separation = Separa\u00e7\u00e3o +ComponentAssemblyConfig.tab.Separation.ttip = Op\u00e7\u00f5es de separa\u00e7\u00e3o de est\u00e1gio StorageOptChooser.checkbox.Compfile = Compactar arquivos StorageOptChooser.lbl.Saveopt = Salvar as Op\u00e7\u00f5es diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index 67c595fa3..50b84632f 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -968,15 +968,15 @@ ComponentCfgDlg.Modify = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C ComponentCfgDlg.ModifyComponents = \u0418\u0437\u043C\u0435\u043D\u0438\u0442\u044C \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u044B !StageConfig -StageConfig.tab.Separation = \u0420\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u0435 -StageConfig.tab.Separation.ttip = \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u044F \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439 -StageConfig.separation.lbl.title = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u043E\u0433\u0434\u0430 \u044D\u0442\u0430 \u0441\u0442\u0443\u043F\u0435\u043D\u044C \u043E\u0442\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F: -StageConfig.separation.lbl.plus = \u043F\u043B\u044E\u0441 -StageConfig.separation.lbl.seconds = \u0441\u0435\u043A\u0443\u043D\u0434 -StageConfig.parallel.radius = \u0420\u0430\u0434\u0438\u0430\u043B\u044C\u043D\u043E\u0435 \u0440\u0430\u0441\u0441\u0442\u043E\u044F\u043D\u0438\u0435: -StageConfig.parallel.angle = \u0423\u0433\u043E\u043B: -StageConfig.parallel.count = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043A\u043E\u043F\u0438\u0439: -StageConfig.parallel.plus = \u043F\u043B\u044E\u0441 +ComponentAssemblyConfig.tab.Separation = \u0420\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u0435 +ComponentAssemblyConfig.tab.Separation.ttip = \u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438 \u0440\u0430\u0437\u0434\u0435\u043B\u0435\u043D\u0438\u044F \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439 +ComponentAssemblyConfig.separation.lbl.title = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435, \u043A\u043E\u0433\u0434\u0430 \u044D\u0442\u0430 \u0441\u0442\u0443\u043F\u0435\u043D\u044C \u043E\u0442\u0434\u0435\u043B\u044F\u0435\u0442\u0441\u044F: +ComponentAssemblyConfig.separation.lbl.plus = \u043F\u043B\u044E\u0441 +ComponentAssemblyConfig.separation.lbl.seconds = \u0441\u0435\u043A\u0443\u043D\u0434 +ComponentAssemblyConfig.parallel.radius = \u0420\u0430\u0434\u0438\u0430\u043B\u044C\u043D\u043E\u0435 \u0440\u0430\u0441\u0441\u0442\u043E\u044F\u043D\u0438\u0435: +ComponentAssemblyConfig.parallel.angle = \u0423\u0433\u043E\u043B: +ComponentAssemblyConfig.parallel.count = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u043A\u043E\u043F\u0438\u0439: +ComponentAssemblyConfig.parallel.plus = \u043F\u043B\u044E\u0441 !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = \u041A\u043E\u043B\u0438\u0447\u0435\u0441\u0442\u0432\u043E \u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0430\u0442\u043E\u0440\u043E\u0432: diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index 654e106ff..003a6dfe3 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -838,11 +838,11 @@ ComponentCfgDlg.configuration = configuration ComponentCfgDlg.Modify = Modify !StageConfig -StageConfig.tab.Separation = Separation -StageConfig.tab.Separation.ttip = Stage separation options -StageConfig.separation.lbl.title = Select when this stage separates: -StageConfig.separation.lbl.plus = plus -StageConfig.separation.lbl.seconds = seconds +ComponentAssemblyConfig.tab.Separation = Separation +ComponentAssemblyConfig.tab.Separation.ttip = Stage separation options +ComponentAssemblyConfig.separation.lbl.title = Select when this stage separates: +ComponentAssemblyConfig.separation.lbl.plus = plus +ComponentAssemblyConfig.separation.lbl.seconds = seconds !EllipticalFinSetConfig EllipticalFinSetCfg.Nbroffins = Number of fins: diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties index e71751995..5171a75d4 100644 --- a/core/resources/l10n/messages_zh_CN.properties +++ b/core/resources/l10n/messages_zh_CN.properties @@ -1120,12 +1120,12 @@ Stage.Stage = \u706B\u7BAD\u7EA7 ! StageAction StageAction.Stage = \u7EA7 -StageConfig.separation.lbl.plus = \u52A0 -StageConfig.separation.lbl.seconds = \u79D2 -StageConfig.separation.lbl.title = \u8BBE\u5B9A\u5206\u79BB\u65F6\u673A: +ComponentAssemblyConfig.separation.lbl.plus = \u52A0 +ComponentAssemblyConfig.separation.lbl.seconds = \u79D2 +ComponentAssemblyConfig.separation.lbl.title = \u8BBE\u5B9A\u5206\u79BB\u65F6\u673A: !StageConfig -StageConfig.tab.Separation = \u5206\u79BB -StageConfig.tab.Separation.ttip = \u591A\u7EA7\u5206\u79BB\u9009\u9879 +ComponentAssemblyConfig.tab.Separation = \u5206\u79BB +ComponentAssemblyConfig.tab.Separation.ttip = \u591A\u7EA7\u5206\u79BB\u9009\u9879 StorageOptChooser.lbl.Saveopt = \u4FDD\u5B58\u9009\u9879 ! StorageOptionChooser diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index 249f8dfeb..672a4990d 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -242,7 +242,9 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC public void clearConfigListeners() { super.clearConfigListeners(); // StageSeparationConfiguration also has config listeners, so clear them as well - StageSeparationConfiguration thisConfig = getSeparationConfiguration(); - thisConfig.clearConfigListeners(); + if (getRoot() instanceof Rocket) { // Root can be different from the rocket if this stage (or its parent) has been removed from the rocket + StageSeparationConfiguration thisConfig = getSeparationConfiguration(); + thisConfig.clearConfigListeners(); + } } } diff --git a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java index 82fb557db..45fbd0b2c 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FinSet.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FinSet.java @@ -1478,61 +1478,18 @@ public abstract class FinSet extends ExternalComponent implements AxialPositiona /** * Split the fin set into individual fins. - * @return A list of the new fin sets. + * + * @return A list of the new fin sets. */ - public List splitFins(boolean freezeRocket) { - final RocketComponent root = getRoot(); - RocketComponent parent = getParent(); - int index = parent.getChildPosition(this); - int count = getFinCount(); - double base = getBaseRotation(); - - List splitFins = null; // List of all the split fins - - try { - // Freeze rocket - if (freezeRocket && root instanceof Rocket) { - ((Rocket) root).freeze(); - } - - // Split the fins - if (count > 1) { - parent.removeChild(index); - splitFins = new ArrayList<>(); - for (int i = 0; i < count; i++) { - FinSet copy = (FinSet) this.copy(); - copy.setFinCount(1); - copy.setBaseRotation(base + i * 2 * Math.PI / count); - copy.setName(copy.getName() + " #" + (i + 1)); - copy.setOverrideMass(getOverrideMass() / getFinCount()); - parent.addChild(copy, index + i); - - splitFins.add(copy); - } - } - - // Split fins for children - for (RocketComponent listener : configListeners) { - if (listener instanceof FinSet) { - ((FinSet) listener).splitFins(false); - this.removeConfigListener(listener); - } - } - } finally { - // Unfreeze rocket - if (freezeRocket && root instanceof Rocket) { - ((Rocket) root).thaw(); - } - } - - return splitFins; + public List splitFins(boolean freezeRocket) { + return splitInstances(freezeRocket); } /** * Split the fin set into individual fins. * @return A list of the new fin sets. */ - public List splitFins() { + public List splitFins() { return splitFins(true); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Instanceable.java b/core/src/net/sf/openrocket/rocketcomponent/Instanceable.java index 47d5c5913..499922bfa 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Instanceable.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Instanceable.java @@ -5,7 +5,7 @@ import net.sf.openrocket.util.Coordinate; public interface Instanceable { @Deprecated - public Coordinate[] getLocations(); + Coordinate[] getLocations(); /** * Returns vector coordinates of each instance of this component relative to this component's parent @@ -16,7 +16,7 @@ public interface Instanceable { * * @return coordinates location of each instance relative to component's parent */ - public Coordinate[] getInstanceLocations(); + Coordinate[] getInstanceLocations(); /** * Returns vector coordinates of each instance of this component relative to this component's reference point (typically front center) @@ -27,20 +27,20 @@ public interface Instanceable { * * @return coordinates location of each instance relative to this component's reference point. */ - public Coordinate[] getInstanceOffsets(); + Coordinate[] getInstanceOffsets(); /** * How many instances of this component are represented. This should generally be editable. * @param newCount number of instances to set */ - public void setInstanceCount( final int newCount ); + void setInstanceCount( final int newCount ); /** * How many instances of this component are represented. This should generally be editable. * * @return number of instances this component currently represent. */ - public int getInstanceCount(); + int getInstanceCount(); /** * Get a human-readable name for this instance arrangement. @@ -48,6 +48,6 @@ public interface Instanceable { * * @return pattern name */ - public String getPatternName(); + String getPatternName(); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 1c308d11c..1824934bc 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -412,7 +412,9 @@ public class Rocket extends ComponentAssembly { * changes. */ public void loadFrom(Rocket source) { - + checkState(); + mutex.lock("loadFrom"); + // Store list of components to invalidate after event has been fired List toInvalidate = this.copyFrom(source); @@ -453,6 +455,8 @@ public class Rocket extends ComponentAssembly { for (RocketComponent c : toInvalidate) { c.invalidate(); } + + mutex.unlock("loadFrom"); } diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index d366a73f4..080a533a2 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -15,6 +15,7 @@ import net.sf.openrocket.aerodynamics.AerodynamicForces; import net.sf.openrocket.aerodynamics.BarrowmanCalculator; import net.sf.openrocket.aerodynamics.FlightConditions; import net.sf.openrocket.logging.WarningSet; +import net.sf.openrocket.rocketcomponent.position.AnglePositionable; import net.sf.openrocket.startup.Application; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.util.ORColor; @@ -1088,6 +1089,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab public int getInstanceCount() { return 1; } + + public void setInstanceCount(int count) { + // Do nothing + log.warn("setInstanceCount called on component that does not support multiple instances"); + } /** * Get the user-defined name of the component. @@ -2382,12 +2388,16 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab */ public final RocketComponent findComponent(String idToFind) { checkState(); + mutex.lock("findComponent"); Iterator iter = this.iterator(true); while (iter.hasNext()) { final RocketComponent c = iter.next(); - if (c.getID().equals(idToFind)) + if (c.getID().equals(idToFind)) { + mutex.unlock("findComponent"); return c; + } } + mutex.unlock("findComponent"); return null; } @@ -2440,6 +2450,67 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab return c; } + /** + * Split the current multi-instance component into multiple single-instance components. + * @param freezeRocket whether to freeze the rocket while splitting + * @return list of all the split components + */ + public List splitInstances(boolean freezeRocket) { + final Rocket rocket = getRocket(); + RocketComponent parent = getParent(); + int index = parent.getChildPosition(this); + int count = getInstanceCount(); + double angleOffset = getAngleOffset(); + + List splitComponents = null; // List of all the split components + + try { + // Freeze rocket + if (freezeRocket) { + rocket.freeze(); + } + + // Split the components + if (count > 1) { + parent.removeChild(index, true); // Remove the original component + splitComponents = new java.util.ArrayList<>(); + for (int i = 0; i < count; i++) { + RocketComponent copy = this.copy(); + copy.setInstanceCount(1); + if (copy instanceof AnglePositionable) { + ((AnglePositionable) copy).setAngleOffset(angleOffset + i * 2 * Math.PI / count); + } + copy.setName(copy.getName() + " #" + (i + 1)); + copy.setOverrideMass(getOverrideMass() / count); + parent.addChild(copy, index + i, true); // Add the new component + + splitComponents.add(copy); + } + } + + // Split components for listeners + for (RocketComponent listener : configListeners) { + if (listener.getClass().isAssignableFrom(this.getClass())) { + listener.splitInstances(false); + this.removeConfigListener(listener); + } + } + } finally { + // Unfreeze rocket + if (freezeRocket) { + rocket.thaw(); + } + } + + fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE); + + return splitComponents; + } + + public List splitInstances() { + return splitInstances(true); + } + /////////// Event handling ////////// // // Listener lists are provided by the root Rocket component, diff --git a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java index 6442c6b4a..171b23b04 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/DoubleModel.java @@ -1000,6 +1000,9 @@ public class DoubleModel implements StateChangeListener, ChangeSource, Invalidat log.warn("Invalidating " + this + " while still having listeners " + listeners); } listeners.clear(); + if (source instanceof ChangeSource) { + ((ChangeSource) source).removeChangeListener(this); + } MemoryManagement.collectable(this); } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/AxialStageConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/AxialStageConfig.java index 8e3cbcb40..0ffbe50e1 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/AxialStageConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/AxialStageConfig.java @@ -31,8 +31,8 @@ public class AxialStageConfig extends ComponentAssemblyConfig { // Stage separation config (for non-first stage) if (component.getStageNumber() > 0) { JPanel tab = separationTab((AxialStage) component); - tabbedPane.insertTab(trans.get("StageConfig.tab.Separation"), null, tab, - trans.get("StageConfig.tab.Separation.ttip"), 0); + tabbedPane.insertTab(trans.get("ComponentAssemblyConfig.tab.Separation"), null, tab, + trans.get("ComponentAssemblyConfig.tab.Separation.ttip"), 0); tabbedPane.setSelectedIndex(0); } @@ -49,7 +49,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig { JPanel panel = new JPanel(new MigLayout()); // Select separation event - panel.add(new StyledLabel(trans.get("StageConfig.separation.lbl.title") + " " + CommonStrings.dagger, Style.BOLD), + panel.add(new StyledLabel(trans.get("ComponentAssemblyConfig.separation.lbl.title") + " " + CommonStrings.dagger, Style.BOLD), "spanx, gaptop unrel, wrap 30lp"); StageSeparationConfiguration sepConfig = stage.getSeparationConfiguration(); @@ -61,7 +61,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig { order.add(combo); // ... and delay - panel.add(new JLabel(trans.get("StageConfig.separation.lbl.plus"))); + panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.plus"))); DoubleModel dm = new DoubleModel( sepConfig, "SeparationDelay", 0); JSpinner spin = new JSpinner(dm.getSpinnerModel()); @@ -70,7 +70,7 @@ public class AxialStageConfig extends ComponentAssemblyConfig { order.add(((SpinnerEditor)spin.getEditor()).getTextField()); //// seconds - panel.add(new JLabel(trans.get("StageConfig.separation.lbl.seconds")), "wrap unrel"); + panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.seconds")), "wrap unrel"); panel.add(new StyledLabel(CommonStrings.override_description, -1), "spanx, pushy, wrap para"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentAssemblyConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentAssemblyConfig.java index 0002f2295..fcc47c85b 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentAssemblyConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentAssemblyConfig.java @@ -1,11 +1,13 @@ package net.sf.openrocket.gui.configdialog; import javax.swing.ComboBoxModel; +import javax.swing.JButton; import javax.swing.JComboBox; import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; +import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; @@ -15,27 +17,35 @@ import net.sf.openrocket.gui.adaptors.EnumModel; import net.sf.openrocket.gui.adaptors.IntegerModel; import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; +import net.sf.openrocket.logging.Markers; import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.ParallelStage; import net.sf.openrocket.rocketcomponent.PodSet; import net.sf.openrocket.rocketcomponent.RocketComponent; -import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.rocketcomponent.position.RadiusMethod; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ItemEvent; import java.awt.event.ItemListener; +import java.util.ArrayList; import java.util.EventObject; +import java.util.List; @SuppressWarnings("serial") public class ComponentAssemblyConfig extends RocketComponentConfig { private static final Translator trans = Application.getTranslator(); + private static final Logger log = LoggerFactory.getLogger(ComponentAssemblyConfig.class); private final RocketComponent component; + + private JButton split = null; public ComponentAssemblyConfig(OpenRocketDocument document, RocketComponent component, JDialog parent) { super(document, component, parent); @@ -46,6 +56,8 @@ public class ComponentAssemblyConfig extends RocketComponentConfig { tabbedPane.insertTab( trans.get("RocketCompCfg.tab.Assembly"), null, parallelTab( (ComponentAssembly)component ), trans.get("RocketCompCfg.tab.AssemblyComment"), 0); tabbedPane.setSelectedIndex(0); + + addSplitButton(); } } @@ -62,7 +74,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig { order.add(radiusMethodCombo); // set radial distance - JLabel radiusLabel = new JLabel(trans.get("StageConfig.parallel.radius")); + JLabel radiusLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.radius")); motherPanel.add( radiusLabel , "align left"); //radiusMethodModel.addEnableComponent(radiusLabel, false); DoubleModel radiusModel = new DoubleModel( boosters, "RadiusOffset", UnitGroup.UNITS_LENGTH, 0); @@ -85,7 +97,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig { }); // set angle - JLabel angleLabel = new JLabel(trans.get("StageConfig.parallel.angle")); + JLabel angleLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.angle")); motherPanel.add( angleLabel, "align left"); DoubleModel angleModel = new DoubleModel( boosters, "AngleOffset", 1.0, UnitGroup.UNITS_ANGLE, -Math.PI, Math.PI); @@ -98,7 +110,7 @@ public class ComponentAssemblyConfig extends RocketComponentConfig { motherPanel.add(new BasicSlider(angleModel.getSliderModel(-Math.PI, Math.PI)), "gapleft para, growx 2, wrap"); // set multiplicity - JLabel countLabel = new JLabel(trans.get("StageConfig.parallel.count")); + JLabel countLabel = new JLabel(trans.get("ComponentAssemblyConfig.parallel.count")); motherPanel.add( countLabel, "align left"); IntegerModel countModel = new IntegerModel( boosters, "InstanceCount", 1); @@ -112,4 +124,64 @@ public class ComponentAssemblyConfig extends RocketComponentConfig { return motherPanel; } + + @Override + public void updateFields() { + super.updateFields(); + if (split != null) { + split.setEnabled(component.getInstanceCount() > 1); + } + } + + private void addSplitButton() { + //// Split fins + final String btnText; + final String btnTextTtip; + final boolean freezeRocket; + if (PodSet.class.isAssignableFrom(component.getClass())) { + btnText = trans.get("ComponentAssemblyConfig.but.splitPods"); + btnTextTtip = trans.get("ComponentAssemblyConfig.but.splitPods.ttip"); + freezeRocket = true; + } else if (ParallelStage.class.isAssignableFrom(component.getClass())) { + btnText = trans.get("ComponentAssemblyConfig.but.splitBoosters"); + btnTextTtip = trans.get("ComponentAssemblyConfig.but.splitBoosters.ttip"); + freezeRocket = false; + } else { + return; + } + split = new SelectColorButton(btnText); + split.setToolTipText(btnTextTtip); + split.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + log.info(Markers.USER_MARKER, "Splitting " + component.getComponentName() + " into separate assemblies, instance count=" + + component.getInstanceCount()); + + // This is a bit awkward, we need to store the listeners before closing the dialog, because closing it + // will remove them. We then add them back before the split and remove them afterwards. + List listeners = new ArrayList<>(component.getConfigListeners()); + + + // Do change in future for overall safety + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + disposeDialog(); + + document.startUndo("Split assembly"); + for (RocketComponent listener : listeners) { + component.addConfigListener(listener); + } + component.splitInstances(freezeRocket); + component.clearConfigListeners(); + document.stopUndo(); + } + }); + } + }); + split.setEnabled(component.getInstanceCount() > 1); + + addButtons(split); + order.add(split); + } } diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index b11871fb0..0c307eec1 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -339,6 +339,7 @@ public class RocketComponentConfig extends JPanel { } protected void disposeDialog() { + invalidate(); if (parent != null) { if (parent instanceof ComponentConfigDialog) { ComponentConfigDialog.disposeDialog(); @@ -700,6 +701,7 @@ public class RocketComponentConfig extends JPanel { panel.add(checkboxes, "growx 1, gapright 20lp"); m = new DoubleModel(component, "OverrideCD", UnitGroup.UNITS_COEFFICIENT, Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY); + register(m); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java index bd90c2617..55eaf5267 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/flightconfiguration/SeparationSelectionDialog.java @@ -82,7 +82,7 @@ public class SeparationSelectionDialog extends JDialog { panel.add(event, "wrap rel"); // ... and delay - panel.add(new JLabel(trans.get("StageConfig.separation.lbl.plus")), "alignx 100%"); + panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.plus")), "alignx 100%"); final DoubleModel delay = new DoubleModel(newConfiguration, "SeparationDelay", UnitGroup.UNITS_SHORT_TIME, 0); JSpinner spin = new JSpinner(delay.getSpinnerModel()); @@ -90,7 +90,7 @@ public class SeparationSelectionDialog extends JDialog { panel.add(spin, "span, split"); //// seconds - panel.add(new JLabel(trans.get("StageConfig.separation.lbl.seconds")), "wrap para"); + panel.add(new JLabel(trans.get("ComponentAssemblyConfig.separation.lbl.seconds")), "wrap para"); panel.add(new JPanel(), "span, split, growx");