diff --git a/core/.classpath b/core/.classpath index acc55d845..453ab552f 100644 --- a/core/.classpath +++ b/core/.classpath @@ -15,6 +15,8 @@ <classpathentry kind="lib" path="lib-extra/RXTXcomm.jar"/> <classpathentry kind="lib" path="resources"/> <classpathentry kind="lib" path="lib/javax.inject.jar"/> + <classpathentry kind="lib" path="lib/javax.json-1.1.3.jar"/> + <classpathentry kind="lib" path="lib/javax.json-api-1.1.3.jar"/> <classpathentry kind="lib" path="lib/aopalliance.jar"/> <classpathentry kind="lib" path="lib/commonmark-0.19.0.jar"/> <classpathentry kind="lib" path="lib/slf4j-api-1.7.30.jar"/> diff --git a/core/resources/l10n/messages.properties b/core/resources/l10n/messages.properties index aa9ae51b7..e70342af9 100644 --- a/core/resources/l10n/messages.properties +++ b/core/resources/l10n/messages.properties @@ -294,6 +294,8 @@ pref.dlg.lbl.PositiontoinsertStages = Position to insert new stages: pref.dlg.lbl.Confirmdeletion = Confirm deletion of simulations. pref.dlg.checkbox.Runsimulations = Run out-dated simulations when you open the simulation tab. pref.dlg.checkbox.Updateestimates = Update estimated flight parameters in design window +pref.dlg.checkbox.Markers = Only show pod set/booster markers when the pod set/booster is selected +pref.dlg.checkbox.Markers.ttip = <html>If checked, pod set/booster markers will only be shown when the pod set/booster is selected.<br>If unchecked, pod set/booster markers will always be shown.</html> pref.dlg.checkbox.AlwaysOpenLeftmost = Always open leftmost tab when opening a component edit dialog pref.dlg.checkbox.AlwaysOpenLeftmost.ttip = <html>If checked, a component edit dialog will always pop up with the first tab selected.<br>If unchecked, the previous selected tab will be used.</html> pref.dlg.lbl.User-definedthrust = User-defined thrust curves: @@ -715,7 +717,7 @@ compaddbuttons.Launchlug = Launch Lug compaddbuttons.RailButton = Rail Button compaddbuttons.InnerComponent = Inner Components compaddbuttons.Innertube = Inner Tube -compaddbuttons.Coupler = Coupler +compaddbuttons.Coupler = Tube Coupler compaddbuttons.Centeringring = Centering\nRing compaddbuttons.Bulkhead = Bulkhead compaddbuttons.Engineblock = Engine\nBlock @@ -887,16 +889,17 @@ RocketCfg.lbl.Material = Material: ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Component name: -RocketCompCfg.ttip.Thecomponentname = The component name. +RocketCompCfg.lbl.Componentname.ttip = The component name. RocketCompCfg.tab.Override = Override -RocketCompCfg.tab.MassandCGoverride = Mass and CG override options +RocketCompCfg.tab.Override.ttip = Mass and CG override options RocketCompCfg.tab.Assembly = General RocketCompCfg.tab.AssemblyComment = Options for locating stages parallel to other stages RocketCompCfg.tab.Figure = Figure RocketCompCfg.tab.Figstyleopt = Figure style options RocketCompCfg.tab.Comment = Comment -RocketCompCfg.tab.Specifyacomment = Specify a comment for the component -RocketCompCfg.tab.Appearance = Appearance +RocketCompCfg.tab.Comment.ttip = Specify a comment for the component +RocketCompCfg.tab.Appearance = Appearance +RocketCompCfg.tab.Appearance.ttip = Component's appearance RocketCompCfg.lbl.Mass = Mass: RocketCompCfg.lbl.Componentmass = Component mass: RocketCompCfg.lbl.overriddento = (overridden to @@ -2071,8 +2074,10 @@ CustomFinImport.error.badimage = Could not deduce fin shape from image. CustomFinImport.description = The image will be converted internally to black and white image (black for the fin), so make sure you use a solid dark color for the fin, and white or a light color for the background. The fin must be touching the bottom of the image, which is the base of the fin. -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database\u2026 +PresetModel.combo.ttip = <html>Select a preset model from a list of favorites (selected in the component preset dialog),<br>or select 'Custom' when no preset is required.</html> +PresetModel.lbl.custompreset = Custom +PresetModel.lbl.partsLib = Parts Library +PresetModel.lbl.partsLib.ttip = Select a preset model for this rocket component from a library of parts. DecalModel.lbl.select = <none> DecalModel.lbl.choose = From file\u2026 @@ -2094,7 +2099,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 = Select to add preset to drop-down menu +ComponentPresetChooserDialog.lbl.favorites = Check to add preset to the preset drop-down menu in the component edit dialog table.column.Favorite = Favorite table.column.Legacy = Legacy table.column.Manufacturer = Manufacturer diff --git a/core/resources/l10n/messages_cs.properties b/core/resources/l10n/messages_cs.properties index f5b5933f7..a30ef6392 100644 --- a/core/resources/l10n/messages_cs.properties +++ b/core/resources/l10n/messages_cs.properties @@ -621,13 +621,13 @@ ShockCordCfg.lbl.Shockcordlength = D ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Jm�no komponenty: -RocketCompCfg.ttip.Thecomponentname = Jm�no komponenty. +RocketCompCfg.lbl.Componentname.ttip = Jm�no komponenty. RocketCompCfg.tab.Override = Prepsat -RocketCompCfg.tab.MassandCGoverride = Prepi\u0161 hmotnost a te\u017Ei\u0161te +RocketCompCfg.tab.Override.ttip = Prepi\u0161 hmotnost a te\u017Ei\u0161te RocketCompCfg.tab.Figure = Obr�zek RocketCompCfg.tab.Figstyleopt = Vlastnosti obr�zku RocketCompCfg.tab.Comment = Koment�r -RocketCompCfg.tab.Specifyacomment = Upresnen� koment�re komponenty +RocketCompCfg.tab.Comment.ttip = Upresnen� koment�re komponenty RocketCompCfg.lbl.Mass = Hmotnost: RocketCompCfg.lbl.Componentmass = Hmotnost komponenty: RocketCompCfg.lbl.overriddento = (preps�no na @@ -1587,8 +1587,8 @@ CustomFinImport.error.badimage = Nemohu vyvodit tvar stabiliz CustomFinImport.description = Obr�zek bude zmenen na cernob�l� \n(cern� pro stabiliz�tor), ujistete se pros�m, \u017Ee jste pou\u017Eily cernou barvu na stabiliz�tor \na b�lou nebo svetlou barvu na pozad�. Stabiliz�tor \nse mus� dot�kat steny obr�zku, kter� predstavuje uchycen� pro stabiliz�tor. -PresetModel.lbl.select = V�ber predvolby: -PresetModel.lbl.database = Z datab�ze... +PresetModel.lbl.custompreset = Vlastn� +PresetModel.lbl.partsLib = Knihovna d�l? ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_de.properties b/core/resources/l10n/messages_de.properties index 45f91eda5..5a1d9a7ec 100644 --- a/core/resources/l10n/messages_de.properties +++ b/core/resources/l10n/messages_de.properties @@ -677,13 +677,13 @@ ShockCordCfg.lbl.Shockcordlength = Gummibandl ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Komponentenname: -RocketCompCfg.ttip.Thecomponentname = Name der Komponente. +RocketCompCfg.lbl.Componentname.ttip = Name der Komponente. RocketCompCfg.tab.Override = Werte �berschreiben -RocketCompCfg.tab.MassandCGoverride = Massen- und Schwerpunktsoptionen +RocketCompCfg.tab.Override.ttip = Massen- und Schwerpunktsoptionen RocketCompCfg.tab.Figure = Form RocketCompCfg.tab.Figstyleopt = Formoptionen RocketCompCfg.tab.Comment = Kommentar -RocketCompCfg.tab.Specifyacomment = Kommentar zu dieser Komponente +RocketCompCfg.tab.Comment.ttip = Kommentar zu dieser Komponente RocketCompCfg.lbl.Mass = Masse: RocketCompCfg.lbl.Componentmass = Masse der Komponente: RocketCompCfg.lbl.overriddento = (�berschrieben auf @@ -1645,8 +1645,8 @@ CustomFinImport.error.badimage = Konnte keine Leitwerksform aus dem Bild erzeuge CustomFinImport.description = Das Bild wird intern in ein Schwarz-Wei�-Bild konvertiert (Leitwerk: schwarz). Bitte sicherstellen, dass das Leitwerk in einer dichten, dunklen Farbe ist, w�hrend der Hintergrund wei� oder sehr hell sein sollte. Das Leitwerk muss das untere Bildende ber�hren, da dies die Verbindungsstelle zur Rakete wird. -PresetModel.lbl.select = Voreinstellung ausw�hlen: -PresetModel.lbl.database = Aus Datenbank... +PresetModel.lbl.custompreset = Benutzerdefiniert +PresetModel.lbl.partsLib = Teile-Bibliothek ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_es.properties b/core/resources/l10n/messages_es.properties index 255e26369..556fd9ede 100644 --- a/core/resources/l10n/messages_es.properties +++ b/core/resources/l10n/messages_es.properties @@ -737,8 +737,8 @@ PreferencesDialog.languages.default = Idioma por defecto PreferencesDialog.lbl.language = Idioma de la interfaz: PreferencesDialog.lbl.languageEffect = El idioma cambiar\u00e1 la pr\u00f3xima vez que abra OpenRocket. -PresetModel.lbl.database = Desde la Base de Datos... -PresetModel.lbl.select = Prefabricado +PresetModel.lbl.custompreset = Personalizado +PresetModel.lbl.partsLib = Biblioteca de piezas PrintDialog.but.previewAndPrint = Vista previa e Imprimir PrintDialog.checkbox.showByStage = Mostrar por etapas @@ -861,13 +861,13 @@ RocketCompCfg.tab.Appearance = Apariencia RocketCompCfg.tab.Comment = Comentarios RocketCompCfg.tab.Figstyleopt = Opciones de estilo de la figura RocketCompCfg.tab.Figure = Estilo -RocketCompCfg.tab.MassandCGoverride = Especificar la Masa y el CG del componente. +RocketCompCfg.tab.Override.ttip = Especificar la Masa y el CG del componente. RocketCompCfg.tab.Override = Masa y CG -RocketCompCfg.tab.Specifyacomment = Especifique un comentario para el componente +RocketCompCfg.tab.Comment.ttip = Especifique un comentario para el componente RocketCompCfg.title.Aftshoulder = Trasera del acople RocketCompCfg.title.Noseconeshoulder = Acople de la ojiva RocketCompCfg.ttip.Endcapped = Si el extremo del soporte est\u00e1 truncado. -RocketCompCfg.ttip.Thecomponentname = El nombre del componente. +RocketCompCfg.lbl.Componentname.ttip = El nombre del componente. RocketComponent.Position.ABSOLUTE = Extremo de la ojiva RocketComponent.Position.AFTER = Despu\u00e9s del componente diff --git a/core/resources/l10n/messages_fr.properties b/core/resources/l10n/messages_fr.properties index d4cee80d0..350bc5874 100644 --- a/core/resources/l10n/messages_fr.properties +++ b/core/resources/l10n/messages_fr.properties @@ -729,8 +729,8 @@ PreferencesDialog.languages.default = Valeur syst\u00E8me par d\u00E9faut PreferencesDialog.lbl.language = Langue du programme: PreferencesDialog.lbl.languageEffect = La langue sera chang\u00E9e apr\u00E8s avoir red\u00E9marr\u00E9 OpenRocket. -PresetModel.lbl.database = A partir d'une base de donn\u00E9es... -PresetModel.lbl.select = Choisir une pi\u00E8ce pr\u00E9d\u00E9finie: +PresetModel.lbl.custompreset = Personnalis� +PresetModel.lbl.partsLib = Biblioth\u00E8que de pi\u00E8ces PrintDialog.but.previewAndPrint = Pr\u00E9-visualiser et imprimer PrintDialog.checkbox.showByStage = Montrer par \u00E9tage @@ -853,13 +853,13 @@ RocketCompCfg.tab.Appearance = Apparence RocketCompCfg.tab.Comment = Commentaires RocketCompCfg.tab.Figstyleopt = Options de la forme RocketCompCfg.tab.Figure = Forme -RocketCompCfg.tab.MassandCGoverride = For\u00E7age de la Masse et du CG +RocketCompCfg.tab.Override.ttip = For\u00E7age de la Masse et du CG RocketCompCfg.tab.Override = Forcer la valeur -RocketCompCfg.tab.Specifyacomment = Commentaires concernant la pi\u00E8ce +RocketCompCfg.tab.Comment.ttip = Commentaires concernant la pi\u00E8ce RocketCompCfg.title.Aftshoulder = Epaulement arri\u00E8re RocketCompCfg.title.Noseconeshoulder = Epaulement du c\u00F4ne RocketCompCfg.ttip.Endcapped = Pr\u00E9cise si l'arri\u00E8re du c\u00F4ne est clos. -RocketCompCfg.ttip.Thecomponentname = Le nom de la pi\u00E8ce. +RocketCompCfg.lbl.Componentname.ttip = Le nom de la pi\u00E8ce. RocketComponent.Position.ABSOLUTE = Pointe de l'ogive RocketComponent.Position.AFTER = Apr\u00E8s la pi\u00E8ce parente diff --git a/core/resources/l10n/messages_it.properties b/core/resources/l10n/messages_it.properties index f99e43eb3..0596e8fc5 100644 --- a/core/resources/l10n/messages_it.properties +++ b/core/resources/l10n/messages_it.properties @@ -679,13 +679,13 @@ ShockCordCfg.lbl.Shockcordlength = Lunghezza della Shock cord : ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Nome componente: -RocketCompCfg.ttip.Thecomponentname = Il nome del componente. +RocketCompCfg.lbl.Componentname.ttip = Il nome del componente. RocketCompCfg.tab.Override = Modifica -RocketCompCfg.tab.MassandCGoverride = Opzioni di sovrascrittura di massa e CG +RocketCompCfg.tab.Override.ttip = Opzioni di sovrascrittura di massa e CG RocketCompCfg.tab.Figure = Disegno RocketCompCfg.tab.Figstyleopt = Opzioni dello stile della figure RocketCompCfg.tab.Comment = Commento -RocketCompCfg.tab.Specifyacomment = Specifica un commento per il componente +RocketCompCfg.tab.Comment.ttip = Specifica un commento per il componente RocketCompCfg.lbl.Mass = Massa: RocketCompCfg.lbl.Componentmass = La massa del componente : RocketCompCfg.lbl.overriddento = (sovrascritto a @@ -1651,8 +1651,8 @@ CustomFinImport.error.badimage = Non riesco a capire la forma della pinna dall CustomFinImport.description = L'immagine sar\u00e0 convertita in bianco e nero internamente (nero per le pinne), cos\u00ec assicurati di usare un nero pieno per le pinne e bianco, o colore chiaro, per lo sfondo. La pinna ndeve toccare il fondo dell'immagine, che \u00e8 la base della pinna. -PresetModel.lbl.select = Seleziona precaricati: -PresetModel.lbl.database = Da database... +PresetModel.lbl.custompreset = Personalizzato +PresetModel.lbl.partsLib = Libreria di parti ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_ja.properties b/core/resources/l10n/messages_ja.properties index 2417dd0c0..3daf4089e 100644 --- a/core/resources/l10n/messages_ja.properties +++ b/core/resources/l10n/messages_ja.properties @@ -709,13 +709,13 @@ ShockCordCfg.lbl.Shockcordlength = \u30B7\u30E7\u30C3\u30AF\u30B3\u30FC\u30C9\u ! RocketComponentConfig RocketCompCfg.lbl.Componentname = \u90E8\u54C1\u540D\uFF1A -RocketCompCfg.ttip.Thecomponentname = \u90E8\u54C1\u306E\u540D\u524D +RocketCompCfg.lbl.Componentname.ttip = \u90E8\u54C1\u306E\u540D\u524D RocketCompCfg.tab.Override = \u518D\u5B9A\u7FA9 -RocketCompCfg.tab.MassandCGoverride = \u8CEA\u91CF\u3068CG\u3092\u518D\u5B9A\u7FA9\u3059\u308B\u30AA\u30D7\u30B7\u30E7\u30F3 +RocketCompCfg.tab.Override.ttip = \u8CEA\u91CF\u3068CG\u3092\u518D\u5B9A\u7FA9\u3059\u308B\u30AA\u30D7\u30B7\u30E7\u30F3 RocketCompCfg.tab.Figure = \u56F3\u793A RocketCompCfg.tab.Figstyleopt = \u56F3\u793A\u306E\u30B9\u30BF\u30A4\u30EB\u30AA\u30D7\u30B7\u30E7\u30F3 RocketCompCfg.tab.Comment = \u30B3\u30E1\u30F3\u30C8 -RocketCompCfg.tab.Specifyacomment = \u90E8\u54C1\u3078\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u8A18\u8FF0 +RocketCompCfg.tab.Comment.ttip = \u90E8\u54C1\u3078\u306E\u30B3\u30E1\u30F3\u30C8\u3092\u8A18\u8FF0 RocketCompCfg.lbl.Mass = \u8CEA\u91CF\uFF1A RocketCompCfg.lbl.Componentmass = \u90E8\u54C1\u8CEA\u91CF\uFF1A RocketCompCfg.lbl.overriddento = (overridden to @@ -1713,8 +1713,8 @@ CustomFinImport.error.badimage = \u753B\u50CF\u304B\u3089\u30D5\u30A3\u30F3\u5F CustomFinImport.description = \u753B\u50CF\u306F\u5185\u90E8\u3067\u767D\u80CC\u666F\u3068\u9ED2\u7DDA\u306B\u5909\u63DB\u3055\u308C\u307E\u3059\u3002\u306A\u306E\u3067\u30D5\u30A3\u30F3\u306B\u306F\u6697\u3044\u8272\u306E\u5B9F\u7DDA\u3001\u80CC\u666F\u306B\u306F\u767D\u304B\u660E\u308B\u3044\u8272\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u30D5\u30A3\u30F3\u306F\u753B\u50CF\u306E\u5E95\u9762\u306B\u63A5\u3057\u3066\u3044\u306A\u304F\u3066\u306F\u3044\u3051\u307E\u305B\u3093\u3001\u3053\u308C\u306F\u30D5\u30A3\u30F3\u306E\u5E95\u9762\u306B\u306A\u308A\u307E\u3059\u3002 -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database... +PresetModel.lbl.custompreset = \u30ab\u30b9\u30bf\u30e0 +PresetModel.lbl.partsLib = \u30d1\u30fc\u30c4\u30e9\u30a4\u30d6\u30e9\u30ea\u30fc ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_nl.properties b/core/resources/l10n/messages_nl.properties index b32a8d0a5..08c3faeed 100644 --- a/core/resources/l10n/messages_nl.properties +++ b/core/resources/l10n/messages_nl.properties @@ -826,15 +826,15 @@ RocketCfg.lbl.Material = Materiaal: ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Componentnaam: -RocketCompCfg.ttip.Thecomponentname = De componentnaam. +RocketCompCfg.lbl.Componentname.ttip = De componentnaam. RocketCompCfg.tab.Override = Overschrijf -RocketCompCfg.tab.MassandCGoverride = Massa en ZP overschrijvingsopties +RocketCompCfg.tab.Override.ttip = Massa en ZP overschrijvingsopties RocketCompCfg.tab.Assembly = Algemeen RocketCompCfg.tab.AssemblyComment = Opties voor het plaatsen van trappen parallel aan andere trappen RocketCompCfg.tab.Figure = Figuur RocketCompCfg.tab.Figstyleopt = Figuurstijl opties RocketCompCfg.tab.Comment = Opmerking -RocketCompCfg.tab.Specifyacomment = Geef een opmerking voor het onderdeel +RocketCompCfg.tab.Comment.ttip = Geef een opmerking voor het onderdeel RocketCompCfg.tab.Appearance = Uiterlijk RocketCompCfg.lbl.Mass = Massa: RocketCompCfg.lbl.Componentmass = Componentmassa: @@ -1967,8 +1967,8 @@ CustomFinImport.error.badimage = Kon de vorm van de vin niet afleiden uit het be CustomFinImport.description = De afbeelding wordt intern geconverteerd naar een zwart-wit afbeelding (zwart voor de vin), dus zorg ervoor dat je een effen donkere kleur gebruikt voor de vin, en wit of een lichte kleur voor de achtergrond. De vin moet de onderkant van het beeld raken, dat is de basis van de vin. -PresetModel.lbl.select = Selecteer preset -PresetModel.lbl.database = Uit databank... +PresetModel.lbl.custompreset = Aangepast +PresetModel.lbl.partsLib = Onderdelenbibliotheek DecalModel.lbl.select = <geen> DecalModel.lbl.choose = Van bestand... diff --git a/core/resources/l10n/messages_pl.properties b/core/resources/l10n/messages_pl.properties index 08bcaec77..4c1cefc01 100644 --- a/core/resources/l10n/messages_pl.properties +++ b/core/resources/l10n/messages_pl.properties @@ -623,13 +623,13 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Nazwa cz\u0119\u015Bci: - RocketCompCfg.ttip.Thecomponentname = Nazwa cz\u0119\u015Bci sk\u0142adowej rakiety. + RocketCompCfg.lbl.Componentname.ttip = Nazwa cz\u0119\u015Bci sk\u0142adowej rakiety. RocketCompCfg.tab.Override = Wymu\u015B - RocketCompCfg.tab.MassandCGoverride = Opcje wymuszenia ci\u0119\u017Caru oraz \u015Brodka ci\u0119\u017Cko\u015Bci + RocketCompCfg.tab.Override.ttip = Opcje wymuszenia ci\u0119\u017Caru oraz \u015Brodka ci\u0119\u017Cko\u015Bci RocketCompCfg.tab.Figure = Wygl\u0105d RocketCompCfg.tab.Figstyleopt = Styl kszta\u0142tu RocketCompCfg.tab.Comment = Uwagi - RocketCompCfg.tab.Specifyacomment = Dodaj uwagi do cz\u0119\u015Bci + RocketCompCfg.tab.Comment.ttip = Dodaj uwagi do cz\u0119\u015Bci RocketCompCfg.lbl.Mass = Masa: RocketCompCfg.lbl.Componentmass = Masa cz\u0119\u015Bci: RocketCompCfg.lbl.overriddento = (wymuszone do @@ -1592,8 +1592,8 @@ update.dlg.latestVersion = Korzystasz z najnowszej wersji OpenRocket: %s. CustomFinImport.description = Obraz zostanie automatycznie zamieniony na czarno-bia\u0142y (statecznik w kolorze czarnym), wi\u0119c upewnij si\u0119, \u017Ce kszta\u0142t statecznika jest wype\u0142niony ciemnym kolorem, a t\u0142o jest bia\u0142e lub jasne. Podstawa statecznika musi przylega\u0107 do dolnej kraw\u0119dzi obrazu. - PresetModel.lbl.select = Wybierz ustawienia: - PresetModel.lbl.database = Z bazy danych... + PresetModel.lbl.custompreset = Niestandardowe + PresetModel.lbl.partsLib = Biblioteka cz??ci ! Component Preset Chooser Dialog diff --git a/core/resources/l10n/messages_pt.properties b/core/resources/l10n/messages_pt.properties index c40699812..18ce530f8 100644 --- a/core/resources/l10n/messages_pt.properties +++ b/core/resources/l10n/messages_pt.properties @@ -715,8 +715,8 @@ PreferencesDialog.languages.default = Padr\u00e3o do sistema PreferencesDialog.lbl.language = Idioma da interface: PreferencesDialog.lbl.languageEffect = A linguagem vai mudar na pr\u00f3xima vez que voc\u00ea iniciar o OpenRocket. -PresetModel.lbl.database = \u00c0 partir do banco de dados... -PresetModel.lbl.select = Selecione ajustes pr\u00e9-definidos +PresetModel.lbl.custompreset = Personalizado +PresetModel.lbl.partsLib = Biblioteca de pe\u00e7as PrintDialog.but.previewAndPrint = Visualiza\u00e7\u00e3o e impress\u00e3o PrintDialog.checkbox.showByStage = Mostrar por est\u00e1gio @@ -838,13 +838,13 @@ RocketCompCfg.lbl.ttip.componentmaterialaffects = O material do componente afe RocketCompCfg.tab.Comment = Coment\u00e1rio RocketCompCfg.tab.Figstyleopt = Op\u00e7\u00f5es do estilo de figura RocketCompCfg.tab.Figure = Figura -RocketCompCfg.tab.MassandCGoverride = Op\u00e7\u00f5es de modifica\u00e7\u00e3o de massa e CG +RocketCompCfg.tab.Override.ttip = Op\u00e7\u00f5es de modifica\u00e7\u00e3o de massa e CG RocketCompCfg.tab.Override = Modificar -RocketCompCfg.tab.Specifyacomment = Especifique um coment\u00e1rio para o componente +RocketCompCfg.tab.Comment.ttip = Especifique um coment\u00e1rio para o componente RocketCompCfg.title.Aftshoulder = Ressalto traseiro RocketCompCfg.title.Noseconeshoulder = Ressalto da ogiva RocketCompCfg.ttip.Endcapped = Quando a extremidade do ressalto \u00e9 limitada. -RocketCompCfg.ttip.Thecomponentname = Nome do componente. +RocketCompCfg.lbl.Componentname.ttip = Nome do componente. RocketComponent.Position.ABSOLUTE = Dica da ogiva RocketComponent.Position.AFTER = Depois do componente pai diff --git a/core/resources/l10n/messages_ru.properties b/core/resources/l10n/messages_ru.properties index f7eeb8792..2ae02679c 100644 --- a/core/resources/l10n/messages_ru.properties +++ b/core/resources/l10n/messages_ru.properties @@ -888,15 +888,15 @@ RocketCfg.lbl.Material = \u041C\u0430\u0442\u0435\u0440\u0438\u0430\u043B: ! RocketComponentConfig RocketCompCfg.lbl.Componentname = \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430: -RocketCompCfg.ttip.Thecomponentname = \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430. +RocketCompCfg.lbl.Componentname.ttip = \u041D\u0430\u0437\u0432\u0430\u043D\u0438\u0435 \u043A\u043E\u043C\u043F\u043E\u043D\u0435\u043D\u0442\u0430. RocketCompCfg.tab.Override = \u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 -RocketCompCfg.tab.MassandCGoverride = \u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 \u043C\u0430\u0441\u0441\u044B \u0438 \u0426\u0422 +RocketCompCfg.tab.Override.ttip = \u041F\u0435\u0440\u0435\u043E\u043F\u0440\u0435\u0434\u0435\u043B\u0435\u043D\u0438\u0435 \u043C\u0430\u0441\u0441\u044B \u0438 \u0426\u0422 RocketCompCfg.tab.Assembly = \u0421\u0431\u043E\u0440\u043A\u0430 RocketCompCfg.tab.AssemblyComment = \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0440\u0430\u0437\u043C\u0435\u0449\u0435\u043D\u0438\u044F \u0441\u0442\u0443\u043F\u0435\u043D\u0435\u0439 \u043F\u0430\u0440\u0430\u043B\u043B\u0435\u043B\u044C\u043D\u043E \u0434\u0440\u0443\u0433\u0438\u043C \u0441\u0442\u0443\u043F\u0435\u043D\u044F\u043C RocketCompCfg.tab.Figure = \u0420\u0438\u0441\u0443\u043D\u043E\u043A RocketCompCfg.tab.Figstyleopt = \u041F\u0430\u0440\u0430\u043C\u0435\u0442\u0440\u044B \u0441\u0442\u0438\u043B\u044F \u0440\u0438\u0441\u0443\u043D\u043A\u0430 RocketCompCfg.tab.Comment = \u041F\u0440\u0438\u043C\u0435\u0447\u0430\u043D\u0438\u0435 -RocketCompCfg.tab.Specifyacomment = \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.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.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: @@ -2075,8 +2075,8 @@ 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.select = \u0412\u044B\u0431\u0435\u0440\u0438\u0442\u0435 \u0437\u0430\u0433\u043E\u0442\u043E\u0432\u043A\u0443 -PresetModel.lbl.database = \u0418\u0437 \u0431\u0430\u0437\u044B \u0434\u0430\u043D\u043D\u044B\u0445... +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 DecalModel.lbl.select = <\u043D\u0435\u0442> DecalModel.lbl.choose = \u0418\u0437 \u0444\u0430\u0439\u043B\u0430... diff --git a/core/resources/l10n/messages_uk_UA.properties b/core/resources/l10n/messages_uk_UA.properties index 5d7147134..a7fc899ed 100644 --- a/core/resources/l10n/messages_uk_UA.properties +++ b/core/resources/l10n/messages_uk_UA.properties @@ -779,13 +779,13 @@ ShockCordCfg.lbl.Shockcordlength = Shock cord length: ! RocketComponentConfig RocketCompCfg.lbl.Componentname = Component name: -RocketCompCfg.ttip.Thecomponentname = The component name. +RocketCompCfg.lbl.Componentname.ttip = The component name. RocketCompCfg.tab.Override = Override -RocketCompCfg.tab.MassandCGoverride = Mass and CG override options +RocketCompCfg.tab.Override.ttip = Mass and CG override options RocketCompCfg.tab.Figure = Figure RocketCompCfg.tab.Figstyleopt = Figure style options RocketCompCfg.tab.Comment = Comment -RocketCompCfg.tab.Specifyacomment = Specify a comment for the component +RocketCompCfg.tab.Comment.ttip = Specify a comment for the component RocketCompCfg.tab.Appearance = Appearance RocketCompCfg.lbl.Mass = Mass: RocketCompCfg.lbl.Componentmass = Component mass: @@ -1812,8 +1812,8 @@ CustomFinImport.error.badimage = Could not deduce fin shape from image. CustomFinImport.description = The image will be converted internally to black and white image (black for the fin), so make sure you use a solid dark color for the fin, and white or a light color for the background. The fin must be touching the bottom of the image, which is the base of the fin. -PresetModel.lbl.select = Select preset -PresetModel.lbl.database = From database... +PresetModel.lbl.custompreset = Custom +PresetModel.lbl.partsLib = Parts Library DecalModel.lbl.select = <none> DecalModel.lbl.choose = From file... diff --git a/core/resources/l10n/messages_zh_CN.properties b/core/resources/l10n/messages_zh_CN.properties index 9e9cfed05..e968675da 100644 --- a/core/resources/l10n/messages_zh_CN.properties +++ b/core/resources/l10n/messages_zh_CN.properties @@ -803,8 +803,8 @@ PreferencesDialog.languages.default = \u7CFB\u7EDF\u9ED8\u8BA4 PreferencesDialog.lbl.language = \u754C\u9762\u8BED\u8A00: PreferencesDialog.lbl.languageEffect = \u8BED\u8A00\u8BBE\u7F6E\u5C06\u5728OpenRocket\u91CD\u542F\u540E\u751F\u6548 -PresetModel.lbl.database = \u4ECE\u6570\u636E\u5E93... -PresetModel.lbl.select = \u9009\u62E9\u9884\u8BBE +PresetModel.lbl.custompreset = \u5b9a\u5236 +PresetModel.lbl.partsLib = \u96f6\u4ef6\u5e93 PrintDialog.but.previewAndPrint = \u9884\u89C8 & \u6253\u5370 PrintDialog.checkbox.showByStage = \u6309\u7EA7\u663E\u793A @@ -927,13 +927,13 @@ RocketCompCfg.tab.Appearance = \u5916\u89C2 RocketCompCfg.tab.Comment = \u6CE8\u91CA RocketCompCfg.tab.Figstyleopt = \u6837\u5F0F\u9009\u9879 RocketCompCfg.tab.Figure = \u6837\u5F0F -RocketCompCfg.tab.MassandCGoverride = \u8D28\u91CF\u53CA\u91CD\u5FC3\u9009\u9879 +RocketCompCfg.tab.Override.ttip = \u8D28\u91CF\u53CA\u91CD\u5FC3\u9009\u9879 RocketCompCfg.tab.Override = \u8986\u5199 -RocketCompCfg.tab.Specifyacomment = \u7EC4\u4EF6\u6CE8\u91CA +RocketCompCfg.tab.Comment.ttip = \u7EC4\u4EF6\u6CE8\u91CA RocketCompCfg.title.Aftshoulder = \u524D\u8FDE\u63A5\u5904 RocketCompCfg.title.Noseconeshoulder = \u5934\u9525\u8FDE\u63A5\u5904 RocketCompCfg.ttip.Endcapped = \u8FDE\u63A5\u5904\u7EC8\u7AEF\u662F\u5426\u6709\u76D6. -RocketCompCfg.ttip.Thecomponentname = \u7EC4\u4EF6\u540D\u79F0. +RocketCompCfg.lbl.Componentname.ttip = \u7EC4\u4EF6\u540D\u79F0. RocketComponent.Position.ABSOLUTE = \u5934\u9525\u5C16\u7AEF RocketComponent.Position.AFTER = \u7236\u7EC4\u4EF6\u4E4B\u540E diff --git a/core/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java b/core/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java index d51448699..fbdac87e4 100644 --- a/core/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/AerodynamicCalculator.java @@ -68,5 +68,5 @@ public interface AerodynamicCalculator extends Monitorable { */ public AerodynamicCalculator newInstance(); - public boolean isContinuous( final Rocket rkt); + public boolean isContinuous(FlightConfiguration configuration, final Rocket rkt); } diff --git a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java index b1d2f3bfc..add6b3b7b 100644 --- a/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java +++ b/core/src/net/sf/openrocket/aerodynamics/BarrowmanCalculator.java @@ -4,6 +4,7 @@ import static net.sf.openrocket.util.MathUtil.pow2; import java.util.*; +import net.sf.openrocket.rocketcomponent.AxialStage; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -80,7 +81,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { Map<RocketComponent, AerodynamicForces> assemblyMap = new LinkedHashMap<>(); // Calculate non-axial force data - calculateForceAnalysis(conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings); + calculateForceAnalysis(configuration, conditions, configuration.getRocket(), instMap, eachMap, assemblyMap, warnings); // Calculate drag coefficient data AerodynamicForces rocketForces = assemblyMap.get(configuration.getRocket()); @@ -125,7 +126,8 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { return finalMap; } - private AerodynamicForces calculateForceAnalysis( FlightConditions conds, + private AerodynamicForces calculateForceAnalysis( FlightConfiguration configuration, + FlightConditions conds, RocketComponent comp, InstanceMap instances, Map<RocketComponent, AerodynamicForces> eachForces, @@ -152,8 +154,12 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { } for( RocketComponent child : comp.getChildren()) { + // Ignore inactive stages + if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) { + continue; + } // forces particular to each component - AerodynamicForces childForces = calculateForceAnalysis(conds, child, instances, eachForces, assemblyForces, warnings); + AerodynamicForces childForces = calculateForceAnalysis(configuration, conds, child, instances, eachForces, assemblyForces, warnings); if(null != childForces) { aggregateForces.merge(childForces); @@ -240,7 +246,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { if (calcMap == null) buildCalcMap(configuration); - if( ! isContinuous( configuration.getRocket() ) ){ + if (!isContinuous(configuration, configuration.getRocket())){ warnings.add( Warning.DIAMETER_DISCONTINUITY); } @@ -266,20 +272,32 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { } @Override - public boolean isContinuous( final Rocket rkt){ - return testIsContinuous( rkt); + public boolean isContinuous(FlightConfiguration configuration, final Rocket rkt){ + return testIsContinuous(configuration, rkt); } - private boolean testIsContinuous( final RocketComponent treeRoot ){ + private boolean testIsContinuous(FlightConfiguration configuration, final RocketComponent treeRoot ){ Queue<RocketComponent> queue = new LinkedList<>(); - queue.addAll(treeRoot.getChildren()); + for (RocketComponent child : treeRoot.getChildren()) { + // Ignore inactive stages + if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) { + continue; + } + queue.add(child); + } boolean isContinuous = true; SymmetricComponent prevComp = null; while((isContinuous)&&( null != queue.peek())){ RocketComponent comp = queue.poll(); if( comp instanceof SymmetricComponent ){ - queue.addAll( comp.getChildren()); + for (RocketComponent child : comp.getChildren()) { + // Ignore inactive stages + if (child instanceof AxialStage && !configuration.isStageActive(child.getStageNumber())) { + continue; + } + queue.add(child); + } SymmetricComponent sym = (SymmetricComponent) comp; if( null == prevComp){ @@ -303,7 +321,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { prevComp = sym; }else if( comp instanceof ComponentAssembly ){ - isContinuous &= testIsContinuous( comp ); + isContinuous &= testIsContinuous(configuration, comp); } } @@ -319,7 +337,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { * @param configuration Rocket configuration * @param conditions Flight conditions taken into account * @param map ? - * @param set Set to handle + * @param warningSet Set to handle warnings * @return friction drag for entire rocket */ private double calculateFrictionCD(FlightConfiguration configuration, FlightConditions conditions, @@ -611,7 +629,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { double radius = 0; final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); - if (prevComponent != null) + if (prevComponent != null && configuration.isComponentActive(prevComponent)) radius = prevComponent.getAftRadius(); if (radius < s.getForeRadius()) { @@ -672,7 +690,7 @@ public class BarrowmanCalculator extends AbstractAerodynamicCalculator { // its aft CD double radius = 0; final SymmetricComponent prevComponent = s.getPreviousSymmetricComponent(); - if (prevComponent != null) { + if (prevComponent != null && configuration.isComponentActive(prevComponent)) { radius = prevComponent.getAftRadius(); } diff --git a/core/src/net/sf/openrocket/document/Simulation.java b/core/src/net/sf/openrocket/document/Simulation.java index 50047e07f..3143eb83d 100644 --- a/core/src/net/sf/openrocket/document/Simulation.java +++ b/core/src/net/sf/openrocket/document/Simulation.java @@ -1,5 +1,6 @@ package net.sf.openrocket.document; +import java.lang.reflect.InvocationTargetException; import java.util.EventListener; import java.util.EventObject; import java.util.List; @@ -350,13 +351,15 @@ public class Simulation implements ChangeSource, Cloneable { SimulationEngine simulator; try { - simulator = simulationEngineClass.newInstance(); + simulator = simulationEngineClass.getConstructor().newInstance(); } catch (InstantiationException e) { throw new IllegalStateException("Cannot instantiate simulator.", e); } catch (IllegalAccessException e) { throw new IllegalStateException("Cannot access simulator instance?! BUG!", e); + } catch (InvocationTargetException | NoSuchMethodException e) { + throw new RuntimeException(e); } - + SimulationConditions simulationConditions = options.toSimulationConditions(); simulationConditions.setSimulation(this); for (SimulationListener l : additionalListeners) { diff --git a/core/src/net/sf/openrocket/file/simplesax/SimpleSAX.java b/core/src/net/sf/openrocket/file/simplesax/SimpleSAX.java index 2053d42e2..cc1fc55f6 100644 --- a/core/src/net/sf/openrocket/file/simplesax/SimpleSAX.java +++ b/core/src/net/sf/openrocket/file/simplesax/SimpleSAX.java @@ -11,6 +11,9 @@ import org.xml.sax.SAXException; import org.xml.sax.XMLReader; import org.xml.sax.helpers.XMLReaderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.parsers.SAXParser; +import javax.xml.parsers.SAXParserFactory; /** * A "simple SAX" XML reader. This system imposes the limit that an XML element may @@ -18,27 +21,26 @@ import org.xml.sax.helpers.XMLReaderFactory; * both. This holds true for both the OpenRocket and RockSim design formats and the * RockSim engine definition format. * <p> - * The actual handling is performed by subclasses of {@link ElementHandler}. The + * The actual handling is performed by subclasses of {@link ElementHandler}. The * initial handler is provided to the {@link #readXML(InputSource, ElementHandler, WarningSet)} * method. - * + * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ public class SimpleSAX { - static final XMLReaderCache cache = new XMLReaderCache(10); /** * Read a simple XML file. - * + * * @param source the SAX input source. * @param initialHandler the initial content handler. * @param warnings a warning set to store warning (cannot be <code>null</code>). * @throws IOException if an I/O exception occurs while reading. * @throws SAXException if e.g. malformed XML is encountered. */ - public static void readXML(InputSource source, ElementHandler initialHandler, - WarningSet warnings) throws IOException, SAXException { + public static void readXML(InputSource source, ElementHandler initialHandler, WarningSet warnings) + throws IOException, SAXException { DelegatorHandler xmlhandler = new DelegatorHandler(initialHandler, warnings); @@ -55,17 +57,27 @@ public class SimpleSAX { } private static class XMLReaderCache { + private final SAXParserFactory parserFactory; private final BlockingQueue<XMLReader> queue; + private XMLReaderCache( int maxSize ) { - this.queue = new LinkedBlockingQueue<XMLReader>(maxSize); + parserFactory = SAXParserFactory.newInstance(); + parserFactory.setNamespaceAware(true); + queue = new LinkedBlockingQueue<XMLReader>(maxSize); } private XMLReader createXMLReader() throws SAXException { - XMLReader reader = queue.poll(); if ( reader == null ) { + try { reader = XMLReaderFactory.createXMLReader(); + SAXParser parser = parserFactory.newSAXParser(); + reader = parser.getXMLReader(); + } + catch (ParserConfigurationException ignore) { + System.out.print(ignore); + } } return reader; } @@ -77,5 +89,4 @@ public class SimpleSAX { queue.offer( reader ); } } - } diff --git a/core/src/net/sf/openrocket/masscalc/MassCalculator.java b/core/src/net/sf/openrocket/masscalc/MassCalculator.java index 463ed7f4b..4b36d03dc 100644 --- a/core/src/net/sf/openrocket/masscalc/MassCalculator.java +++ b/core/src/net/sf/openrocket/masscalc/MassCalculator.java @@ -104,7 +104,7 @@ public class MassCalculator implements Monitorable { public static RigidBody calculate( final MassCalculation.Type _type, final SimulationStatus status ){ final FlightConfiguration config = status.getConfiguration(); final double time = status.getSimulationTime(); - final Collection<MotorClusterState> activeMotorList = status.getMotors(); + final Collection<MotorClusterState> activeMotorList = status.getActiveMotors(); MassCalculation calculation= new MassCalculation( _type, config, time, activeMotorList, config.getRocket(), Transformation.IDENTITY, null); calculation.calculateAssembly(); diff --git a/core/src/net/sf/openrocket/motor/IgnitionEvent.java b/core/src/net/sf/openrocket/motor/IgnitionEvent.java index 60a2bf2ea..619642167 100644 --- a/core/src/net/sf/openrocket/motor/IgnitionEvent.java +++ b/core/src/net/sf/openrocket/motor/IgnitionEvent.java @@ -4,6 +4,7 @@ import java.util.Locale; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.simulation.FlightEvent; import net.sf.openrocket.startup.Application; @@ -13,25 +14,25 @@ public enum IgnitionEvent { //// Automatic (launch or ejection charge) AUTOMATIC( "AUTOMATIC", "MotorMount.IgnitionEvent.AUTOMATIC"){ @Override - public boolean isActivationEvent(FlightEvent testEvent, RocketComponent targetComponent) { + public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent) { AxialStage targetStage = targetComponent.getStage(); - if ( targetStage.isLaunchStage() ){ - return LAUNCH.isActivationEvent(testEvent, targetComponent); + if (targetStage.isLaunchStage(config)) { + return LAUNCH.isActivationEvent(config, testEvent, targetComponent); } else { - return EJECTION_CHARGE.isActivationEvent(testEvent, targetComponent); + return EJECTION_CHARGE.isActivationEvent(config, testEvent, targetComponent); } } }, LAUNCH ( "LAUNCH", "MotorMount.IgnitionEvent.LAUNCH"){ @Override - public boolean isActivationEvent( FlightEvent fe, RocketComponent source){ + public boolean isActivationEvent(FlightConfiguration config, FlightEvent fe, RocketComponent source){ return (fe.getType() == FlightEvent.Type.LAUNCH); } }, EJECTION_CHARGE ("EJECTION_CHARGE", "MotorMount.IgnitionEvent.EJECTION_CHARGE"){ @Override - public boolean isActivationEvent( FlightEvent testEvent, RocketComponent targetComponent){ + public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent){ if (testEvent.getType() != FlightEvent.Type.EJECTION_CHARGE){ return false; } @@ -44,7 +45,7 @@ public enum IgnitionEvent { }, BURNOUT ("BURNOUT", "MotorMount.IgnitionEvent.BURNOUT"){ @Override - public boolean isActivationEvent( FlightEvent testEvent, RocketComponent targetComponent){ + public boolean isActivationEvent(FlightConfiguration config, FlightEvent testEvent, RocketComponent targetComponent){ if (testEvent.getType() != FlightEvent.Type.BURNOUT) return false; @@ -64,7 +65,7 @@ public enum IgnitionEvent { //public static final IgnitionEvent[] events = {AUTOMATIC, LAUNCH, EJECTION_CHARGE, BURNOUT, NEVER}; - public boolean isActivationEvent( FlightEvent fe, RocketComponent source){ + public boolean isActivationEvent(FlightConfiguration config, FlightEvent fe, RocketComponent source){ // default behavior. Also for the NEVER case. return false; } diff --git a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java index 20f3a13f5..8e0f533c5 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/AxialStage.java @@ -67,6 +67,23 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC public boolean isCompatible(Class<? extends RocketComponent> type) { return BodyComponent.class.isAssignableFrom(type); } + + /** + * Returns whether the current stage is active in the currently selected configuration. + * @return true if the stage is active, false if not + */ + public boolean isStageActive() { + return getRocket().getSelectedConfiguration().isStageActive(getStageNumber()); + } + + /** + * Returns whether the current stage is active in the flight configuration. + * @param fc the flight configuration to check + * @return true if the stage is active, false if not + */ + public boolean isStageActive(FlightConfiguration fc) { + return fc.isStageActive(getStageNumber()); + } @Override public void copyFlightConfiguration(FlightConfigurationId oldConfigId, FlightConfigurationId newConfigId) { @@ -113,11 +130,11 @@ public class AxialStage extends ComponentAssembly implements FlightConfigurableC /** * returns if the object is a launch stage + * @param config the flight configuration which will check which stages are active * @return if the object is a launch stage */ - public boolean isLaunchStage(){ - return ( this instanceof ParallelStage ) - ||( getRocket().getBottomCoreStage().equals(this)); + public boolean isLaunchStage(FlightConfiguration config) { + return (getRocket().getBottomCoreStage(config).equals(this)); } /** diff --git a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java index 2ae0211fa..7bb3cfae3 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java +++ b/core/src/net/sf/openrocket/rocketcomponent/FlightConfiguration.java @@ -60,8 +60,8 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo } /* Cached data */ - final protected HashMap<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>(); - final protected HashMap<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>(); + final protected Map<Integer, StageFlags> stages = new HashMap<Integer, StageFlags>(); // Map of stage number to StageFlags of the corresponding stage + final protected Map<MotorConfigurationId, MotorConfiguration> motors = new HashMap<MotorConfigurationId, MotorConfiguration>(); final private Collection<MotorConfiguration> activeMotors = new ArrayList<MotorConfiguration>(); final private InstanceMap activeInstances = new InstanceMap(); @@ -179,31 +179,52 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo */ public void setOnlyStage(final int stageNumber) { _setAllStages(false); - _setStageActive(stageNumber, true); + _setStageActive(stageNumber, true, false); updateMotors(); updateActiveInstances(); } - - /** + + /** * This method flags the specified stage as requested. Other stages are unaffected. - * + * * @param stageNumber stage number to flag * @param _active inactive (<code>false</code>) or active (<code>true</code>) + * @param activateSubStages whether the sub-stages of the specified stage should be activated as well. */ - private void _setStageActive(final int stageNumber, final boolean _active ) { + public void _setStageActive(final int stageNumber, final boolean _active, final boolean activateSubStages) { if ((0 <= stageNumber) && (stages.containsKey(stageNumber))) { stages.get(stageNumber).active = _active; + if (activateSubStages) { + // Set the active state of all the sub-stages as well. + for (AxialStage stage : rocket.getStage(stageNumber).getSubStages()) { + stages.get(stage.getStageNumber()).active = _active; + } + } fireChangeEvent(); return; } log.error("error: attempt to retrieve via a bad stage number: " + stageNumber); } + /** + * This method flags the specified stage as requested. Actives the sub-stages of the specified stage as well. + * + * @param stageNumber stage number to flag + * @param _active inactive (<code>false</code>) or active (<code>true</code>) + */ + public void _setStageActive(final int stageNumber, final boolean _active ) { + _setStageActive(stageNumber, _active, true); + } + public void toggleStage(final int stageNumber) { if ((0 <= stageNumber) && (stages.containsKey(stageNumber))) { StageFlags flags = stages.get(stageNumber); flags.active = !flags.active; + // Set the active state of all the sub-stages as well. + for (AxialStage stage : rocket.getStage(stageNumber).getSubStages()) { + stages.get(stage.getStageNumber()).active = flags.active; + } updateMotors(); updateActiveInstances(); @@ -338,6 +359,18 @@ public class FlightConfiguration implements FlightConfigurableParameter<FlightCo return results; } + + /** + * Return all the stages in this configuration. + * @return all the stages in this configuration. + */ + public List<AxialStage> getAllStages() { + List<AxialStage> stages = new ArrayList<>(); + for (StageFlags flags : this.stages.values()) { + stages.add( rocket.getStage(flags.stageNumber)); + } + return stages; + } public List<AxialStage> getActiveStages() { List<AxialStage> activeStages = new ArrayList<>(); diff --git a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java index a1935c0cf..272de11ac 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java +++ b/core/src/net/sf/openrocket/rocketcomponent/InnerTube.java @@ -26,7 +26,7 @@ import net.sf.openrocket.util.MathUtil; * * @author Sampo Niskanen <sampo.niskanen@iki.fi> */ -public class InnerTube extends ThicknessRingComponent implements AxialPositionable, BoxBounded, Clusterable, RadialParent, MotorMount { +public class InnerTube extends ThicknessRingComponent implements AxialPositionable, BoxBounded, Clusterable, RadialParent, MotorMount, InsideColorComponent { private static final Translator trans = Application.getTranslator(); private static final Logger log = LoggerFactory.getLogger(InnerTube.class); @@ -37,6 +37,8 @@ public class InnerTube extends ThicknessRingComponent implements AxialPositionab private double overhang = 0; private boolean isActingMount; private MotorConfigurationSet motors; + + private InsideColorComponentHandler insideColorComponentHandler = new InsideColorComponentHandler(this); /** * Main constructor. @@ -447,6 +449,16 @@ public class InnerTube extends ThicknessRingComponent implements AxialPositionab return this.motors.toDebug(); } + @Override + public InsideColorComponentHandler getInsideColorComponentHandler() { + return this.insideColorComponentHandler; + } + + @Override + public void setInsideColorComponentHandler(InsideColorComponentHandler handler) { + this.insideColorComponentHandler = handler; + } + @Override public boolean addConfigListener(RocketComponent listener) { boolean success = super.addConfigListener(listener); diff --git a/core/src/net/sf/openrocket/rocketcomponent/ParallelStage.java b/core/src/net/sf/openrocket/rocketcomponent/ParallelStage.java index 8f74ee6fa..d09b71f54 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/ParallelStage.java +++ b/core/src/net/sf/openrocket/rocketcomponent/ParallelStage.java @@ -111,8 +111,8 @@ public class ParallelStage extends AxialStage implements FlightConfigurableCompo } @Override - public boolean isLaunchStage(){ - return true; + public boolean isLaunchStage(FlightConfiguration config) { + return config.isStageActive(this.stageNumber); } @Override diff --git a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java index 7a3f1780a..37d245772 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/Rocket.java +++ b/core/src/net/sf/openrocket/rocketcomponent/Rocket.java @@ -197,24 +197,37 @@ public class Rocket extends ComponentAssembly { public AxialStage getStage( final int stageNumber ) { return this.stageMap.get( stageNumber); } - - /* - * Returns the stage at the top of the central stack - * - * @Return a reference to the topmost stage + + /** + * Get the topmost stage, only taking into account active stages from the flight configuration. + * @param config flight configuration dictating which stages are active + * @return the topmost active stage, or null if there are no active stages. */ - public AxialStage getTopmostStage(){ - return (AxialStage) getChild(0); + public AxialStage getTopmostStage(FlightConfiguration config) { + if (config == null) return null; + + for (int i = 0; i < getChildCount(); i++) { + if (getChild(i) instanceof AxialStage && config.isStageActive(getChild(i).getStageNumber())) { + return (AxialStage) getChild(i); + } + } + return null; } - - /* - * Returns the stage at the top of the central stack - * - * @Return a reference to the topmost stage + + /** + * Get the bottommost stage, only taking into account active stages from the flight configuration. + * @param config flight configuration dictating which stages are active + * @return the bottommost active stage, or null if there are no active stages. */ - /*package-local*/ AxialStage getBottomCoreStage(){ - // get last stage that's a direct child of the rocket. - return (AxialStage) children.get( children.size()-1 ); + public AxialStage getBottomCoreStage(FlightConfiguration config) { + if (config == null) return null; + + for (int i = getChildCount() - 1; i >= 0; i--) { + if (getChild(i) instanceof AxialStage && config.isStageActive(getChild(i).getStageNumber())) { + return (AxialStage) getChild(i); + } + } + return null; } @Override @@ -274,6 +287,11 @@ public class Rocket extends ComponentAssembly { refType = type; fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } + + @Override + public double getLength() { + return selectedConfiguration.getLength(); + } public double getCustomReferenceLength() { @@ -291,6 +309,17 @@ public class Rocket extends ComponentAssembly { fireComponentChangeEvent(ComponentChangeEvent.NONFUNCTIONAL_CHANGE); } } + + @Override + public double getBoundingRadius() { + double bounding = 0; + for (RocketComponent comp : children) { + if (comp instanceof ComponentAssembly) { + bounding = Math.max(bounding, ((ComponentAssembly) comp).getBoundingRadius()); + } + } + return bounding; + } @@ -390,10 +419,12 @@ public class Rocket extends ComponentAssembly { this.stageMap = r.stageMap; // these flight configurations need to reference the _this_ Rocket: + this.configSet.reset(); this.configSet.setDefault(new FlightConfiguration(this)); for (FlightConfigurationId key : r.configSet.map.keySet()) { this.configSet.set(key, new FlightConfiguration(this, key)); } + this.selectedConfiguration = this.configSet.get(r.getSelectedConfiguration().getId()); this.perfectFinish = r.perfectFinish; @@ -735,6 +766,14 @@ public class Rocket extends ComponentAssembly { fireComponentChangeEvent(ComponentChangeEvent.TREE_CHANGE); return nextConfig.getFlightConfigurationID(); } + + /** + * Return all the flight configurations of this rocket. + * @return all the flight configurations of this rocket. + */ + public FlightConfigurableParameterSet<FlightConfiguration> getFlightConfigurations() { + return this.configSet; + } /** diff --git a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java index 076211716..637d01465 100644 --- a/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java +++ b/core/src/net/sf/openrocket/rocketcomponent/RocketComponent.java @@ -1010,7 +1010,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab * If the length of a component is settable, the class must define the setter method * itself. */ - public final double getLength() { + public double getLength() { mutex.verify(); return length; } @@ -1545,6 +1545,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab AxialStage stage = (AxialStage) component; this.getRocket().forgetStage(stage); } + + // Remove sub-stages of the removed component + for (AxialStage stage : component.getSubStages()) { + this.getRocket().forgetStage(stage); + } this.checkComponentStructure(); component.checkComponentStructure(); @@ -1749,6 +1754,21 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab } throw new IllegalStateException("getStage() called on hierarchy without an AxialStage."); } + + /** + * Returns all the stages that are a child or sub-child of this component. + * @return all the stages that are a child or sub-child of this component. + */ + public final List<AxialStage> getSubStages() { + List<AxialStage> result = new LinkedList<>(); + Iterator<RocketComponent> it = iterator(false); + while (it.hasNext()) { + RocketComponent c = it.next(); + if (c instanceof AxialStage) + result.add((AxialStage) c); + } + return result; + } /** * Return the first component assembly component that this component belongs to. diff --git a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java index 8fdae800a..1d5b62e76 100644 --- a/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java +++ b/core/src/net/sf/openrocket/simulation/BasicEventSimulationEngine.java @@ -45,7 +45,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { private final static double AOA_TUMBLE_CONDITION = Math.PI / 9.0; // The thrust must be below this value for the transition to tumbling. - // TODO: this is an arbitrary value + // TODO HIGH: this is an arbitrary value private final static double THRUST_TUMBLE_CONDITION = 0.01; private SimulationStepper currentStepper; @@ -65,7 +65,9 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Set up rocket configuration this.fcid = simulationConditions.getFlightConfigurationID(); - FlightConfiguration simulationConfig = simulationConditions.getRocket().getFlightConfiguration( this.fcid).clone(); + FlightConfiguration origConfig = simulationConditions.getRocket().getFlightConfiguration(this.fcid); + FlightConfiguration simulationConfig = origConfig.clone(); + simulationConfig.copyStages(origConfig); // Clone the stage activation configuration if ( ! simulationConfig.hasMotors() ) { throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noMotorsDefined")); } @@ -74,7 +76,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { currentStatus.getEventQueue().add(new FlightEvent(FlightEvent.Type.LAUNCH, 0, simulationConditions.getRocket())); { // main simulation branch - final String branchName = simulationConfig.getRocket().getTopmostStage().getName(); + final String branchName = simulationConfig.getRocket().getTopmostStage(currentStatus.getConfiguration()).getName(); currentStatus.setFlightData(new FlightDataBranch( branchName, FlightDataType.TYPE_TIME)); } toSimulate.push(currentStatus); @@ -273,9 +275,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // Check for motor ignition events, add ignition events to queue for (MotorClusterState state : currentStatus.getActiveMotors() ){ - if( state.testForIgnition(event )){ - final double simulationTime = currentStatus.getSimulationTime() ; - + if (state.testForIgnition(currentStatus.getConfiguration(), event)) { MotorClusterState sourceState = (MotorClusterState) event.getData(); double ignitionDelay = 0; if (event.getType() == FlightEvent.Type.BURNOUT) @@ -543,7 +543,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { } - // TODO : FUTURE : do not hard code the 1200 (maybe even make it configurable by the user) + // TODO FUTURE : do not hard code the 1200 (maybe even make it configurable by the user) if( 1200 < currentStatus.getSimulationTime() ){ ret = false; log.error("Simulation hit max time (1200s): aborting."); @@ -553,6 +553,7 @@ public class BasicEventSimulationEngine implements SimulationEngine { // If no motor has ignited, abort if (!currentStatus.isMotorIgnited()) { + // TODO MEDIUM: display this as a warning to the user (e.g. highlight the cell in the simulation panel in red and a hover: 'make sure the motor ignition is correct' or something) throw new MotorIgnitionException(trans.get("BasicEventSimulationEngine.error.noIgnition")); } diff --git a/core/src/net/sf/openrocket/simulation/FlightEvent.java b/core/src/net/sf/openrocket/simulation/FlightEvent.java index 55ceaab36..b78ad1f3b 100644 --- a/core/src/net/sf/openrocket/simulation/FlightEvent.java +++ b/core/src/net/sf/openrocket/simulation/FlightEvent.java @@ -172,7 +172,7 @@ public class FlightEvent implements Comparable<FlightEvent> { * @return */ public void validate(){ - if( this.time == Double.NaN ){ + if(Double.isNaN(this.time)){ throw new IllegalStateException(type.name()+" event has a NaN time!"); } switch( this.type ){ diff --git a/core/src/net/sf/openrocket/simulation/MotorClusterState.java b/core/src/net/sf/openrocket/simulation/MotorClusterState.java index 43c73f9c3..728287c96 100644 --- a/core/src/net/sf/openrocket/simulation/MotorClusterState.java +++ b/core/src/net/sf/openrocket/simulation/MotorClusterState.java @@ -4,6 +4,7 @@ import net.sf.openrocket.motor.IgnitionEvent; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; import net.sf.openrocket.motor.MotorConfigurationId; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.MotorMount; import net.sf.openrocket.rocketcomponent.RocketComponent; @@ -121,8 +122,8 @@ public class MotorClusterState { /** * Compute the average thrust over an interval. * - * @param simulationTime - * @param cond + * @param startSimulationTime start time of the averaging interval + * @param endSimulationTime end time of the averaging interval * @return */ public double getAverageThrust( final double startSimulationTime, final double endSimulationTime) { @@ -141,7 +142,6 @@ public class MotorClusterState { * Compute the average thrust over an interval. * * @param simulationTime - * @param cond * @return */ public double getThrust( final double simulationTime){ @@ -182,9 +182,9 @@ public class MotorClusterState { currentState = ThrustState.ARMED; } - public boolean testForIgnition( final FlightEvent _event ){ + public boolean testForIgnition(FlightConfiguration flightConfiguration, final FlightEvent _event ){ RocketComponent mount = (RocketComponent) this.getMount(); - return getIgnitionEvent().isActivationEvent( _event, mount); + return getIgnitionEvent().isActivationEvent(flightConfiguration, _event, mount); } public String toDescription(){ diff --git a/core/src/net/sf/openrocket/startup/Preferences.java b/core/src/net/sf/openrocket/startup/Preferences.java index 64f1de5a0..3d750c651 100644 --- a/core/src/net/sf/openrocket/startup/Preferences.java +++ b/core/src/net/sf/openrocket/startup/Preferences.java @@ -68,6 +68,7 @@ public abstract class Preferences implements ChangeSource { public static final String PREFERRED_THRUST_CURVE_MOTOR_NODE = "preferredThrustCurveMotors"; private static final String AUTO_OPEN_LAST_DESIGN = "AUTO_OPEN_LAST_DESIGN"; private static final String OPEN_LEFTMOST_DESIGN_TAB = "OPEN_LEFTMOST_DESIGN_TAB"; + private static final String SHOW_MARKERS = "SHOW_MARKERS"; private static final String SHOW_ROCKSIM_FORMAT_WARNING = "SHOW_ROCKSIM_FORMAT_WARNING"; //Preferences related to 3D graphics @@ -469,7 +470,26 @@ public abstract class Preferences implements ChangeSource { public final boolean isAlwaysOpenLeftmostTab() { return this.getBoolean(OPEN_LEFTMOST_DESIGN_TAB, false); } - + + /** + * Set whether pod set/booster markers should only be displayed when the pod set/booster is selected. + * @param enabled true if pod set/booster markers should only be displayed when the pod set/booster is selected, + * false if they should be displayed permanently. + */ + public final void setShowMarkers(boolean enabled) { + this.putBoolean(SHOW_MARKERS, enabled); + } + + /** + * Answer if pod set/booster markers should only be displayed when the pod set/booster is selected + * + * @return true if pod set/booster markers should only be displayed when the pod set/booster is selected, + * false if they should be displayed permanently. + */ + public final boolean isShowMarkers() { + return this.getBoolean(SHOW_MARKERS, false); + } + /** * Return the OpenRocket unique ID. * diff --git a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java index 9f285a709..828a49447 100644 --- a/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java +++ b/core/test/net/sf/openrocket/aerodynamics/BarrowmanCalculatorTest.java @@ -267,22 +267,25 @@ public class BarrowmanCalculatorTest { public void testContinuousRocket() { Rocket rocket = TestRockets.makeEstesAlphaIII(); AerodynamicCalculator calc = new BarrowmanCalculator(); + FlightConfiguration configuration = rocket.getSelectedConfiguration(); - assertTrue("Estes Alpha III should be continous: ", calc.isContinuous( rocket)); + assertTrue("Estes Alpha III should be continous: ", calc.isContinuous(configuration, rocket)); } @Test public void testContinuousRocketWithStrapOns() { Rocket rocket = TestRockets.makeFalcon9Heavy(); AerodynamicCalculator calc = new BarrowmanCalculator(); + FlightConfiguration configuration = rocket.getSelectedConfiguration(); - assertTrue("F9H should be continuous: ", calc.isContinuous( rocket)); + assertTrue("F9H should be continuous: ", calc.isContinuous(configuration, rocket)); } @Test public void testRadialDiscontinuousRocket() { Rocket rocket = TestRockets.makeEstesAlphaIII(); AerodynamicCalculator calc = new BarrowmanCalculator(); + FlightConfiguration configuration = rocket.getSelectedConfiguration(); NoseCone nose = (NoseCone)rocket.getChild(0).getChild(0); BodyTube body = (BodyTube)rocket.getChild(0).getChild(1); @@ -291,13 +294,14 @@ public class BarrowmanCalculatorTest { body.setOuterRadius( 0.012 ); body.setName( body.getName()+" << discontinuous"); - assertFalse(" Estes Alpha III has an undetected discontinuity:", calc.isContinuous( rocket)); + assertFalse(" Estes Alpha III has an undetected discontinuity:", calc.isContinuous(configuration, rocket)); } @Test public void testRadialDiscontinuityWithStrapOns() { Rocket rocket = TestRockets.makeFalcon9Heavy(); AerodynamicCalculator calc = new BarrowmanCalculator(); + FlightConfiguration configuration = rocket.getSelectedConfiguration(); final AxialStage coreStage = (AxialStage)rocket.getChild(1); final ParallelStage booster = (ParallelStage)coreStage.getChild(0).getChild(0); @@ -309,7 +313,7 @@ public class BarrowmanCalculatorTest { body.setOuterRadius( 0.012 ); body.setName( body.getName()+" << discontinuous"); - assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous( rocket)); + assertFalse(" Missed discontinuity in Falcon 9 Heavy:", calc.isContinuous(configuration, rocket)); } @Test diff --git a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java index c4ad802af..51db91e7d 100644 --- a/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java +++ b/core/test/net/sf/openrocket/rocketcomponent/FlightConfigurationTest.java @@ -294,9 +294,22 @@ public class FlightConfigurationTest extends BaseTestCase { config.toggleStage(0); assertThat(" toggle stage #0: ", config.isStageActive(0), equalTo(false)); - - AxialStage sustainer = rkt.getTopmostStage(); - AxialStage booster = rkt.getBottomCoreStage(); + + AxialStage sustainer = rkt.getTopmostStage(config); + AxialStage booster = rkt.getBottomCoreStage(config); + assertThat(" sustainer stage is stage #1: ", sustainer.getStageNumber(), equalTo(1)); + assertThat(" booster stage is stage #1: ", booster.getStageNumber(), equalTo(1)); + + config.setAllStages(); + config._setStageActive(1, false); + sustainer = rkt.getTopmostStage(config); + booster = rkt.getBottomCoreStage(config); + assertThat(" sustainer stage is stage #1: ", sustainer.getStageNumber(), equalTo(0)); + assertThat(" booster stage is stage #1: ", booster.getStageNumber(), equalTo(0)); + + config.setAllStages(); + sustainer = rkt.getTopmostStage(config); + booster = rkt.getBottomCoreStage(config); assertThat(" sustainer stage is stage #0: ", sustainer.getStageNumber(), equalTo(0)); assertThat(" booster stage is stage #1: ", booster.getStageNumber(), equalTo(1)); @@ -351,7 +364,6 @@ public class FlightConfigurationTest extends BaseTestCase { selected.clearAllStages(); selected.toggleStage(1); - selected.toggleStage(2); // vvvv Test Target vvvv InstanceMap instances = selected.getActiveInstances(); diff --git a/core/test/net/sf/openrocket/simulation/DisableStageTest.java b/core/test/net/sf/openrocket/simulation/DisableStageTest.java new file mode 100644 index 000000000..9b4b0f7df --- /dev/null +++ b/core/test/net/sf/openrocket/simulation/DisableStageTest.java @@ -0,0 +1,305 @@ +package net.sf.openrocket.simulation; + +import net.sf.openrocket.document.Simulation; +import net.sf.openrocket.rocketcomponent.FlightConfiguration; +import net.sf.openrocket.rocketcomponent.FlightConfigurationId; +import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.simulation.exception.MotorIgnitionException; +import net.sf.openrocket.simulation.exception.SimulationException; +import net.sf.openrocket.simulation.listeners.AbstractSimulationListener; +import net.sf.openrocket.simulation.listeners.SimulationListener; +import net.sf.openrocket.util.BaseTestCase.BaseTestCase; +import net.sf.openrocket.util.TestRockets; +import org.junit.Assert; +import org.junit.Test; + +/** + * Test class that tests the effect on the simulation results of activating/deactivating stages. + * + * @author Sibo Van Gool <sibo.vangool@hotmail.com> + */ +public class DisableStageTest extends BaseTestCase { + /** + * Tests that the simulation results are correct when a single stage is deactivated and re-activated. + */ + @Test + public void testSingleStage() throws SimulationException { + //// Test disabling the stage + Rocket rocket = TestRockets.makeEstesAlphaIII(); + + Simulation simDisabled = new Simulation(rocket); + simDisabled.setFlightConfigurationId(TestRockets.TEST_FCID_0); + simDisabled.getActiveConfiguration()._setStageActive(0, false); + simDisabled.getOptions().setISAAtmosphere(true); + simDisabled.getOptions().setTimeStep(0.05); + + SimulationListener simulationListener = new AbstractSimulationListener(); + + // Since there are no stages, the simulation should throw an exception. + try { + simDisabled.simulate(simulationListener); + } catch (SimulationException e) { + if (!(e instanceof MotorIgnitionException)) { + Assert.fail("Simulation should have thrown a MotorIgnitionException"); + } + } + + //// Test re-enableing the stage. + Rocket rocketOriginal = TestRockets.makeEstesAlphaIII(); + + Simulation simOriginal = new Simulation(rocketOriginal); + simOriginal.setFlightConfigurationId(TestRockets.TEST_FCID_0); + simOriginal.getOptions().setISAAtmosphere(true); + simOriginal.getOptions().setTimeStep(0.05); + + simDisabled.getActiveConfiguration().setAllStages(); // Re-enable all stages. + + double delta = 0.05; // 5 % error margin (simulations are not exact) + compareSims(simOriginal, simDisabled, simulationListener, delta); + } + + /** + * Tests that the simulation results are correct when the last stage of a multi-stage rocket is deactivated and re-activated. + */ + @Test + public void testMultiStageLastDisabled() { + //// Test disabling the stage + Rocket rocketRemoved = TestRockets.makeBeta(); // Rocket with the last stage removed + Rocket rocketDisabled = TestRockets.makeBeta(); // Rocket with the last stage disabled + + int stageNr = rocketRemoved.getChildCount() - 1; + rocketRemoved.removeChild(stageNr); + FlightConfiguration fc = rocketDisabled.getFlightConfiguration(TestRockets.TEST_FCID_1); + fc._setStageActive(stageNr, false); + + Simulation simRemoved = new Simulation(rocketRemoved); + simRemoved.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simRemoved.getOptions().setISAAtmosphere(true); + simRemoved.getOptions().setTimeStep(0.05); + + Simulation simDisabled = new Simulation(rocketDisabled); + simDisabled.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simDisabled.getOptions().setISAAtmosphere(true); + simDisabled.getOptions().setTimeStep(0.05); + + 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. + Rocket rocketOriginal = TestRockets.makeBeta(); + Simulation simOriginal = new Simulation(rocketOriginal); + simOriginal.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simOriginal.getOptions().setISAAtmosphere(true); + simOriginal.getOptions().setTimeStep(0.05); + + simDisabled.getActiveConfiguration().setAllStages(); + + compareSims(simOriginal, simDisabled, simulationListener, delta); + } + + /** + * Tests that the simulation results are correct when the first stage of a multi-stage rocket is deactivated and re-activated. + */ + // Don't even know if this test was useful, but simulation results vary wildly because the first stage is disabled, + // so I'm just gonna ignore this test. + /*@Test + public void testMultiStageFirstDisabled() { + //// Test disabling the stage + Rocket rocketRemoved = TestRockets.makeBeta(); // Rocket with the last stage removed + Rocket rocketDisabled = TestRockets.makeBeta(); // Rocket with the last stage disabled + + // You need to disable the second stage body tube going into automatic radius mode, otherwise the + // removed and disabled rocket will have different results (removed rocket will have a different diameter) + BodyTube bodyTube = (BodyTube) rocketRemoved.getChild(1).getChild(0); + bodyTube.setOuterRadiusAutomatic(false); + + + int stageNr = 0; + rocketRemoved.removeChild(stageNr); + FlightConfiguration fc = rocketDisabled.getFlightConfiguration(TestRockets.TEST_FCID_1); + fc._setStageActive(stageNr, false); + + Simulation simRemoved = new Simulation(rocketRemoved); + simRemoved.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simRemoved.getOptions().setISAAtmosphere(true); + simRemoved.getOptions().setTimeStep(0.05); + + Simulation simDisabled = new Simulation(rocketDisabled); + simDisabled.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simDisabled.getOptions().setISAAtmosphere(true); + simDisabled.getOptions().setTimeStep(0.05); + + SimulationListener simulationListener = new AbstractSimulationListener(); + + double delta = 0.1; // 10 % error margin (simulations are very unstable and not exact when the first stage is disabled...) + compareSims(simRemoved, simDisabled, simulationListener, delta); + + //// Test re-enableing the stage. + Rocket rocketOriginal = TestRockets.makeBeta(); + Simulation simOriginal = new Simulation(rocketOriginal); + simOriginal.setFlightConfigurationId(TestRockets.TEST_FCID_1); + simOriginal.getOptions().setISAAtmosphere(true); + simOriginal.getOptions().setTimeStep(0.05); + + simDisabled.getActiveConfiguration().setAllStages(); + + compareSims(simOriginal, simDisabled, simulationListener, delta); + }*/ + + /** + * Tests that the simulation results are correct when a booster stage is deactivated and re-activated. + */ + @Test + public void testBooster1() { + //// Test disabling the stage + Rocket rocketRemoved = TestRockets.makeFalcon9Heavy(); // Rocket with the last stage removed + Rocket rocketDisabled = TestRockets.makeFalcon9Heavy(); // Rocket with the last stage disabled + + FlightConfigurationId fcid = new FlightConfigurationId(TestRockets.FALCON_9H_FCID_1); + int stageNr = 2; // Stage 2 is the Parallel Booster Stage + rocketRemoved.getChild(1).getChild(0).removeChild(0); // Remove the Parallel Booster Stage + FlightConfiguration fc = rocketDisabled.getFlightConfiguration(fcid); + fc._setStageActive(stageNr, false); + + Simulation simRemoved = new Simulation(rocketRemoved); + simRemoved.setFlightConfigurationId(fcid); + simRemoved.getOptions().setISAAtmosphere(true); + simRemoved.getOptions().setTimeStep(0.05); + + Simulation simDisabled = new Simulation(rocketDisabled); + simDisabled.setFlightConfigurationId(fcid); + simDisabled.getOptions().setISAAtmosphere(true); + simDisabled.getOptions().setTimeStep(0.05); + + 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. + Rocket rocketOriginal = TestRockets.makeFalcon9Heavy(); + Simulation simOriginal = new Simulation(rocketOriginal); + simOriginal.setFlightConfigurationId(fcid); + simOriginal.getOptions().setISAAtmosphere(true); + simOriginal.getOptions().setTimeStep(0.05); + + simDisabled.getActiveConfiguration().setAllStages(); + + compareSims(simOriginal, simDisabled, simulationListener, delta); + } + + /** + * Tests that the simulation results are correct when the parent stage of a booster stage is deactivated and re-activated. + */ + @Test + public void testBooster2() { + //// Test disabling the stage + Rocket rocketRemoved = TestRockets.makeFalcon9Heavy(); // Rocket with the last stage removed + Rocket rocketDisabled = TestRockets.makeFalcon9Heavy(); // Rocket with the last stage disabled + + FlightConfigurationId fid = new FlightConfigurationId(TestRockets.FALCON_9H_FCID_1); + int stageNr = 1; // Stage 1 is the Parallel Booster Stage's parent stage + rocketRemoved.getChild(1).removeChild(0); // Remove the Parallel Booster Stage's parent stage + FlightConfiguration fc = rocketDisabled.getFlightConfiguration(fid); + fc._setStageActive(stageNr, false); + + Simulation simRemoved = new Simulation(rocketRemoved); + simRemoved.setFlightConfigurationId(fid); + simRemoved.getOptions().setISAAtmosphere(true); + simRemoved.getOptions().setTimeStep(0.05); + + Simulation simDisabled = new Simulation(rocketDisabled); + simDisabled.setFlightConfigurationId(fid); + simDisabled.getOptions().setISAAtmosphere(true); + simDisabled.getOptions().setTimeStep(0.05); + + SimulationListener simulationListener = new AbstractSimulationListener(); + + // There should be no motors left at this point, so a no motors exception should be thrown + try { + simRemoved.simulate(simulationListener); + } catch (SimulationException e) { + if (!(e instanceof MotorIgnitionException)) { + Assert.fail("Simulation failed: " + e); + } + } + + try { + simDisabled.simulate(simulationListener); + } catch (SimulationException e) { + if (!(e instanceof MotorIgnitionException)) { + Assert.fail("Simulation failed: " + e); + } + } + + //// Test re-enableing the stage. + Rocket rocketOriginal = TestRockets.makeFalcon9Heavy(); + Simulation simOriginal = new Simulation(rocketOriginal); + simOriginal.setFlightConfigurationId(fid); + simOriginal.getOptions().setISAAtmosphere(true); + simOriginal.getOptions().setTimeStep(0.05); + + simDisabled.getActiveConfiguration().setAllStages(); + + double delta = 0.05; // 5 % error margin (simulations are not exact) + compareSims(simOriginal, simDisabled, simulationListener, delta); + } + + /** + * Compare simActual to simExpected and fail the unit test if there was an error during simulation or + * the two don't match. + * Tested parameters: + * - maxAcceleration + * - maxAltitude + * - maxVelocity + * - maxMachNumber + * - flightTime + * - launchRodVelocity + * - deploymentVelocity + * - groundHitVelocity + * @param simExpected the expected simulation results + * @param simActual the actual simulation results + * @param simulationListener the simulation listener to use for the comparison + * @param delta the error margin for the comparison (e.g. 0.05 = 5 % error margin) + */ + private void compareSims(Simulation simExpected, Simulation simActual, + SimulationListener simulationListener, double delta) { + try { + simExpected.simulate(simulationListener); + double maxAccelerationOriginal = simExpected.getSimulatedData().getMaxAcceleration(); + double maxAltitudeOriginal = simExpected.getSimulatedData().getMaxAltitude(); + double maxVelocityOriginal = simExpected.getSimulatedData().getMaxVelocity(); + double maxMachNumberOriginal = simExpected.getSimulatedData().getMaxMachNumber(); + double flightTimeOriginal = simExpected.getSimulatedData().getFlightTime(); + double timeToApogeeOriginal = simExpected.getSimulatedData().getTimeToApogee(); + double launchRodVelocityOriginal = simExpected.getSimulatedData().getLaunchRodVelocity(); + double deploymentVelocityOriginal = simExpected.getSimulatedData().getDeploymentVelocity(); + double groundHitVelocityOriginal = simExpected.getSimulatedData().getGroundHitVelocity(); + + simActual.simulate(simulationListener); + double maxAccelerationDisabled = simActual.getSimulatedData().getMaxAcceleration(); + double maxAltitudeDisabled = simActual.getSimulatedData().getMaxAltitude(); + double maxVelocityDisabled = simActual.getSimulatedData().getMaxVelocity(); + double maxMachNumberDisabled = simActual.getSimulatedData().getMaxMachNumber(); + double flightTimeDisabled = simActual.getSimulatedData().getFlightTime(); + double timeToApogeeDisabled = simActual.getSimulatedData().getTimeToApogee(); + double launchRodVelocityDisabled = simActual.getSimulatedData().getLaunchRodVelocity(); + double deploymentVelocityDisabled = simActual.getSimulatedData().getDeploymentVelocity(); + double groundHitVelocityDisabled = simActual.getSimulatedData().getGroundHitVelocity(); + + Assert.assertEquals(maxAccelerationOriginal, maxAccelerationDisabled, maxAccelerationOriginal * delta); + Assert.assertEquals(maxAltitudeOriginal, maxAltitudeDisabled, maxAltitudeOriginal * delta); + Assert.assertEquals(maxVelocityOriginal, maxVelocityDisabled, maxVelocityOriginal * delta); + Assert.assertEquals(maxMachNumberOriginal, maxMachNumberDisabled, maxMachNumberOriginal * delta); + Assert.assertEquals(flightTimeOriginal, flightTimeDisabled, flightTimeOriginal * delta); + Assert.assertEquals(timeToApogeeOriginal, timeToApogeeDisabled, timeToApogeeOriginal * delta); + Assert.assertEquals(launchRodVelocityOriginal, launchRodVelocityDisabled, launchRodVelocityOriginal * delta); + Assert.assertEquals(deploymentVelocityOriginal, deploymentVelocityDisabled, deploymentVelocityOriginal * delta); + Assert.assertEquals(groundHitVelocityOriginal, groundHitVelocityDisabled, groundHitVelocityOriginal * delta); + } catch (SimulationException e) { + Assert.fail("Simulation failed: " + e); + } + } +} diff --git a/swing/.classpath b/swing/.classpath index 0a33deaf2..7daea48ea 100644 --- a/swing/.classpath +++ b/swing/.classpath @@ -17,6 +17,8 @@ <classpathentry kind="lib" path="/OpenRocket Core/lib/aopalliance.jar"/> <classpathentry kind="lib" path="/OpenRocket Core/lib/commonmark-0.19.0.jar"/> <classpathentry kind="lib" path="/OpenRocket Core/lib/javax.inject.jar"/> + <classpathentry kind="lib" path="/OpenRocket Core/lib/javax.json-1.1.3.jar"/> + <classpathentry kind="lib" path="/OpenRocket Core/lib/javax.json-api-1.1.3.jar"/> <classpathentry kind="lib" path="/OpenRocket Core/resources"/> <classpathentry kind="lib" path="resources"/> <classpathentry kind="lib" path="lib/miglayout-4.0-swing.jar" sourcepath="reference/miglayout-4.0-sources.jar"/> diff --git a/swing/build.xml b/swing/build.xml index 6333164ad..578f0994c 100644 --- a/swing/build.xml +++ b/swing/build.xml @@ -154,7 +154,7 @@ depends="build"> <for param="vendor-dir"> - <dirset dir="${resources-src.dir}/datafiles/rocksim_components" + <dirset dir="${resources-src.dir}/datafiles/rocksim_src" includes="*"/> <sequential> <propertyregex property="vendor" diff --git a/swing/src/net/sf/openrocket/gui/SpinnerEditor.java b/swing/src/net/sf/openrocket/gui/SpinnerEditor.java index be4b79525..d02997e3d 100644 --- a/swing/src/net/sf/openrocket/gui/SpinnerEditor.java +++ b/swing/src/net/sf/openrocket/gui/SpinnerEditor.java @@ -1,8 +1,14 @@ package net.sf.openrocket.gui; import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import javax.swing.text.DefaultFormatter; import javax.swing.text.DefaultFormatterFactory; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; /** * Editable editor for a JSpinner. Simply uses JSpinner.DefaultEditor, which has been made @@ -22,6 +28,59 @@ public class SpinnerEditor extends JSpinner.DefaultEditor { DefaultFormatterFactory dff = (DefaultFormatterFactory) getTextField().getFormatterFactory(); DefaultFormatter formatter = (DefaultFormatter) dff.getDefaultFormatter(); formatter.setOverwriteMode(false); + + + // Add listeners to select all the text when the field is focussed + { + getTextField().addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + selectAllText(); + } + + @Override + public void focusLost(FocusEvent e) { + } + }); + + getTextField().addMouseListener(new MouseListener() { + private boolean isFocussed = false; // Checks whether the text field was focussed when it was clicked upon + + @Override + public void mouseClicked(MouseEvent e) { + // If the text field was focussed when it was clicked upon instead of e.g. tab-switching to gain focus, + // then the select all action from the focus listener is ignored (it is replaced by a cursor-click event). + // So if we detect such a focus change, then redo the select all action. + if (!isFocussed) { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + JTextField tf = (JTextField) e.getSource(); + tf.selectAll(); + } + }); + } + } + + @Override + public void mousePressed(MouseEvent e) { + JTextField tf = (JTextField) e.getSource(); + isFocussed = tf.hasFocus(); + } + + @Override + public void mouseReleased(MouseEvent e) { + } + + @Override + public void mouseEntered(MouseEvent e) { + } + + @Override + public void mouseExited(MouseEvent e) { + } + }); + } } /** @@ -33,5 +92,17 @@ public class SpinnerEditor extends JSpinner.DefaultEditor { this(spinner); getTextField().setColumns(cols); } + + /** + * Highlights all the text in the text field. + */ + private void selectAllText() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + getTextField().selectAll(); + } + }); + } } diff --git a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java index 5fb2355d7..2fcec306e 100644 --- a/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java +++ b/swing/src/net/sf/openrocket/gui/adaptors/PresetModel.java @@ -5,16 +5,14 @@ import java.util.List; import javax.swing.AbstractListModel; import javax.swing.ComboBoxModel; -import javax.swing.SwingUtilities; -import net.sf.openrocket.database.ComponentPresetDatabase; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import net.sf.openrocket.gui.configdialog.RocketComponentConfig; import net.sf.openrocket.database.Database; import net.sf.openrocket.database.DatabaseListener; import net.sf.openrocket.document.OpenRocketDocument; -import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.logging.Markers; import net.sf.openrocket.preset.ComponentPreset; @@ -29,8 +27,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com private static final Logger log = LoggerFactory.getLogger(PresetModel.class); private static final Translator trans = Application.getTranslator(); - private static final String NONE_SELECTED = trans.get("lbl.select"); - private static final String SELECT_DATABASE = trans.get("lbl.database"); + private static final String NONE_SELECTED = String.format("<html><i>%s</i></html>", trans.get("PresetModel.lbl.custompreset")); private final Component parent; private final RocketComponent component; @@ -50,7 +47,7 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com @Override public int getSize() { - return presets.size() + 2; + return presets.size() + 1; } @Override @@ -58,9 +55,6 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com if (index == 0) { return NONE_SELECTED; } - if (index == getSize() - 1) { - return SELECT_DATABASE; - } return presets.get(index - 1); } @@ -72,24 +66,10 @@ public class PresetModel extends AbstractListModel implements ComboBoxModel, Com throw new BugException("item is null"); } else if (item.equals(NONE_SELECTED)) { component.clearPreset(); - } else if (item.equals(SELECT_DATABASE)) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(PresetModel.this); - ComponentPresetChooserDialog dialog = - new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(parent), component); - dialog.setVisible(true); - ComponentPreset preset = dialog.getSelectedComponentPreset(); - if (preset != null) { - setSelectedItem(preset); - } - ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(PresetModel.this); - } - }); } else { document.addUndoPosition("Use Preset " + component.getComponentName()); component.loadPreset((ComponentPreset) item); + ((RocketComponentConfig) parent).setFocusElement(); } } diff --git a/swing/src/net/sf/openrocket/gui/components/ConfigurationComboBox.java b/swing/src/net/sf/openrocket/gui/components/ConfigurationComboBox.java index 4785570c2..43fc172a1 100644 --- a/swing/src/net/sf/openrocket/gui/components/ConfigurationComboBox.java +++ b/swing/src/net/sf/openrocket/gui/components/ConfigurationComboBox.java @@ -24,21 +24,49 @@ public class ConfigurationComboBox extends JComboBox<FlightConfiguration> implem public class ConfigurationModel implements MutableComboBoxModel<FlightConfiguration> { private final Rocket rkt; - - public ConfigurationModel(final Rocket _rkt) { + private FlightConfiguration selectedConfig; + private final boolean updateRocketConfig; + private final ConfigurationModel listener; + + /** + * @param _rkt the rocket to get the configurations from and to (optionally) change the rocket's selected configuration + * @param _updateRocketConfig whether to update the rocket's selected configuration based on the selected combo box item, + * or just change the combo box item without altering the rocket's configuration. + * @param listener model that should change its selected item to this model's selected item + */ + public ConfigurationModel(final Rocket _rkt, boolean _updateRocketConfig, ConfigurationModel listener) { this.rkt = _rkt; + this.updateRocketConfig = _updateRocketConfig; + this.selectedConfig = this.rkt.getSelectedConfiguration(); + this.listener = listener; + } + + public ConfigurationModel(final Rocket _rkt, boolean _updateRocketConfig) { + this(_rkt, _updateRocketConfig, null); } @Override public FlightConfiguration getSelectedItem() { - return rkt.getSelectedConfiguration(); + if (updateRocketConfig) { + return rkt.getSelectedConfiguration(); + } else { + return selectedConfig; + } } @Override public void setSelectedItem(Object nextItem) { if( nextItem instanceof FlightConfiguration ){ FlightConfigurationId selectedId = ((FlightConfiguration)nextItem).getId(); - rkt.setSelectedConfiguration(selectedId); + if (updateRocketConfig) { + rkt.setSelectedConfiguration(selectedId); + } else { + selectedConfig = rkt.getFlightConfiguration(selectedId); + } + + if (listener != null) { + listener.setSelectedItem(nextItem); + } } } @@ -79,9 +107,15 @@ public class ConfigurationComboBox extends JComboBox<FlightConfiguration> implem private final Rocket rkt; - public ConfigurationComboBox(Rocket _rkt) { + /** + * @param _rkt the rocket to get the configurations from and to (optionally) change the rocket's selected configuration + * @param _updateRocketConfig whether to update the rocket's selected configuration based on the selected combo box item, + * or just change the combo box item without altering the rocket's configuration. + */ + public ConfigurationComboBox(Rocket _rkt, boolean _updateRocketConfig) { rkt = _rkt; - setModel(new ConfigurationModel(rkt)); + final ConfigurationModel model = new ConfigurationModel(rkt, _updateRocketConfig); + setModel(model); rkt.addChangeListener(this); addPopupMenuListener(new PopupMenuListener() { @@ -89,12 +123,18 @@ public class ConfigurationComboBox extends JComboBox<FlightConfiguration> implem public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {} public void popupMenuWillBecomeVisible(PopupMenuEvent e) { - setModel(new ConfigurationModel(rkt)); + final ConfigurationModel model2 = new ConfigurationModel(rkt, _updateRocketConfig, model); + model2.setSelectedItem(model.getSelectedItem()); + setModel(model2); } }); } + + public ConfigurationComboBox(Rocket _rkt) { + this(_rkt, true); + } @Override public void stateChanged(EventObject e) { diff --git a/swing/src/net/sf/openrocket/gui/configdialog/BodyTubeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/BodyTubeConfig.java index 9ab43f596..39fbaa2ba 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/BodyTubeConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/BodyTubeConfig.java @@ -6,6 +6,8 @@ import javax.swing.JDialog; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSpinner; +import javax.swing.JTextField; +import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.document.OpenRocketDocument; @@ -23,6 +25,12 @@ import net.sf.openrocket.rocketcomponent.SymmetricComponent; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.MouseEvent; +import java.awt.event.MouseListener; +import java.util.Arrays; + @SuppressWarnings("serial") public class BodyTubeConfig extends RocketComponentConfig { @@ -43,6 +51,7 @@ public class BodyTubeConfig extends RocketComponentConfig { JSpinner spin = new JSpinner(length.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(length), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java index 3566cf3e9..bd15b1b90 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ComponentConfigDialog.java @@ -1,18 +1,24 @@ package net.sf.openrocket.gui.configdialog; +import java.awt.GraphicsDevice; +import java.awt.GraphicsEnvironment; +import java.awt.Rectangle; import java.awt.Window; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; +import java.util.Arrays; import java.util.List; import javax.swing.JDialog; import net.sf.openrocket.document.OpenRocketDocument; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.util.SwingPreferences; +import net.sf.openrocket.gui.util.WindowLocationUtil; import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; @@ -230,8 +236,9 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis * @param document the document to configure. * @param component the component to configure. * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog + * @param includeUndoModify if true, include a 'Modify component' undo action */ - public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab, boolean includeUndoModify) { if (dialog != null) { // Don't remember the previous tab for rockets or stages, because this will leave you in the override tab for // the next component, which is generally not what you want. @@ -256,15 +263,32 @@ public class ComponentConfigDialog extends JDialog implements ComponentChangeLis dialog = new ComponentConfigDialog(parent, document, component); dialog.setVisible(true); + if (parent instanceof BasicFrame && BasicFrame.getStartupFrame() == parent) { + WindowLocationUtil.moveIfOutsideOfParentMonitor(dialog, parent); + } ////Modify - if (component.getConfigListeners().size() == 0) { - document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); - } else { - document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents")); + if (includeUndoModify) { + if (component.getConfigListeners().size() == 0) { + document.addUndoPosition(trans.get("ComponentCfgDlg.Modify") + " " + component.getComponentName()); + } else { + document.addUndoPosition(trans.get("ComponentCfgDlg.ModifyComponents")); + } } } + /** + * A singleton configuration dialog. Will create and show a new dialog if one has not + * previously been used, or update the dialog and show it if a previous one exists. + * + * @param document the document to configure. + * @param component the component to configure. + * @param rememberPreviousTab if true, the previous tab will be remembered and used for the new dialog + */ + public static void showDialog(Window parent, OpenRocketDocument document, RocketComponent component, boolean rememberPreviousTab) { + ComponentConfigDialog.showDialog(parent, document, component, rememberPreviousTab, true); + } + /** * A singleton configuration dialog. Will create and show a new dialog if one has not * previously been used, or update the dialog and show it if a previous one exists. diff --git a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java index ba6b077da..b695e5dbf 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/FreeformFinSetConfig.java @@ -81,6 +81,7 @@ public class FreeformFinSetConfig extends FinSetConfig { private Point dragPoint = null; private FinPointFigure figure = null; + private ScaleScrollPane figurePane = null; private ScaleSelector selector; public FreeformFinSetConfig(OpenRocketDocument d, RocketComponent component, JDialog parent) { @@ -219,7 +220,7 @@ public class FreeformFinSetConfig extends FinSetConfig { // Create the figure figure = new FinPointFigure(finset); - ScaleScrollPane figurePane = new FinPointScrollPane( figure); + figurePane = new FinPointScrollPane( figure); // Create the table tableModel = new FinPointTableModel(); @@ -392,9 +393,10 @@ public class FreeformFinSetConfig extends FinSetConfig { } figure.updateFigure(); } - - revalidate(); - repaint(); + + if (figurePane != null) { + figurePane.revalidate(); + } } private class FinPointScrollPane extends ScaleScrollPane { @@ -408,8 +410,6 @@ public class FreeformFinSetConfig extends FinSetConfig { @Override public void mousePressed(MouseEvent event) { - int mods = event.getModifiersEx(); - final FreeformFinSet finset = (FreeformFinSet) component; final int pressIndex = getPoint(event); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java index e5595286f..3e3ddb3cb 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/InnerTubeConfig.java @@ -64,7 +64,6 @@ public class InnerTubeConfig extends RocketComponentConfig { super(d, c, parent); //// General and General properties - JPanel rightPanel = new JPanel(new MigLayout()); JPanel panel = new JPanel(new MigLayout("gap rel unrel", "[][65lp::][30lp::][]", "")); DoubleModel m; @@ -86,7 +85,7 @@ public class InnerTubeConfig extends RocketComponentConfig { panel.add(spin, "growx"); panel.add(new UnitSelector(od), "growx"); - panel.add(new BasicSlider(od.getSliderModel(0, 0.04, 0.2)), "w 100lp, wrap"); + panel.add(new BasicSlider(od.getSliderModel(0, 0.04, 0.2)), "wmin 100lp, growx, wrap"); if (od.isAutomaticAvailable()) { JCheckBox check = new JCheckBox(od.getAutomaticAction()); @@ -106,7 +105,7 @@ public class InnerTubeConfig extends RocketComponentConfig { panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); - panel.add(new BasicSlider(m.getSliderModel(new DoubleModel(0), od)), "w 100lp, wrap"); + panel.add(new BasicSlider(m.getSliderModel(new DoubleModel(0), od)), "wmin 100lp, growx, wrap"); if (m.isAutomaticAvailable()) { JCheckBox check = new JCheckBox(m.getAutomaticAction()); @@ -127,7 +126,7 @@ public class InnerTubeConfig extends RocketComponentConfig { panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); - panel.add(new BasicSlider(m.getSliderModel(0, 0.01)), "w 100lp, wrap"); + panel.add(new BasicSlider(m.getSliderModel(0, 0.01)), "wmin 100lp, growx, wrap"); //// Inner tube length @@ -138,14 +137,15 @@ public class InnerTubeConfig extends RocketComponentConfig { spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); - panel.add(new BasicSlider(m.getSliderModel(0, 0.1, 1.0)), "w 100lp, wrap"); + panel.add(new BasicSlider(m.getSliderModel(0, 0.1, 1.0)), "wmin 100lp, growx, wrap"); //// Material panel.add(materialPanel(Material.Type.BULK), - "spanx 3, growx, wrap 15lp"); + "spanx 4, growx, wrap 15lp"); //// Right side of panel ---- @@ -174,7 +174,7 @@ public class InnerTubeConfig extends RocketComponentConfig { panel2.add(new BasicSlider(m.getSliderModel( new DoubleModel(component.getParent(), "Length", -1.0, UnitGroup.UNITS_NONE), new DoubleModel(component.getParent(), "Length"))), - "w 100lp, wrap"); + "wmin 100lp, growx, wrap"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/LaunchLugConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/LaunchLugConfig.java index eedab2730..8302641bb 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/LaunchLugConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/LaunchLugConfig.java @@ -45,6 +45,7 @@ public class LaunchLugConfig extends RocketComponentConfig { JSpinner spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/MassComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/MassComponentConfig.java index 5e7d185ec..23102e1f9 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/MassComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/MassComponentConfig.java @@ -138,6 +138,7 @@ public class MassComponentConfig extends RocketComponentConfig { m = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel2.add(spin, "growx"); panel2.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java index f63abcc16..38fdffc3b 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ParachuteConfig.java @@ -163,6 +163,7 @@ public class ParachuteConfig extends RecoveryDeviceConfig { m = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java index 3edfa1e89..b5e005a4a 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RailButtonConfig.java @@ -92,6 +92,7 @@ public class RailButtonConfig extends RocketComponentConfig { DoubleModel offsetModel = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); JSpinner offsetSpinner = new JSpinner(offsetModel.getSpinnerModel()); offsetSpinner.setEditor(new SpinnerEditor(offsetSpinner)); + focusElement = offsetSpinner; panel.add(offsetSpinner, "growx"); panel.add(new UnitSelector(offsetModel), "growx"); panel.add(new BasicSlider(offsetModel.getSliderModel( diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RingComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RingComponentConfig.java index f10ea3caa..27c2a4282 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RingComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RingComponentConfig.java @@ -20,6 +20,7 @@ import net.sf.openrocket.l10n.Translator; import net.sf.openrocket.material.Material; import net.sf.openrocket.rocketcomponent.EngineBlock; import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.rocketcomponent.ThicknessRingComponent; import net.sf.openrocket.rocketcomponent.position.AxialMethod; import net.sf.openrocket.startup.Application; import net.sf.openrocket.unit.UnitGroup; @@ -116,6 +117,9 @@ public class RingComponentConfig extends RocketComponentConfig { spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + if (component instanceof ThicknessRingComponent) { + focusElement = spin; + } panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); @@ -139,6 +143,9 @@ public class RingComponentConfig extends RocketComponentConfig { m = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + if (!(component instanceof ThicknessRingComponent)) { + focusElement = spin; + } panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java index 976e2945a..59628c5ac 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java @@ -22,8 +22,10 @@ import javax.swing.JSpinner; import javax.swing.JTabbedPane; import javax.swing.JTextArea; import javax.swing.JTextField; +import javax.swing.SwingUtilities; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.database.ComponentPresetDatabase; import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.BooleanModel; @@ -36,6 +38,7 @@ import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.StyledLabel; import net.sf.openrocket.gui.components.StyledLabel.Style; import net.sf.openrocket.gui.components.UnitSelector; +import net.sf.openrocket.gui.dialogs.preset.ComponentPresetChooserDialog; import net.sf.openrocket.gui.util.GUIUtil; import net.sf.openrocket.gui.widgets.SelectColorButton; import net.sf.openrocket.l10n.Translator; @@ -61,6 +64,7 @@ public class RocketComponentConfig extends JPanel { private JComboBox<?> presetComboBox; private PresetModel presetModel; + protected Component focusElement = null; // Element that will be focused on after a preset is selected protected final JTextField componentNameField; protected JTextArea commentTextArea; @@ -100,7 +104,7 @@ public class RocketComponentConfig extends JPanel { //// Component name: JLabel label = new JLabel(trans.get("RocketCompCfg.lbl.Componentname")); //// The component name. - label.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); + label.setToolTipText(trans.get("RocketCompCfg.lbl.Componentname.ttip")); this.add(label, "spanx, height 32!, split"); componentNameField = new JTextField(15); @@ -108,15 +112,27 @@ public class RocketComponentConfig extends JPanel { componentNameField.addActionListener(textFieldListener); componentNameField.addFocusListener(textFieldListener); //// The component name. - componentNameField.setToolTipText(trans.get("RocketCompCfg.ttip.Thecomponentname")); + componentNameField.setToolTipText(trans.get("RocketCompCfg.lbl.Componentname.ttip")); this.add(componentNameField, "growx"); if (allSameType && component.getPresetType() != null) { // If the component supports a preset, show the preset selection box. presetModel = new PresetModel(this, document, component); presetComboBox = new JComboBox(presetModel); + presetComboBox.setMaximumRowCount(25); presetComboBox.setEditable(false); - this.add(presetComboBox, ""); + presetComboBox.setToolTipText(trans.get("PresetModel.combo.ttip")); + this.add(presetComboBox, "growx 110"); + + final JButton selectPreset = new SelectColorButton(trans.get("PresetModel.lbl.partsLib")); + selectPreset.setToolTipText(trans.get("PresetModel.lbl.partsLib.ttip")); + selectPreset.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + selectPreset(); + } + }); + this.add(selectPreset); } tabbedPane = new JTabbedPane(); @@ -124,17 +140,17 @@ public class RocketComponentConfig extends JPanel { //// Override and Mass and CG override options tabbedPane.addTab(trans.get("RocketCompCfg.tab.Override"), null, overrideTab(), - trans.get("RocketCompCfg.tab.MassandCGoverride")); + trans.get("RocketCompCfg.tab.Override.ttip")); if (allMassive) { //// Appearance options appearancePanel = new AppearancePanel(document, component, parent); tabbedPane.addTab(trans.get("RocketCompCfg.tab.Appearance"), null, appearancePanel, - "Appearance Tool Tip"); + trans.get("RocketCompCfg.tab.Appearance.ttip")); } //// Comment and Specify a comment for the component tabbedPane.addTab(trans.get("RocketCompCfg.tab.Comment"), null, commentTab(), - trans.get("RocketCompCfg.tab.Specifyacomment")); + trans.get("RocketCompCfg.tab.Comment.ttip")); addButtons(); @@ -241,6 +257,24 @@ public class RocketComponentConfig extends JPanel { } } + private void selectPreset() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + if (presetComboBox == null || presetModel == null) return; + ((ComponentPresetDatabase) Application.getComponentPresetDao()).addDatabaseListener(presetModel); + ComponentPresetChooserDialog dialog = + new ComponentPresetChooserDialog(SwingUtilities.getWindowAncestor(RocketComponentConfig.this), component); + dialog.setVisible(true); + ComponentPreset preset = dialog.getSelectedComponentPreset(); + if (preset != null) { + presetModel.setSelectedItem(preset); + } + ((ComponentPresetDatabase) Application.getComponentPresetDao()).removeChangeListener(presetModel); + } + }); + } + public void clearConfigListeners() { if (appearancePanel != null) { appearancePanel.clearConfigListeners(); @@ -702,6 +736,24 @@ public class RocketComponentConfig extends JPanel { } } } + + /** + * Requests focus for the focus element that should be active after a preset is selected. + */ + public void setFocusElement() { + if (focusElement != null) { + SwingUtilities.invokeLater(new Runnable() { + public void run() { + if (focusElement instanceof JSpinner) { + SpinnerEditor ed = (SpinnerEditor) ((JSpinner)focusElement).getEditor(); + ed.getTextField().requestFocusInWindow(); + } else { + focusElement.requestFocusInWindow(); + } + } + }); + } + } protected void register(Invalidatable model) { diff --git a/swing/src/net/sf/openrocket/gui/configdialog/ShockCordConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/ShockCordConfig.java index 624738a95..a7a6e8a78 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/ShockCordConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/ShockCordConfig.java @@ -80,6 +80,7 @@ public class ShockCordConfig extends RocketComponentConfig { m = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel2.add(spin, "growx"); panel2.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java index c4f6e2d65..504c6e712 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/StreamerConfig.java @@ -149,6 +149,7 @@ public class StreamerConfig extends RecoveryDeviceConfig { m = new DoubleModel(component, "AxialOffset", UnitGroup.UNITS_LENGTH); spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/configdialog/TubeFinSetConfig.java b/swing/src/net/sf/openrocket/gui/configdialog/TubeFinSetConfig.java index a005d11e2..e9de11352 100644 --- a/swing/src/net/sf/openrocket/gui/configdialog/TubeFinSetConfig.java +++ b/swing/src/net/sf/openrocket/gui/configdialog/TubeFinSetConfig.java @@ -54,6 +54,7 @@ public class TubeFinSetConfig extends RocketComponentConfig { spin = new JSpinner(m.getSpinnerModel()); spin.setEditor(new SpinnerEditor(spin)); + focusElement = spin; panel.add(spin, "growx"); panel.add(new UnitSelector(m), "growx"); diff --git a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java index ad53576e6..3a1db6af8 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/ScaleDialog.java @@ -346,7 +346,7 @@ public class ScaleDialog extends JDialog { panel.add(selectionOption, "growx, wrap para*2"); // Select the 'scale component / scale selection and all subcomponents' if a component is selected - if (selection != null && selection.size() > 0) { + if (options.size() > 1 && selection != null && selection.size() > 0) { boolean entireRocket = false; // Flag to scale entire rocket for (RocketComponent component : selection) { if (component instanceof Rocket || (component instanceof AxialStage && !(component instanceof ParallelStage))) { diff --git a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java index 08ef4acb1..3a98bd616 100644 --- a/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java +++ b/swing/src/net/sf/openrocket/gui/dialogs/preferences/DesignPreferencesPanel.java @@ -11,6 +11,7 @@ import javax.swing.JSpinner; import net.miginfocom.swing.MigLayout; import net.sf.openrocket.gui.SpinnerEditor; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.main.BasicFrame; import net.sf.openrocket.startup.Preferences; import net.sf.openrocket.unit.UnitGroup; @@ -93,7 +94,6 @@ public class DesignPreferencesPanel extends PreferencesPanel { // // Always open leftmost tab when opening a component edit dialog final JCheckBox alwaysOpenLeftmostTab = new JCheckBox( trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost")); - alwaysOpenLeftmostTab.setSelected(preferences.isAlwaysOpenLeftmostTab()); alwaysOpenLeftmostTab.setToolTipText(trans.get("pref.dlg.checkbox.AlwaysOpenLeftmost.ttip")); alwaysOpenLeftmostTab.addActionListener(new ActionListener() { @@ -103,7 +103,7 @@ public class DesignPreferencesPanel extends PreferencesPanel { .isSelected()); } }); - this.add(alwaysOpenLeftmostTab, "wrap, growx, span 2"); + this.add(alwaysOpenLeftmostTab, "wrap, growx, spanx"); // // Update flight estimates in the design window final JCheckBox updateEstimates = new JCheckBox( @@ -117,5 +117,23 @@ public class DesignPreferencesPanel extends PreferencesPanel { } }); this.add(updateEstimates, "wrap, growx, sg combos "); + + // // Only show pod set/booster markers when they are selected + final JCheckBox showMarkers = new JCheckBox( + trans.get("pref.dlg.checkbox.Markers")); + showMarkers.setToolTipText(trans.get("pref.dlg.checkbox.Markers.ttip")); + showMarkers.setSelected(preferences.isShowMarkers()); + showMarkers.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + preferences.setShowMarkers(showMarkers + .isSelected()); + // Update all BasicFrame rocket panel figures because it can change due to the preference change + for (BasicFrame frame : BasicFrame.getAllFrames()) { + frame.getRocketPanel().updateFigures(); + } + } + }); + this.add(showMarkers, "wrap, growx, spanx"); } } diff --git a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java index b83143638..5c2d39c1e 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/geometry/ComponentRenderer.java @@ -7,6 +7,7 @@ import com.jogamp.opengl.fixedfunc.GLMatrixFunc; import com.jogamp.opengl.glu.GLU; import com.jogamp.opengl.glu.GLUquadric; +import net.sf.openrocket.rocketcomponent.InnerTube; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -98,6 +99,8 @@ public class ComponentRenderer { if (c instanceof BodyTube) { renderTube(gl, (BodyTube) c, which); + } else if (c instanceof InnerTube) { + renderTube(gl, (InnerTube) c, which); } else if (c instanceof LaunchLug) { renderLug(gl, (LaunchLug) c, which); } else if ( c instanceof RailButton ){ @@ -257,6 +260,10 @@ public class ComponentRenderer { renderTube(gl, which, t.getOuterRadius(), t.getInnerRadius(), t.getLength()); } + private void renderTube(GL2 gl, InnerTube t, Surface which) { + renderTube(gl, which, t.getOuterRadius(), t.getInnerRadius(), t.getLength()); + } + private void renderRing(GL2 gl, RingComponent r) { gl.glRotated(90, 0, 1.0, 0); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java index 2030dba2b..3ce2cd3cc 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoFrame.java @@ -108,7 +108,7 @@ public class PhotoFrame extends JFrame { settings = new JDialog(this, trans.get("PhotoSettingsConfig.title")) { { - setContentPane(new PhotoSettingsConfig(p)); + setContentPane(new PhotoSettingsConfig(p, document)); pack(); this.setLocationByPlatform(true); GUIUtil.rememberWindowSize(this); diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java index eca18a495..23599f7f3 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettings.java @@ -36,7 +36,7 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings private double exhaustScale = 1.0; private double flameAspectRatio = 1.0; - private double sparkConcentration = 0; + private double sparkConcentration = 0.2; private double sparkWeight = 0; private Sky sky = Mountains.instance; @@ -278,5 +278,6 @@ public class PhotoSettings extends AbstractChangeSource implements FlameSettings public void setSmokeOpacity(double smokeOpacity) { this.smokeOpacity = smokeOpacity; + setSmokeAlpha(smokeOpacity); } } \ No newline at end of file diff --git a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java index 6294a6e96..ef27b0dc4 100644 --- a/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java +++ b/swing/src/net/sf/openrocket/gui/figure3d/photo/PhotoSettingsConfig.java @@ -25,8 +25,10 @@ import javax.swing.event.ChangeListener; import com.jogamp.opengl.GL2; import net.miginfocom.swing.MigLayout; +import net.sf.openrocket.document.OpenRocketDocument; import net.sf.openrocket.gui.adaptors.BooleanModel; import net.sf.openrocket.gui.adaptors.DoubleModel; +import net.sf.openrocket.gui.components.BasicSlider; import net.sf.openrocket.gui.components.ColorIcon; import net.sf.openrocket.gui.components.EditableSpinner; import net.sf.openrocket.gui.components.StyledLabel; @@ -125,7 +127,7 @@ public class PhotoSettingsConfig extends JTabbedPane { } } - public PhotoSettingsConfig(PhotoSettings p) { + public PhotoSettingsConfig(PhotoSettings p, OpenRocketDocument document) { super(); setPreferredSize(new Dimension(240, 320)); @@ -170,25 +172,29 @@ public class PhotoSettingsConfig extends JTabbedPane { add(new JLabel(trans.get("PhotoSettingsConfig.lbl.pitch"))); DoubleModel pitchModel = new DoubleModel(p, "Pitch", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(pitchModel.getSpinnerModel()), "growx"); - add(new UnitSelector(pitchModel), "pushx, left, wrap"); + add(new UnitSelector(pitchModel), "growx"); + add(new BasicSlider(pitchModel.getSliderModel(0, 2 * Math.PI)), "pushx, left, wrap"); /// Yaw add(new JLabel(trans.get("PhotoSettingsConfig.lbl.yaw"))); DoubleModel yawModel = new DoubleModel(p, "Yaw", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(yawModel.getSpinnerModel()), "growx"); - add(new UnitSelector(yawModel), "wrap"); + add(new UnitSelector(yawModel), "growx"); + add(new BasicSlider(yawModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// Roll add(new JLabel(trans.get("PhotoSettingsConfig.lbl.roll"))); DoubleModel rollModel = new DoubleModel(p, "Roll", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(rollModel.getSpinnerModel()), "growx"); - add(new UnitSelector(rollModel), "wrap"); + add(new UnitSelector(rollModel), "growx"); + add(new BasicSlider(rollModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// Advance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.advance"))); DoubleModel advanceModel = new DoubleModel(p, "Advance", UnitGroup.UNITS_LENGTH); add(new EditableSpinner(advanceModel.getSpinnerModel()), "growx"); - add(new UnitSelector(advanceModel), "wrap"); + add(new UnitSelector(advanceModel), "growx"); + add(new BasicSlider(advanceModel.getSliderModel(-document.getRocket().getLength(), document.getRocket().getLength())), "wrap"); // Camera add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.camera"), Style.BOLD), "split, gapright para, span"); @@ -198,25 +204,29 @@ public class PhotoSettingsConfig extends JTabbedPane { add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAz"))); DoubleModel viewAzModel = new DoubleModel(p, "ViewAz", UnitGroup.UNITS_ANGLE); add(new EditableSpinner(viewAzModel.getSpinnerModel()), "growx"); - add(new UnitSelector(viewAzModel), "wrap"); + add(new UnitSelector(viewAzModel), "growx"); + add(new BasicSlider(viewAzModel.getSliderModel(0, 2 * Math.PI)), "wrap"); /// View altitude add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vAlt"))); - DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE); + DoubleModel viewAltModle = new DoubleModel(p, "ViewAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2); add(new EditableSpinner(viewAltModle.getSpinnerModel()), "growx"); - add(new UnitSelector(viewAltModle), "wrap"); + add(new UnitSelector(viewAltModle), "growx"); + add(new BasicSlider(viewAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap"); /// View distance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.vDist"))); DoubleModel viewDistanceModel = new DoubleModel(p, "ViewDistance", UnitGroup.UNITS_LENGTH); add(new EditableSpinner(viewDistanceModel.getSpinnerModel()), "growx"); - add(new UnitSelector(viewDistanceModel), "wrap"); + add(new UnitSelector(viewDistanceModel), "growx"); + add(new BasicSlider(viewDistanceModel.getSliderModel(0, 2 * document.getRocket().getLength())), "wrap"); /// FoV add(new JLabel(trans.get("PhotoSettingsConfig.lbl.fov"))); - DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE); + DoubleModel fovModel = new DoubleModel(p, "Fov", UnitGroup.UNITS_ANGLE, Math.PI * 57.3/180, Math.PI * 160/180); add(new EditableSpinner(fovModel.getSpinnerModel()), "growx"); - add(new UnitSelector(fovModel), "wrap"); + add(new UnitSelector(fovModel), "growx"); + add(new BasicSlider(fovModel.getSliderModel(Math.PI * 57.3/180, Math.PI * 160/180)), "wrap"); } }); @@ -232,19 +242,24 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Ambiance add(new JLabel(trans.get("PhotoSettingsConfig.lbl.amb"))); - DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", 100, UnitGroup.UNITS_NONE, 0, 100); - add(new EditableSpinner(ambianceModel.getSpinnerModel()), "wrap"); + DoubleModel ambianceModel = new DoubleModel(p, "Ambiance", UnitGroup.UNITS_RELATIVE, 0, 1); + add(new EditableSpinner(ambianceModel.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(ambianceModel)); + add(new BasicSlider(ambianceModel.getSliderModel(0, 1)), "pushx, left, wrap"); /// Light azimuth add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAz"))); DoubleModel lightAzModel = new DoubleModel(p, "LightAz", UnitGroup.UNITS_ANGLE); - add(new EditableSpinner(lightAzModel.getSpinnerModel()), "split 2"); - add(new UnitSelector(lightAzModel), "pushx, left, wrap"); + add(new EditableSpinner(lightAzModel.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(lightAzModel)); + add(new BasicSlider(lightAzModel.getSliderModel(-Math.PI, Math.PI)), "wrap"); /// Light altitude add(new JLabel(trans.get("PhotoSettingsConfig.lbl.lightAlt"))); - DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE); - add(new EditableSpinner(lightAltModle.getSpinnerModel()), "wrap"); + DoubleModel lightAltModle = new DoubleModel(p, "LightAlt", UnitGroup.UNITS_ANGLE, -Math.PI / 2, Math.PI / 2); + add(new EditableSpinner(lightAltModle.getSpinnerModel()), "growx, split 2"); + add(new UnitSelector(lightAltModle)); + add(new BasicSlider(lightAltModle.getSliderModel(-Math.PI / 2, Math.PI / 2)), "wrap"); // Sky add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.sky"), Style.BOLD), "split, span, gapright para"); @@ -292,7 +307,7 @@ public class PhotoSettingsConfig extends JTabbedPane { setSelectedItem(noSky); } } - }, "wrap"); + }, "spanx, wrap"); /// Image credit final JLabel creditLabel = new JLabel(trans.get("PhotoSettingsConfig.lbl.skyCredit")); @@ -304,7 +319,7 @@ public class PhotoSettingsConfig extends JTabbedPane { credit.setOpaque(false); credit.setFocusable(false); credit.setFont(creditLabel.getFont()); - add(credit); + add(credit, "spanx"); final StateChangeListener skyChange = new StateChangeListener() { @Override @@ -332,22 +347,28 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Smoke add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smoke"))); BooleanModel smokeModel = new BooleanModel(p, "Smoke"); - add(new JCheckBox(smokeModel), "split 2, w 15"); + add(new JCheckBox(smokeModel), "split 2, spanx"); - add(smokeColorButton, "pushx, left, wrap"); + add(smokeColorButton, "wrap"); smokeModel.addEnableComponent(smokeColorButton); /// Smoke opacity add(new JLabel(trans.get("PhotoSettingsConfig.lbl.smokeOpacity"))); - DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", 100, UnitGroup.UNITS_NONE, 0, 100); + DoubleModel smokeOpacityModel = new DoubleModel(p, "SmokeOpacity", UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner opacitySpinner = new EditableSpinner(smokeOpacityModel.getSpinnerModel()); - add(opacitySpinner, "wrap"); + UnitSelector opacitySelector = new UnitSelector(smokeOpacityModel); + BasicSlider opacitySlider = new BasicSlider(smokeOpacityModel.getSliderModel(0, 1)); + add(opacitySpinner, "growx"); + add(opacitySelector); + add(opacitySlider, "wrap"); smokeModel.addEnableComponent(opacitySpinner); + smokeModel.addEnableComponent(opacitySelector); + smokeModel.addEnableComponent(opacitySlider); /// Flame add(new JLabel(trans.get("PhotoSettingsConfig.lbl.flame"))); BooleanModel fireModel = new BooleanModel(p, "Flame"); - add(new JCheckBox(fireModel), "split 2, w 15"); + add(new JCheckBox(fireModel), "split 2, spanx"); add(flameColorButton, "wrap"); fireModel.addEnableComponent(flameColorButton); @@ -357,8 +378,11 @@ public class PhotoSettingsConfig extends JTabbedPane { DoubleModel flameAspectModel = new DoubleModel(p, "FlameAspectRatio", 100, UnitGroup.UNITS_NONE, 25, 250); EditableSpinner flameAspectSpinner = new EditableSpinner(flameAspectModel.getSpinnerModel()); - add(flameAspectSpinner, "wrap"); + BasicSlider flameAspectSlider = new BasicSlider(flameAspectModel.getSliderModel(25, 250)); + add(flameAspectSpinner, "growx"); + add(flameAspectSlider, "skip 1, wrap"); fireModel.addEnableComponent(flameAspectSpinner); + fireModel.addEnableComponent(flameAspectSlider); /// Sparks add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparks"))); @@ -369,23 +393,36 @@ public class PhotoSettingsConfig extends JTabbedPane { /// Sparks concentration add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkConcentration"))); - DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration", 100, - UnitGroup.UNITS_NONE, 0, 100); + DoubleModel sparkConcentrationModel = new DoubleModel(p, "SparkConcentration", + UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner sparkConcentrationSpinner = new EditableSpinner(sparkConcentrationModel.getSpinnerModel()); - add(sparkConcentrationSpinner, "wrap"); + UnitSelector sparkConcentrationSelector = new UnitSelector(sparkConcentrationModel); + BasicSlider sparkConcentrationSlider = new BasicSlider(sparkConcentrationModel.getSliderModel(0, 1)); + add(sparkConcentrationSpinner, "growx"); + add(sparkConcentrationSelector); + add(sparkConcentrationSlider, "wrap"); sparksModel.addEnableComponent(sparkConcentrationSpinner); + sparksModel.addEnableComponent(sparkConcentrationSelector); + sparksModel.addEnableComponent(sparkConcentrationSlider); /// Spark weight add(new JLabel(trans.get("PhotoSettingsConfig.lbl.sparkWeight"))); - DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", 100, UnitGroup.UNITS_NONE, 0, 100); + DoubleModel sparkWeightModel = new DoubleModel(p, "SparkWeight", UnitGroup.UNITS_RELATIVE, 0, 1); EditableSpinner sparkWeightSpinner = new EditableSpinner(sparkWeightModel.getSpinnerModel()); - add(sparkWeightSpinner, "wrap"); + UnitSelector sparkWeightSelector = new UnitSelector(sparkWeightModel); + BasicSlider sparkWeightSlider = new BasicSlider(sparkWeightModel.getSliderModel(0, 1)); + add(sparkWeightSpinner, "growx"); + add(sparkWeightSelector); + add(sparkWeightSlider, "wrap"); sparksModel.addEnableComponent(sparkWeightSpinner); + sparksModel.addEnableComponent(sparkWeightSelector); + sparksModel.addEnableComponent(sparkWeightSlider); /// Exhaust scale add(new JLabel(trans.get("PhotoSettingsConfig.lbl.exhaustScale"))); DoubleModel exhaustScaleModel = new DoubleModel(p, "ExhaustScale", 100, UnitGroup.UNITS_NONE, 0, 1000); - add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "wrap"); + add(new EditableSpinner(exhaustScaleModel.getSpinnerModel()), "growx"); + add(new BasicSlider(exhaustScaleModel.getSliderModel(0, 1000)), "skip 1, wrap"); // Effects add(new StyledLabel(trans.get("PhotoSettingsConfig.lbl.effects"), Style.BOLD), "split, span, gapright para"); diff --git a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java index 28bce901e..d3876b181 100644 --- a/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java +++ b/swing/src/net/sf/openrocket/gui/figureelements/RocketInfo.java @@ -41,7 +41,7 @@ public class RocketInfo implements FigureElement { private final Caret cpCaret = new CPCaret(0,0); private final Caret cgCaret = new CGCaret(0,0); - private final UnitGroup stabilityUnits; + private UnitGroup stabilityUnits; private FlightConfiguration configuration; private double cg = 0, cp = 0; @@ -459,5 +459,6 @@ public class RocketInfo implements FigureElement { public void setCurrentConfig(FlightConfiguration newConfig) { this.configuration = newConfig; + this.stabilityUnits = UnitGroup.stabilityUnits(newConfig); } } diff --git a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java index f83ef3fb4..6f76a838d 100644 --- a/swing/src/net/sf/openrocket/gui/main/BasicFrame.java +++ b/swing/src/net/sf/openrocket/gui/main/BasicFrame.java @@ -19,6 +19,7 @@ import java.net.URL; import java.net.URLDecoder; import java.util.ArrayList; import java.util.Arrays; +import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.concurrent.ExecutionException; @@ -116,7 +117,8 @@ public class BasicFrame extends JFrame { * List of currently open frames. When the list goes empty * it is time to exit the application. */ - private static final ArrayList<BasicFrame> frames = new ArrayList<BasicFrame>(); + private static final List<BasicFrame> frames = new ArrayList<BasicFrame>(); + private static BasicFrame startupFrame = null; // the frame that was created at startup /** @@ -300,23 +302,28 @@ public class BasicFrame extends JFrame { componentSelectionModel.addTreeSelectionListener(new TreeSelectionListener() { @Override public void valueChanged(TreeSelectionEvent e) { - TreePath selPath = e.getNewLeadSelectionPath(); - if (selPath == null) return; - RocketComponent c = (RocketComponent) selPath.getLastPathComponent(); + if (tree == null || tree.getSelectionPaths() == null || tree.getSelectionPaths().length == 0 + || rocketpanel == null) return; - if (c instanceof AxialStage || c instanceof Rocket || c instanceof PodSet) { - if (rocketpanel == null) return; - - List<RocketComponent> children = new LinkedList<>(); - for (RocketComponent child : c) { - children.add(child); + // Get all the components that need to be selected = currently selected components + children of stages/boosters/podsets + List<RocketComponent> children = new ArrayList<>(Arrays.asList(rocketpanel.getFigure().getSelection())); + for (TreePath p : tree.getSelectionPaths()) { + if (p != null) { + RocketComponent c = (RocketComponent) p.getLastPathComponent(); + if (c instanceof AxialStage || c instanceof Rocket || c instanceof PodSet) { + Iterator<RocketComponent> iter = c.iterator(false); + while (iter.hasNext()) { + RocketComponent child = iter.next(); + children.add(child); + } + } } + } - // Select all the child components - if (rocketpanel.getFigure() != null && rocketpanel.getFigure3d() != null) { - rocketpanel.getFigure().setSelection(children.toArray(new RocketComponent[0])); - rocketpanel.getFigure3d().setSelection(children.toArray(new RocketComponent[0])); - } + // Select all the child components + if (rocketpanel.getFigure() != null && rocketpanel.getFigure3d() != null) { + rocketpanel.getFigure().setSelection(children.toArray(new RocketComponent[0])); + rocketpanel.getFigure3d().setSelection(children.toArray(new RocketComponent[0])); } } }); @@ -483,6 +490,10 @@ public class BasicFrame extends JFrame { return result; } + public RocketPanel getRocketPanel() { + return rocketpanel; + } + /** * Creates the menu for the window. */ @@ -1225,6 +1236,19 @@ public class BasicFrame extends JFrame { return menu; } + /** + * Return the frame that was created at the application's startup. + */ + public static BasicFrame getStartupFrame() { + return startupFrame; + } + + /** + * Set the frame that is created at the application's startup. + */ + public static void setStartupFrame(BasicFrame startupFrame) { + BasicFrame.startupFrame = startupFrame; + } /** * Select the tab on the main pane. @@ -1267,7 +1291,7 @@ public class BasicFrame extends JFrame { for (File file : files) { log.info("Opening file: " + file); - if (open(file, parent)) { + if (open(file, parent) != null) { MRUDesignFile opts = MRUDesignFile.getInstance(); opts.addFile(file.getAbsolutePath()); } @@ -1297,7 +1321,7 @@ public class BasicFrame extends JFrame { for (File file : files) { log.info("Opening file: " + file); - if (open(file, this)) { + if (open(file, this) != null) { MRUDesignFile opts = MRUDesignFile.getInstance(); opts.addFile(file.getAbsolutePath()); } @@ -1367,9 +1391,9 @@ public class BasicFrame extends JFrame { * * @param file the file to open. * @param parent the parent component for which a progress dialog is opened. - * @return whether the file was successfully loaded and opened. + * @return the BasicFrame that was created, or null if not created successfully. */ - public static boolean open(File file, Window parent) { + public static BasicFrame open(File file, Window parent) { OpenFileWorker worker = new OpenFileWorker(file); return open(worker, file.getName(), parent, false); } @@ -1382,15 +1406,15 @@ public class BasicFrame extends JFrame { * @param displayName the file name to display in dialogs. * @param parent * @param openRocketConfigDialog if true, will open the configuration dialog of the rocket. This is useful for examples. - * @return + * @return the BasicFrame that was created, or null if not created successfully. */ - private static boolean open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) { + private static BasicFrame open(OpenFileWorker worker, String displayName, Window parent, boolean openRocketConfigDialog) { //// Open the file in a Swing worker thread log.info("Starting OpenFileWorker"); if (!SwingWorkerDialog.runWorker(parent, "Opening file", "Reading " + displayName + "...", worker)) { // // User cancelled the operation log.info("User cancelled the OpenFileWorker"); - return false; + return null; } //// Handle the document @@ -1409,7 +1433,7 @@ public class BasicFrame extends JFrame { JOptionPane.showMessageDialog(parent, "File not found: " + displayName, "Error opening file", JOptionPane.ERROR_MESSAGE); - return false; + return null; } else if (cause instanceof RocketLoadException) { @@ -1418,7 +1442,7 @@ public class BasicFrame extends JFrame { "Unable to open file '" + displayName + "': " + cause.getMessage(), "Error opening file", JOptionPane.ERROR_MESSAGE); - return false; + return null; } else { @@ -1462,7 +1486,7 @@ public class BasicFrame extends JFrame { ComponentConfigDialog.showDialog(frame, doc, doc.getRocket()); } - return true; + return frame; } @@ -1768,24 +1792,27 @@ public class BasicFrame extends JFrame { /** * Opens a new design file or the last design file, if set in the preferences. * Can be used for reopening the application or opening it the first time. + * @return the BasicFrame that was created */ - public static void reopen() { + public static BasicFrame reopen() { if (!Application.getPreferences().isAutoOpenLastDesignOnStartupEnabled()) { - BasicFrame.newAction(); + return BasicFrame.newAction(); } else { String lastFile = MRUDesignFile.getInstance().getLastEditedDesignFile(); if (lastFile != null) { log.info("Opening last design file: " + lastFile); - if (!BasicFrame.open(new File(lastFile), null)) { + BasicFrame frame = BasicFrame.open(new File(lastFile), null); + if (frame == null) { MRUDesignFile.getInstance().removeFile(lastFile); - BasicFrame.newAction(); + return BasicFrame.newAction(); } else { MRUDesignFile.getInstance().addFile(lastFile); + return frame; } } else { - BasicFrame.newAction(); + return BasicFrame.newAction(); } } } @@ -1793,8 +1820,9 @@ public class BasicFrame extends JFrame { /** * Open a new design window with a basic rocket+stage. + * @return the BasicFrame that was created */ - public static void newAction() { + public static BasicFrame newAction() { log.info("New action initiated"); OpenRocketDocument doc = OpenRocketDocumentFactory.createNewRocket(); @@ -1802,6 +1830,7 @@ public class BasicFrame extends JFrame { BasicFrame frame = new BasicFrame(doc); frame.replaceable = true; frame.setVisible(true); + return frame; } @@ -1865,6 +1894,13 @@ public class BasicFrame extends JFrame { return null; } + /** + * Return all BasicFrame instances + */ + public static List<BasicFrame> getAllFrames() { + return frames; + } + /** * Checks whether all the BasicFrames are closed. * @return true if all the BasicFrames are closed, false if not diff --git a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java index 8ed2338c9..0ff03e261 100644 --- a/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java +++ b/swing/src/net/sf/openrocket/gui/main/ComponentAddButtons.java @@ -488,7 +488,7 @@ public class ComponentAddButtons extends JPanel implements Scrollable { } } - ComponentConfigDialog.showDialog(parent, document, component, false); + ComponentConfigDialog.showDialog(parent, document, component, false, false); } } diff --git a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java index 5582e4fca..3ff152d82 100644 --- a/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java +++ b/swing/src/net/sf/openrocket/gui/main/MRUDesignFileAction.java @@ -73,7 +73,7 @@ public final class MRUDesignFileAction extends JMenu { @Override public void actionPerformed(ActionEvent e) { String command = e.getActionCommand(); - if (BasicFrame.open(new File(command), parent)) { + if (BasicFrame.open(new File(command), parent) != null) { MRUDesignFile.getInstance().addFile(command); } else { diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java index fb174692a..11c991e1f 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/RecoveryConfigurationPanel.java @@ -1,9 +1,12 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; @@ -162,6 +165,30 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel<Recovery return recoveryTable; } + @Override + protected void installTableListener() { + super.installTableListener(); + + table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + updateComponentSelection(e); + } + }); + + table.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + updateComponentSelection(new ListSelectionEvent(this, 0, 0, false)); + } + + @Override + public void focusLost(FocusEvent e) { + + } + }); + } + public void selectDeployment() { List<RecoveryDevice> devices = getSelectedComponents(); List<FlightConfigurationId> fcIds = getSelectedConfigurationIds(); @@ -239,6 +266,16 @@ public class RecoveryConfigurationPanel extends FlightConfigurablePanel<Recovery popupMenuFull.show(e.getComponent(), e.getX(), e.getY()); } + public void updateComponentSelection(ListSelectionEvent e) { + if (e.getValueIsAdjusting() || getSelectedComponents() == null) { + return; + } + List<RocketComponent> components = new ArrayList<>(getSelectedComponents()); + if (components.size() == 0) return; + + flightConfigurationPanel.setSelectedComponents(components); + } + public void updateButtonState() { boolean componentSelected = getSelectedComponent() != null; selectDeploymentButton.setEnabled(componentSelected); diff --git a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java index 6ae182dcd..ccf27e1d1 100644 --- a/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java +++ b/swing/src/net/sf/openrocket/gui/main/flightconfigpanel/SeparationConfigurationPanel.java @@ -1,9 +1,12 @@ package net.sf.openrocket.gui.main.flightconfigpanel; import java.awt.event.ActionEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; +import java.util.ArrayList; import java.util.List; import javax.swing.AbstractAction; @@ -27,6 +30,7 @@ import net.sf.openrocket.rocketcomponent.AxialStage; import net.sf.openrocket.rocketcomponent.ComponentChangeEvent; import net.sf.openrocket.rocketcomponent.FlightConfigurationId; import net.sf.openrocket.rocketcomponent.Rocket; +import net.sf.openrocket.rocketcomponent.RocketComponent; import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration; import net.sf.openrocket.rocketcomponent.StageSeparationConfiguration.SeparationEvent; import net.sf.openrocket.startup.Application; @@ -170,6 +174,30 @@ public class SeparationConfigurationPanel extends FlightConfigurablePanel<AxialS return separationTable; } + @Override + protected void installTableListener() { + super.installTableListener(); + + table.getColumnModel().getSelectionModel().addListSelectionListener(new ListSelectionListener() { + @Override + public void valueChanged(ListSelectionEvent e) { + updateComponentSelection(e); + } + }); + + table.addFocusListener(new FocusListener() { + @Override + public void focusGained(FocusEvent e) { + updateComponentSelection(new ListSelectionEvent(this, 0, 0, false)); + } + + @Override + public void focusLost(FocusEvent e) { + + } + }); + } + public void selectSeparation() { List<AxialStage> stages = getSelectedComponents(); List<FlightConfigurationId> fcIds = getSelectedConfigurationIds(); @@ -248,6 +276,16 @@ public class SeparationConfigurationPanel extends FlightConfigurablePanel<AxialS popupMenuFull.show(e.getComponent(), e.getX(), e.getY()); } + public void updateComponentSelection(ListSelectionEvent e) { + if (e.getValueIsAdjusting() || getSelectedComponents() == null) { + return; + } + List<RocketComponent> components = new ArrayList<>(getSelectedComponents()); + if (components.size() == 0) return; + + flightConfigurationPanel.setSelectedComponents(components); + } + public void updateButtonState() { boolean componentSelected = getSelectedComponent() != null; selectSeparationButton.setEnabled(componentSelected); diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java new file mode 100644 index 000000000..35b048551 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/EmptyShapes.java @@ -0,0 +1,122 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.util.Coordinate; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; +import java.awt.geom.Line2D; +import java.awt.geom.Rectangle2D; + +/** + * Shapes of an "empty"/virtual object, e.g. a podset without any children. + * The shape is a center square with additional lines on the north, east, south and west side of the square. + * + * @author Sibo Van Gool <sibo.vangool@hotmail.com> + */ +public class EmptyShapes extends RocketComponentShape { + /** + * Returns the empty shape in the side view. + * @param radius radius of the center square + */ + public static Shape[] getShapesSide(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double x = instanceAbsoluteLocation.x; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + final Shape[] s = new Shape[5]; + // Center square + s[0] = new Rectangle2D.Double(x - radius, y - radius, 2 * radius, 2 * radius); + // Line North + s[1] = new Line2D.Double(x, y + radius, x, y + radius + lineLength); + // Line East + s[2] = new Line2D.Double(x + radius, y, x + radius + lineLength, y); + // Line South + s[3] = new Line2D.Double(x, y - radius, x, y - radius - lineLength); + // Line West + s[4] = new Line2D.Double(x - radius, y, x - radius - lineLength, y); + + return s; + } + + /** + * Returns the empty shape in the side view, with an additional square encompassing the shape that can be used + * for selecting the object. + * @param radius radius of the center square + */ + public static Shape[] getShapesSideWithSelectionSquare(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double x = instanceAbsoluteLocation.x; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + Shape[] shapes = getShapesSide(transformation, radius); + + // Invisible shape for selecting the component (= a square encompassing the component) + Shape selectionShape = new Rectangle2D.Double(x - radius - lineLength, y - radius - lineLength, + lineLength * 2 + radius * 2, lineLength * 2 + radius * 2); + + Shape[] finalShapes = new Shape[shapes.length + 1]; + System.arraycopy(shapes, 0, finalShapes, 0, shapes.length); + finalShapes[finalShapes.length - 1] = selectionShape; + + return finalShapes; + } + + /** + * Returns the empty shape in the side view. + * @param radius radius of the center square + */ + public static Shape[] getShapesBack(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double z = instanceAbsoluteLocation.z; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + final Shape[] s = new Shape[5]; + // Center square + s[0] = new Rectangle2D.Double(z - radius, y - radius, 2 * radius, 2 * radius); + // Line North + s[1] = new Line2D.Double(z, y + radius, z, y + radius + lineLength); + // Line East + s[2] = new Line2D.Double(z + radius, y, z + radius + lineLength, y); + // Line South + s[3] = new Line2D.Double(z, y - radius, z, y - radius - lineLength); + // Line West + s[4] = new Line2D.Double(z - radius, y, z - radius - lineLength, y); + + return s; + } + + /** + * Returns the empty shape in the back view, with an additional square encompassing the shape that can be used + * for selecting the object. + * @param radius radius of the center square + */ + public static Shape[] getShapesBackWithSelectionSquare(final Transformation transformation, final double radius) { + final Coordinate instanceAbsoluteLocation = transformation.transform(Coordinate.ZERO); + double z = instanceAbsoluteLocation.z; + double y = instanceAbsoluteLocation.y; + + double lineLength = getLineLength(radius); // Length of the line protruding the center square + + Shape[] shapes = getShapesBack(transformation, radius); + + // Invisible shape for selecting the component (= a square encompassing the component) + Shape selectionShape = new Rectangle2D.Double(z - radius - lineLength, y - radius - lineLength, + lineLength * 2 + radius * 2, lineLength * 2 + radius * 2); + + Shape[] finalShapes = new Shape[shapes.length + 1]; + System.arraycopy(shapes, 0, finalShapes, 0, shapes.length); + finalShapes[finalShapes.length - 1] = selectionShape; + + return finalShapes; + } + + private static double getLineLength(double radius) { + return radius * 3; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java new file mode 100644 index 000000000..565a23591 --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/ParallelStageShapes.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; + +public class ParallelStageShapes extends RocketComponentShape { + public static final Color boosterColor = new Color(198,163,184); + + public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) { + ParallelStage booster = (ParallelStage)component; + double radius = getDisplayRadius(booster); + + Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(boosterColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) { + ParallelStage booster = (ParallelStage)component; + double radius = getDisplayRadius(booster); + + Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(boosterColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + private static double getDisplayRadius(ParallelStage booster) { + return booster.getRocket().getBoundingRadius() * 0.03; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java new file mode 100644 index 000000000..7ef928f2d --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/PodSetShapes.java @@ -0,0 +1,48 @@ +package net.sf.openrocket.gui.rocketfigure; + +import net.sf.openrocket.rocketcomponent.PodSet; +import net.sf.openrocket.rocketcomponent.RocketComponent; +import net.sf.openrocket.util.Color; +import net.sf.openrocket.util.Transformation; + +import java.awt.Shape; + +public class PodSetShapes extends RocketComponentShape { + public static final Color podsetColor = new Color(160,160,215); + + public static RocketComponentShape[] getShapesSide(final RocketComponent component, final Transformation transformation) { + PodSet podset = (PodSet)component; + double radius = getDisplayRadius(podset); + + Shape[] s = EmptyShapes.getShapesSideWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(podsetColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + public static RocketComponentShape[] getShapesBack(final RocketComponent component, final Transformation transformation) { + PodSet podset = (PodSet)component; + double radius = getDisplayRadius(podset); + + Shape[] s = EmptyShapes.getShapesBackWithSelectionSquare(transformation, radius); + RocketComponentShape[] shapes = RocketComponentShape.toArray(s, component); + + // Set the color of the shapes + for (int i = 0; i < shapes.length - 1; i++) { + shapes[i].setColor(podsetColor); + } + shapes[shapes.length - 1].setColor(Color.INVISIBLE); + + return shapes; + } + + private static double getDisplayRadius(PodSet podset) { + return podset.getRocket().getBoundingRadius() * 0.03; + } +} diff --git a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java index 725713b80..c132bfd3d 100644 --- a/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java +++ b/swing/src/net/sf/openrocket/gui/rocketfigure/RocketComponentShape.java @@ -64,6 +64,10 @@ public class RocketComponentShape { return new RocketComponentShape[0]; } + public Color getColor() { + return color; + } + public void setColor(Color color) { this.color = color; } diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java index 3226abee2..6e4bf9e4b 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketFigure.java @@ -17,6 +17,9 @@ import java.awt.geom.Rectangle2D; import java.util.*; import java.util.Map.Entry; +import net.sf.openrocket.rocketcomponent.AxialStage; +import net.sf.openrocket.rocketcomponent.ParallelStage; +import net.sf.openrocket.rocketcomponent.PodSet; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -26,7 +29,6 @@ import net.sf.openrocket.gui.util.ColorConversion; import net.sf.openrocket.gui.util.SwingPreferences; import net.sf.openrocket.motor.Motor; import net.sf.openrocket.motor.MotorConfiguration; -import net.sf.openrocket.rocketcomponent.ComponentAssembly; import net.sf.openrocket.rocketcomponent.FlightConfiguration; import net.sf.openrocket.rocketcomponent.InstanceContext; import net.sf.openrocket.rocketcomponent.MotorMount; @@ -52,6 +54,7 @@ import net.sf.openrocket.util.Transformation; public class RocketFigure extends AbstractScaleFigure { private final static Logger log = LoggerFactory.getLogger(FinPointFigure.class); + protected final SwingPreferences preferences = (SwingPreferences) Application.getPreferences(); private static final String ROCKET_FIGURE_PACKAGE = "net.sf.openrocket.gui.rocketfigure"; private static final String ROCKET_FIGURE_SUFFIX = "Shapes"; @@ -378,6 +381,20 @@ public class RocketFigure extends AbstractScaleFigure { for(Entry<RocketComponent, ArrayList<InstanceContext>> entry: config.getActiveInstances().entrySet() ) { final RocketComponent comp = entry.getKey(); + + // Only draw podsets when they are selected + if ((comp instanceof PodSet || comp instanceof ParallelStage) && preferences.isShowMarkers()) { + boolean selected = false; + + // Check if component is in the selection + for (RocketComponent component : selection) { + if (comp == component) { + selected = true; + break; + } + } + if (!selected) continue; + } final ArrayList<InstanceContext> contextList = entry.getValue(); @@ -390,8 +407,12 @@ public class RocketFigure extends AbstractScaleFigure { /** * Gets the shapes required to draw the component. - * - * @param component + * + * @param allShapes output buffer for the shapes to add to + * @param viewType the view type to draw the component in + * @param component component to draw and add to <allShapes> + * @param transformation transformation to apply to the component before drawing it + * @param color color to draw the component in * * @return the <code>ArrayList</code> containing all the shapes to draw. */ @@ -399,10 +420,11 @@ public class RocketFigure extends AbstractScaleFigure { PriorityQueue<RocketComponentShape> allShapes, // this is the output parameter final RocketPanel.VIEW_TYPE viewType, final RocketComponent component, - final Transformation transformation) { + final Transformation transformation, + final net.sf.openrocket.util.Color color) { Reflection.Method m; - if(( component instanceof Rocket)||( component instanceof ComponentAssembly )){ + if ((component instanceof Rocket) || (component instanceof AxialStage && !(component instanceof ParallelStage))){ // no-op; no shapes here return allShapes; } @@ -431,9 +453,35 @@ public class RocketFigure extends AbstractScaleFigure { RocketComponentShape[] returnValue = (RocketComponentShape[]) m.invokeStatic(component, transformation); + + if (color != null) { + for (RocketComponentShape rcs : returnValue) { + if (rcs.getColor() == net.sf.openrocket.util.Color.INVISIBLE) continue; // don't change the color of invisible (often selection) components + rcs.setColor(color); + } + } + allShapes.addAll(Arrays.asList(returnValue)); return allShapes; } + + /** + * Gets the shapes required to draw the component. + * + * @param allShapes output buffer for the shapes to add to + * @param viewType the view type to draw the component in + * @param component component to draw and add to <allShapes> + * @param transformation transformation to apply to the component before drawing it + * + * @return the <code>ArrayList</code> containing all the shapes to draw. + */ + private static PriorityQueue<RocketComponentShape> addThisShape( + PriorityQueue<RocketComponentShape> allShapes, // this is the output parameter + final RocketPanel.VIEW_TYPE viewType, + final RocketComponent component, + final Transformation transformation) { + return addThisShape(allShapes, viewType, component, transformation, null); + } /** diff --git a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java index 92df95906..9f4f309f7 100644 --- a/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java +++ b/swing/src/net/sf/openrocket/gui/scalefigure/RocketPanel.java @@ -252,7 +252,7 @@ public class RocketPanel extends JPanel implements TreeSelectionListener, Change }); } - private void updateFigures() { + public void updateFigures() { if (!is3d) figure.updateFigure(); else diff --git a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java b/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java index 2bfe10afc..03b9ab944 100644 --- a/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java +++ b/swing/src/net/sf/openrocket/gui/simulation/SimulationEditDialog.java @@ -151,7 +151,7 @@ public class SimulationEditDialog extends JDialog { final Rocket rkt = document.getRocket(); final FlightConfiguration config = rkt.getFlightConfiguration(simulationList[0].getFlightConfigurationId()); - final ConfigurationComboBox configComboBox = new ConfigurationComboBox(rkt); + final ConfigurationComboBox configComboBox = new ConfigurationComboBox(rkt, false); configComboBox.setSelectedItem(config); //// Select the motor configuration to use. diff --git a/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java new file mode 100644 index 000000000..d4443954c --- /dev/null +++ b/swing/src/net/sf/openrocket/gui/util/WindowLocationUtil.java @@ -0,0 +1,74 @@ +package net.sf.openrocket.gui.util; + +import java.awt.GraphicsDevice; +import java.awt.Window; + +/** + * Helper class for setting the location of a Swing window. E.g. to check when the window is outside the screen and + * recenter it if so. + * + * @author Sibo Van Gool <sibo.vangool@hotmail.com> + */ +public abstract class WindowLocationUtil { + /** + * Sets the location of the window, but don't go outside the screen. + * @param window the window to move + * @param x the target x position on the screen + * @param y the target y position on the screen + */ + public static void setLocationWithinScreen(Window window, int x, int y) { + java.awt.Dimension screenSize = java.awt.Toolkit.getDefaultToolkit().getScreenSize(); + java.awt.Dimension windowSize = window.getSize(); + int screenWidth = screenSize.width; + int screenHeight = screenSize.height; + int windowWidth = windowSize.width; + int windowHeight = windowSize.height; + int xPos = x; + int yPos = y; + if (xPos + windowWidth > screenWidth) { + xPos = screenWidth - windowWidth; + } + if (yPos + windowHeight > screenHeight) { + yPos = screenHeight - windowHeight; + } + window.setLocation(xPos, yPos); + } + + /** + * Moves the window to the center of the screen if its location is outside the boundary of the screen. + * @param window window to move + */ + public static void moveIfOutsideOfMonitor(Window window) { + GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice(); + if (currentDevice != null && window.isVisible() && + !currentDevice.getDefaultConfiguration().getBounds().contains(window.getLocationOnScreen())) { + int width = currentDevice.getDefaultConfiguration().getBounds().width; + int height = currentDevice.getDefaultConfiguration().getBounds().height; + window.setLocation( + ((width / 2) - (window.getSize().width / 2)) + currentDevice.getDefaultConfiguration().getBounds().x, + ((height / 2) - (window.getSize().height / 2)) + currentDevice.getDefaultConfiguration().getBounds().y + ); + } + } + + /** + * Moves the window to the center of the screen if it is on another monitor as the parent window, or if its location + * is outside the boundary of the parent's monitor screen. + * @param window window to move + * @param parent parent window + */ + public static void moveIfOutsideOfParentMonitor(Window window, Window parent) { + GraphicsDevice parentDevice = parent.getGraphicsConfiguration().getDevice(); + GraphicsDevice currentDevice = window.getGraphicsConfiguration().getDevice(); + if (parentDevice != null && (currentDevice == null || currentDevice != parentDevice || + (window.isVisible() && !parentDevice.getDefaultConfiguration().getBounds().contains( + window.getLocationOnScreen())))) { + int width = parentDevice.getDefaultConfiguration().getBounds().width; + int height = parentDevice.getDefaultConfiguration().getBounds().height; + window.setLocation( + ((width / 2) - (window.getSize().width / 2)) + parentDevice.getDefaultConfiguration().getBounds().x, + ((height / 2) - (window.getSize().height / 2)) + parentDevice.getDefaultConfiguration().getBounds().y + ); + } + } +} diff --git a/swing/src/net/sf/openrocket/startup/SwingStartup.java b/swing/src/net/sf/openrocket/startup/SwingStartup.java index 5d6ff3386..462549e11 100644 --- a/swing/src/net/sf/openrocket/startup/SwingStartup.java +++ b/swing/src/net/sf/openrocket/startup/SwingStartup.java @@ -223,7 +223,8 @@ public class SwingStartup { // Starting action (load files or open new document) log.info("Opening main application window"); if (!handleCommandLine(args)) { - BasicFrame.reopen(); + BasicFrame startupFrame = BasicFrame.reopen(); + BasicFrame.setStartupFrame(startupFrame); } // Check whether update info has been fetched or whether it needs more time @@ -298,7 +299,7 @@ public class SwingStartup { // Check command-line for files boolean opened = false; for (String file : args) { - if (BasicFrame.open(new File(file), null)) { + if (BasicFrame.open(new File(file), null) != null) { opened = true; } } diff --git a/swing/test/net/sf/openrocket/IntegrationTest.java b/swing/test/net/sf/openrocket/IntegrationTest.java index 24cb6178e..e0a280f09 100644 --- a/swing/test/net/sf/openrocket/IntegrationTest.java +++ b/swing/test/net/sf/openrocket/IntegrationTest.java @@ -71,7 +71,7 @@ public class IntegrationTest { private Action undoAction, redoAction; private AerodynamicCalculator aeroCalc = new BarrowmanCalculator(); - private FlightConfiguration config; + private FlightConfigurationId fcid; private FlightConditions conditions; private String massComponentID = null; @@ -112,14 +112,12 @@ public class IntegrationTest { undoAction = UndoRedoAction.newUndoAction(document); redoAction = UndoRedoAction.newRedoAction(document); - FlightConfigurationId fcid = document.getSimulation(0).getFlightConfigurationId(); - config = document.getRocket().getFlightConfiguration(fcid); + fcid = document.getSimulation(0).getFlightConfigurationId(); + FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid); conditions = new FlightConditions(config); // Test undo state checkUndoState(null, null); - - InnerTube mmt = (InnerTube)config.getRocket().getChild(0).getChild(1).getChild(2); // Compute cg+cp + altitude // double cgx, double mass, double cpx, double cna) @@ -330,6 +328,7 @@ public class IntegrationTest { } private void checkCgCp(double cgx, double mass, double cpx, double cna) { + FlightConfiguration config = document.getRocket().getFlightConfiguration(fcid); final RigidBody launchData = MassCalculator.calculateLaunch(config); final Coordinate cg = launchData.getCenterOfMass(); assertEquals(cgx, cg.x, 0.001);