Merged l10n branch to trunk
authorplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 18 Jun 2011 20:53:40 +0000 (20:53 +0000)
committerplaa <plaa@180e2498-e6e9-4542-8430-84ac67f01cd8>
Sat, 18 Jun 2011 20:53:40 +0000 (20:53 +0000)
git-svn-id: https://openrocket.svn.sourceforge.net/svnroot/openrocket/trunk@130 180e2498-e6e9-4542-8430-84ac67f01cd8

74 files changed:
ChangeLog
LICENSE.TXT
ReleaseNotes
build.properties
build.xml
l10n/messages.properties
l10n/messages_de.properties
l10n/messages_es.properties
l10n/messages_fr.properties
pix-src/icon/icon-large-close.xcf.gz [new file with mode: 0644]
releasing.txt
src/net/sf/openrocket/database/Databases.java
src/net/sf/openrocket/gui/components/ColorChooserButton.java [new file with mode: 0644]
src/net/sf/openrocket/gui/components/ColorIcon.java [new file with mode: 0644]
src/net/sf/openrocket/gui/configdialog/RocketComponentConfig.java
src/net/sf/openrocket/gui/dialogs/AboutDialog.java
src/net/sf/openrocket/gui/dialogs/PrintDialog.java
src/net/sf/openrocket/gui/dialogs/PrintPanel.java [deleted file]
src/net/sf/openrocket/gui/dialogs/PrintSettingsDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/main/BasicFrame.java
src/net/sf/openrocket/gui/main/ExceptionHandler.java
src/net/sf/openrocket/gui/print/ConceptPrintDialog.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/DesignReport.java
src/net/sf/openrocket/gui/print/PDFPrintStreamDoc.java
src/net/sf/openrocket/gui/print/PaperOrientation.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/PaperSize.java
src/net/sf/openrocket/gui/print/PrintController.java
src/net/sf/openrocket/gui/print/PrintSettings.java [new file with mode: 0644]
src/net/sf/openrocket/gui/print/PrintUtilities.java
src/net/sf/openrocket/gui/print/PrintableContext.java
src/net/sf/openrocket/gui/print/TemplateProperties.java
src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java [deleted file]
src/net/sf/openrocket/gui/print/visitor/FinSetVisitorStrategy.java
src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java [deleted file]
src/net/sf/openrocket/gui/print/visitor/PartsDetailVisitorStrategy.java
src/net/sf/openrocket/gui/print/visitor/PartsListVisitorStrategy.java
src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java [deleted file]
src/net/sf/openrocket/l10n/ClassBasedTranslator.java [new file with mode: 0644]
src/net/sf/openrocket/l10n/DebugTranslator.java
src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java [new file with mode: 0644]
src/net/sf/openrocket/l10n/ResourceBundleTranslator.java
src/net/sf/openrocket/l10n/Translator.java
src/net/sf/openrocket/rocketcomponent/BodyComponent.java
src/net/sf/openrocket/rocketcomponent/BodyTube.java
src/net/sf/openrocket/rocketcomponent/CenteringRing.java
src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java [deleted file]
src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java [deleted file]
src/net/sf/openrocket/rocketcomponent/EllipticalFinSet.java
src/net/sf/openrocket/rocketcomponent/ExternalComponent.java
src/net/sf/openrocket/rocketcomponent/FreeformFinSet.java
src/net/sf/openrocket/rocketcomponent/InnerTube.java
src/net/sf/openrocket/rocketcomponent/LaunchLug.java
src/net/sf/openrocket/rocketcomponent/MassObject.java
src/net/sf/openrocket/rocketcomponent/NoseCone.java
src/net/sf/openrocket/rocketcomponent/RadiusRingComponent.java
src/net/sf/openrocket/rocketcomponent/RingComponent.java
src/net/sf/openrocket/rocketcomponent/Rocket.java
src/net/sf/openrocket/rocketcomponent/RocketComponent.java
src/net/sf/openrocket/rocketcomponent/Stage.java
src/net/sf/openrocket/rocketcomponent/Transition.java
src/net/sf/openrocket/rocketcomponent/TrapezoidFinSet.java
src/net/sf/openrocket/startup/Application.java
src/net/sf/openrocket/startup/Startup.java
src/net/sf/openrocket/util/AbstractChangeSource.java [new file with mode: 0644]
src/net/sf/openrocket/util/Prefs.java
test/net/sf/openrocket/gui/print/TestPaperSize.java [new file with mode: 0644]
test/net/sf/openrocket/l10n/TestClassBasedTranslator.java [new file with mode: 0644]
test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java [new file with mode: 0644]
web/html/actions/updates.php
web/html/download.html
web/html/index.html
web/html/menubg.png
web/htp/htp.def
web/htp/news.htp

index a7fe5f9a918e3c1fe4e9faae5d0db19c7d79fd25..78c5cf604971a1b68470f0c086d5c07be3b0931b 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,19 @@
+2011-06-10  Sampo Niskanen
+
+       * Released version 1.1.5
+
+2011-05-16  Sampo Niskanen
+
+       * Updated about dialog
+
+2011-04-15  Sampo Niskanen
+
+       * Removed native printing support
+
+2011-03-05  Sampo Niskanen
+
+       * Released version 1.1.4
+
 2011-01-20  Sampo Niskanen
 
        * Initial i18n support
index 4a6a4ce628af7f55de068e44db346604564d5455..61a98f5aaafb6fb70792b78c3233674d3400b374 100644 (file)
@@ -1,6 +1,6 @@
 OpenRocket - A model rocket simulator
 
-Copyright (C) 2007-2010 Sampo Niskanen
+Copyright (C) 2007-2011 Sampo Niskanen
 
 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
index 613117165ae332fbfe8638ee6c7d3ecc695f75de..c05aa498c905e8f9d6b8d2bd682ca81d248cd0b1 100644 (file)
@@ -1,3 +1,17 @@
+OpenRocket 1.1.5  (2011-06-10):
+-------------------------------
+
+Removed native printing support.  Printing is now handled via PDF
+viewer, which should make printing much more reliable and less
+bug-prone.
+
+
+OpenRocket 1.1.4  (2011-03-05):
+-------------------------------
+
+Initial printing support by Doug Pedrick, and various bug fixes.
+
+
 OpenRocket 1.1.3  (2010-10-06):
 -------------------------------
 
index fd13d5a763b6d91d2d4d7f9406e9e708c5512163..99a27987bfc59d60f6b8c2e6a2281e0509c00b7a 100644 (file)
@@ -1,7 +1,7 @@
 
 # The OpenRocket build version
 
-build.version=1.1.4pre
+build.version=1.1.6pre
 
 
 # The source of the package.  When building a package for a specific
index 30f62d25b5109c18b6956348dc39aa65dc75851f..84462ba713150f6ef3819f6baaa66c5e1abb7842 100644 (file)
--- a/build.xml
+++ b/build.xml
        <target name="todo" depends="checktodo"/>
        <target name="checktodo">
                <tempfile property="todo.file" prefix="checktodo-"/>
-               <echo>Checking project for critical TODOs.</echo>
+               <echo>Checking project for FIXMEs.</echo>
                <concat destfile="${todo.file}">
                        <fileset dir="${src.dir}">
                            <include name="**/*.java"/>
                        </fileset>
                        <filterchain>
                                <linecontainsregexp>
-                                       <regexp pattern="TODO:.*CRITICAL"/>
+                                       <regexp pattern="(FIXME|TODO:.*CRITICAL)"/>
                                </linecontainsregexp>
                        </filterchain>
                </concat>
index 65b4003eaf9f136eefea95b5e69b2e5c91d9162c..fcabdad2916c7c830abe78a17253a86517ca9a14 100644 (file)
@@ -83,10 +83,36 @@ dlg.but.ok = OK
 dlg.but.cancel = Cancel
 dlg.but.close = Close
 
+
+! General file type names
+filetypes.pdf = PDF files
+
+
 ! About Dialog
-about.dlg.but.close = Close
-about.dlg.lbl.translation = 
-about.dlg.lbl.transwebsite = 
+AboutDialog.lbl.version = Version
+! The texts below provide additional credits for the translation maintainer
+! - In AboutDialog.lbl.translation replace "English" with the current language.
+! - AboutDialog.lbl.translator is the translator / group name (may be empty)
+! - AboutDialog.lbl.translatorWebsite is a URL to the translator / group (may be empty)
+! - AboutDialog.lbl.translatorIcon is the file name of an icon under pix/translators/ (may be empty)
+AboutDialog.lbl.translation = English translation by:
+AboutDialog.lbl.translator = 
+AboutDialog.lbl.translatorWebsite = 
+AboutDialog.lbl.translatorIcon = 
+
+
+! Print dialog
+PrintDialog.title = Print or export
+PrintDialog.but.previewAndPrint = Preview & Print
+PrintDialog.checkbox.showByStage = Show by stage
+PrintDialog.lbl.selectElements = Select elements to include:
+printdlg.but.saveaspdf = Save as PDF
+printdlg.but.preview = Preview
+printdlg.but.settings = Settings
+PrintDialog.error.preview.title = Unable to open preview
+PrintDialog.error.preview.desc1 = Unable to open PDF preview.
+PrintDialog.error.preview.desc2 = Please use the "Save as PDF" option instead.
+
 
 ! Bug Report dialog
 bugreport.dlg.title = Bug report
@@ -138,10 +164,6 @@ exdesigndlg.lbl.Openexampledesign = Open example design
 exdesigndlg.lbl.Exampledesignsnotfound = Example designs could not be found.
 exdesigndlg.lbl.Examplesnotfound = Examples not found
 
-! Print panel dialog
-printdlg.but.saveaspdf = Save as PDF
-printdlg.but.preview = Preview
-printdlg.but.settings = Settings
 
 ! Material edit panel
 matedtpan.but.new = New
@@ -874,7 +896,7 @@ main.menu.file.open = Open...
 main.menu.file.openExample = Open example...
 main.menu.file.save = Save
 main.menu.file.saveAs = Save as...
-main.menu.file.print = Print...
+main.menu.file.print = Print / Export PDF...
 main.menu.file.close = Close
 main.menu.file.quit = Quit
 
@@ -934,7 +956,7 @@ Databases.materials.Polyethyleneheavy = Polyethylene (heavy)
 Databases.materials.Silk = Silk
 Databases.materials.Paperoffice = Paper (office)
 Databases.materials.Cellophane = Cellophane
-Databases.materials.Creape paper = Cr\u00eape paper
+Databases.materials.Crepepaper = Cr\u00eape paper
 ! LINE_MATERIAL
 Databases.materials.Threadheavy-duty = Thread (heavy-duty)
 Databases.materials.Elasticcordround2mm = Elastic cord (round 2mm, 1/16 in)
@@ -973,12 +995,8 @@ Shape.Ellipsoid = Ellipsoid
 Shape.Ellipsoid.desc1 = An ellipsoidal nose cone has a profile of a half-ellipse with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.
 Shape.Ellipsoid.desc2 = An ellipsoidal transition has a profile of a half-ellipse with major axes of lengths 2&times;<i>Length</i> and <i>Diameter</i>.  If the transition is not clipped, then the profile is extended at the center by the corresponding radius.             
 Shape.Powerseries = Power series
-Shape.Powerseries.descA1 = A power series nose cone has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 this is a <b>
-Shape.Powerseries.descA2 = -power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a <b>
-Shape.Powerseries.descA3 = -power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.
-Shape.Powerseries.descB1 = A power series transition has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 the transition is <b>
-Shape.Powerseries.descB2 = -power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a <b>
-Shape.Powerseries.descB3 = -power</b>, and for <i>k</i>=1 <b>conical</b>.
+Shape.Powerseries.desc1 = A power series nose cone has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 this is a <b>\u00BD-power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a <b>\u00BE-power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.
+Shape.Powerseries.desc2 = A power series transition has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 the transition is <b>\u00BD-power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a <b>\u00BE-power</b>, and for <i>k</i>=1 <b>conical</b>.
 Shape.Parabolicseries = Parabolic series
 Shape.Parabolicseries.desc1 = A parabolic series nose cone has a profile of a parabola.  The shape parameter defines the segment of the parabola to utilize.  The shape parameter 1.0 produces a <b>full parabola</b> which is tangent to the body tube, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a <b>1/2 parabola</b> and 0 produces a <b>conical</b> nose cone.
 Shape.Parabolicseries.desc2 = A parabolic series transition has a profile of a parabola.  The shape parameter defines the segment of the parabola to utilize.  The shape parameter 1.0 produces a <b>full parabola</b> which is tangent to the body tube at the aft end, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a <b>1/2 parabola</b> and 0 produces a <b>conical</b> transition.
index 9731785f1af156ea7bacdf107f50f2e4863e9e1a..bd51927fb259a5eece8925c61ee58ca7e0833677 100644 (file)
@@ -923,7 +923,7 @@ Databases.materials.Polyethyleneheavy = Polyethylen (schwer)
 Databases.materials.Silk = Seide\r
 Databases.materials.Paperoffice = Papier (Büro)\r
 Databases.materials.Cellophane = Zellophan\r
-Databases.materials.Creape paper = Krepppapier\r
+Databases.materials.Crepepaper = Krepppapier\r
 ! LINE_MATERIAL\r
 Databases.materials.Threadheavy-duty = Faden (stark)\r
 Databases.materials.Elasticcordround2mm = Elastikband (rund, 2mm, 1/16 in)\r
index 585ddbd8d62117bcf6e008f24450d3e95d12636e..b1aeebb9c3d8e406f7969dcc461da30806d6e7f1 100644 (file)
@@ -406,14 +406,14 @@ simplotpanel.CUSTOM = Casero
 ! Component add buttons
 compaddbuttons.Bodycompandfinsets = Componentes del fuselaje y aletas
 compaddbuttons.Nosecone = Ojiva
-compaddbuttons.Bodytube = Tubo del fuselaje
+compaddbuttons.Bodytube = Tubo del\nfuselaje
 compaddbuttons.Transition = Transición
 compaddbuttons.Trapezoidal = Trapezoidal
 compaddbuttons.Elliptical = Elíptico
 compaddbuttons.Freeform = Forma libre
-compaddbuttons.Launchlug = Guía en el fuselaje
+compaddbuttons.Launchlug = Guía en el\nfuselaje
 compaddbuttons.Innercomponent = Componente interior
-compaddbuttons.Innertube = Tubo interior
+compaddbuttons.Innertube = Tubo\ninterior
 compaddbuttons.Coupler = Acoplador
 compaddbuttons.Centeringring = Arandela\nde centraje
 compaddbuttons.Bulkhead = Cámara
@@ -421,7 +421,7 @@ compaddbuttons.Engineblock = Ret
 compaddbuttons.Massobjects = Objeto masa
 compaddbuttons.Parachute = Paracaídas
 compaddbuttons.Streamer = Banderola
-compaddbuttons.Shockcord = Tirante de suspensión
+compaddbuttons.Shockcord = Tirante de\nsuspensión
 compaddbuttons.Masscomponent = Componente\nmasa
 compaddbuttons.Donotaskmeagain = No me pregunte de nuevo
 compaddbuttons.Selectcomppos = Seleccionar la posición del componente
@@ -934,7 +934,7 @@ Databases.materials.Polyethyleneheavy = Polietileno (grueso)
 Databases.materials.Silk = Seda
 Databases.materials.Paperoffice = Papel (oficina)
 Databases.materials.Cellophane = Celofán
-Databases.materials.Creape paper = Cr\u00eape Papel
+Databases.materials.Crepepaper = Cr\u00eape Papel
 ! LINE_MATERIAL
 Databases.materials.Threadheavy-duty = Trenzado (Alta resistencia)
 Databases.materials.Elasticcordround2mm = Cordón elástico (aprox. 2mm, 1/16 in)
index e5c2f238663cbb367854661ee54521dc31e3e065..d4c0972aeedddfc88d53f3fae9a8b30c2850f549 100644 (file)
@@ -79,9 +79,9 @@ dlg.but.cancel = Annuler
 dlg.but.close = Fermer\r
 \r
 ! About Dialog\r
-about.dlg.but.close = Fermer\r
-about.dlg.lbl.translation = Traduit en français par Tripoli France\r
-about.dlg.lbl.transwebsite = http://tripoli.france.free.fr/\r
+AboutDialog.lbl.translation = Traduit en français par\r
+AboutDialog.lbl.translator = Tripoli France\r
+AboutDialog.lbl.translatorWebsite = http://tripoli.france.free.fr/\r
 \r
 ! Bug Report dialog\r
 bugreport.dlg.title = Rapport d'erreurs\r
@@ -403,22 +403,22 @@ simplotpanel.CUSTOM = Personnalis
 ! Component add buttons\r
 compaddbuttons.Bodycompandfinsets = Pièces du corps et ailerons\r
 compaddbuttons.Nosecone = Cône\r
-compaddbuttons.Bodytube = Tube du corps\r
+compaddbuttons.Bodytube = Tube du\ncorps\r
 compaddbuttons.Transition = Transition\r
 compaddbuttons.Trapezoidal = Trapézoïdale\r
 compaddbuttons.Elliptical = Elliptique\r
 compaddbuttons.Freeform = Forme libre\r
-compaddbuttons.Launchlug = Tube de guidage\r
+compaddbuttons.Launchlug = Tube de\nguidage\r
 compaddbuttons.Innercomponent = Pièces internes\r
 compaddbuttons.Innertube = Tube interne\r
 compaddbuttons.Coupler = Coupleur\r
-compaddbuttons.Centeringring = Anneau\nde centrage\r
+compaddbuttons.Centeringring = Anneau de\ncentrage\r
 compaddbuttons.Bulkhead = Cloison\r
-compaddbuttons.Engineblock = Bague \nde rétention\nmoteur\r
+compaddbuttons.Engineblock = Bague de\nrétention\nmoteur\r
 compaddbuttons.Massobjects = Pièces pleines\r
 compaddbuttons.Parachute = Parachute\r
 compaddbuttons.Streamer = Banderole\r
-compaddbuttons.Shockcord = Cordon amortisseur\r
+compaddbuttons.Shockcord = Cordon\namortisseur\r
 compaddbuttons.Masscomponent = Poids\r
 compaddbuttons.Donotaskmeagain = Ne plus me poser la question\r
 compaddbuttons.Selectcomppos = Position de la pièce\r
@@ -930,7 +930,7 @@ Databases.materials.Polyethyleneheavy = Poly
 Databases.materials.Silk = Soie\r
 Databases.materials.Paperoffice1 = Papier (bureau)\r
 Databases.materials.Cellophane = Cellophane\r
-Databases.materials.Creapepaper = Cr\u00eape paper\r
+Databases.materials.Crepepaper = Cr\u00eape paper\r
 ! LINE_MATERIAL\r
 Databases.materials.Threadheavy-duty = Fil(résistant)\r
 Databases.materials.Elasticcordround2mm = Corde Elastique (ronde 2mm, 1/16 in)\r
diff --git a/pix-src/icon/icon-large-close.xcf.gz b/pix-src/icon/icon-large-close.xcf.gz
new file mode 100644 (file)
index 0000000..9ed1129
Binary files /dev/null and b/pix-src/icon/icon-large-close.xcf.gz differ
index e48b765e43ffc987a0b6dd7d444b001c6447e0fa..da3b7c8351f371c120ca6abeaa6612f8d0b950ae 100644 (file)
@@ -16,12 +16,7 @@ Steps for making a release:
 11. Tag the version in SVN, URL:
     https://openrocket.svn.sourceforge.net/svnroot/openrocket/tags/Release_1.1.x
 12. Upload JAR and source distribution and ReleaseNotes to Sourceforge 
-     - Project Admin -> File Manager
-     - create new version directory under /openrocket
-     - upload JAR, ZIP and ReleaseNotes
-     - select ReleaseNotes properties, set as release notes
-     - select JAR properties, set release note file and default downloads
-     - select ZIP properties, set release note file
+     - Files -> Add Folder -> Add File
 13. Update HTML to web server:
     scp * plaa,openrocket@web.sourceforge.net:htdocs/
 14. Test downloading from Sourceforge and web site
index 52b935348e72a5ee676c865209fe35b608c9e560..fae1eb47eeb8090748a8f410adf2062d942dce0b 100644 (file)
@@ -17,7 +17,7 @@ import net.sf.openrocket.util.Prefs;
 public class Databases {
        private static final LogHelper log = Application.getLogger();
        private static final Translator trans = Application.getTranslator();
-
+       
        /* Static implementations of specific databases: */
 
        /**
@@ -67,7 +67,7 @@ public class Databases {
                SURFACE_MATERIAL.add(new Material.Surface(trans.get("Databases.materials.Silk"), 0.060, false));
                SURFACE_MATERIAL.add(new Material.Surface(trans.get("Databases.materials.Paperoffice"), 0.080, false));
                SURFACE_MATERIAL.add(new Material.Surface(trans.get("Databases.materials.Cellophane"), 0.018, false));
-               SURFACE_MATERIAL.add(new Material.Surface(trans.get("Databases.materials.Creapepaper"), 0.025, false));
+               SURFACE_MATERIAL.add(new Material.Surface(trans.get("Databases.materials.Crepepaper"), 0.025, false));
                
                //// Thread (heavy-duty)
                LINE_MATERIAL.add(new Material.Line(trans.get("Databases.materials.Threadheavy-duty"), 0.0003, false));
diff --git a/src/net/sf/openrocket/gui/components/ColorChooserButton.java b/src/net/sf/openrocket/gui/components/ColorChooserButton.java
new file mode 100644 (file)
index 0000000..15e4adc
--- /dev/null
@@ -0,0 +1,71 @@
+package net.sf.openrocket.gui.components;
+
+import java.awt.Color;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JColorChooser;
+import javax.swing.JDialog;
+import javax.swing.JPanel;
+
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
+
+/**
+ * A color chooser button.  The currently selected color can be queried or set using the
+ * {@link #getSelectedColor()} and {@link #setSelectedColor(Color)}, and changes listened
+ * to by listening to property events with property name {@link #COLOR_KEY}.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ColorChooserButton extends JButton {
+       private static final LogHelper log = Application.getLogger();
+       
+       public static final String COLOR_KEY = "selectedColor";
+       
+       
+       public ColorChooserButton(Color initial) {
+               
+               setSelectedColor(initial);
+               
+               // Add action listener that opens color chooser dialog
+               this.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               log.user("Activating color chooser");
+                               final JColorChooser chooser = new JColorChooser(getSelectedColor());
+                               chooser.setPreviewPanel(new JPanel());
+                               final JDialog dialog = JColorChooser.createDialog(ColorChooserButton.this, "Select color", true,
+                                               chooser, new ActionListener() {
+                                                       @Override
+                                                       public void actionPerformed(ActionEvent e2) {
+                                                               Color c = chooser.getColor();
+                                                               log.user("User selected color " + c);
+                                                               setSelectedColor(chooser.getColor());
+                                                       }
+                                               }, null);
+                               log.info("Closing color chooser");
+                               dialog.setVisible(true);
+                       }
+               });
+               
+       }
+       
+       
+       public void addColorPropertyChangeListener(PropertyChangeListener listener) {
+               this.addPropertyChangeListener(COLOR_KEY, listener);
+       }
+       
+       public void setSelectedColor(Color c) {
+               log.debug("Selecting color " + c);
+               this.setIcon(new ColorIcon(c));
+               this.putClientProperty(COLOR_KEY, c);
+       }
+       
+       public Color getSelectedColor() {
+               return (Color) this.getClientProperty(COLOR_KEY);
+       }
+       
+}
diff --git a/src/net/sf/openrocket/gui/components/ColorIcon.java b/src/net/sf/openrocket/gui/components/ColorIcon.java
new file mode 100644 (file)
index 0000000..747a426
--- /dev/null
@@ -0,0 +1,35 @@
+package net.sf.openrocket.gui.components;
+
+import java.awt.Color;
+
+import javax.swing.Icon;
+
+/**
+ * An Icon that displays a specific color, suitable for drawing into a button.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ColorIcon implements Icon {
+       private final Color color;
+       
+       public ColorIcon(Color c) {
+               this.color = c;
+       }
+       
+       @Override
+       public int getIconHeight() {
+               return 15;
+       }
+       
+       @Override
+       public int getIconWidth() {
+               return 25;
+       }
+       
+       @Override
+       public void paintIcon(java.awt.Component c, java.awt.Graphics g, int x, int y) {
+               g.setColor(color);
+               g.fill3DRect(x, y, getIconWidth(), getIconHeight(), false);
+       }
+       
+}
index 448a449c7295985c3ce07224ead21527878bf027..3779f5483876eda4cef9309dc882146f908b6cb6 100644 (file)
@@ -2,8 +2,6 @@ package net.sf.openrocket.gui.configdialog;
 
 
 import java.awt.Color;
-import java.awt.Component;
-import java.awt.Graphics;
 import java.awt.event.ActionEvent;
 import java.awt.event.ActionListener;
 import java.awt.event.FocusEvent;
@@ -13,7 +11,6 @@ import java.util.Iterator;
 import java.util.List;
 
 import javax.swing.BorderFactory;
-import javax.swing.Icon;
 import javax.swing.JButton;
 import javax.swing.JCheckBox;
 import javax.swing.JColorChooser;
@@ -33,6 +30,7 @@ import net.sf.openrocket.gui.adaptors.DoubleModel;
 import net.sf.openrocket.gui.adaptors.EnumModel;
 import net.sf.openrocket.gui.adaptors.MaterialModel;
 import net.sf.openrocket.gui.components.BasicSlider;
+import net.sf.openrocket.gui.components.ColorIcon;
 import net.sf.openrocket.gui.components.StyledLabel;
 import net.sf.openrocket.gui.components.StyledLabel.Style;
 import net.sf.openrocket.gui.components.UnitSelector;
@@ -151,7 +149,7 @@ public class RocketComponentConfig extends JPanel {
                
                // Component color and "Use default color" checkbox
                if (colorButton != null && colorDefault != null) {
-                       colorButton.setIcon(new ColorIcon(component.getColor()));
+                       colorButton.setIcon(new ColorIcon(getColor()));
                        
                        if ((component.getColor() == null) != colorDefault.isSelected())
                                colorDefault.setSelected(component.getColor() == null);
@@ -378,7 +376,7 @@ public class RocketComponentConfig extends JPanel {
                //// Component color:
                panel.add(new JLabel(trans.get("RocketCompCfg.lbl.Componentcolor")), "gapleft para, gapright 10lp");
                
-               colorButton = new JButton(new ColorIcon(component.getColor()));
+               colorButton = new JButton(new ColorIcon(getColor()));
                colorButton.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -443,7 +441,15 @@ public class RocketComponentConfig extends JPanel {
        }
        
        
-
+       private Color getColor() {
+               Color c = component.getColor();
+               if (c == null) {
+                       c = Prefs.getDefaultColor(component.getClass());
+               }
+               return c;
+       }
+       
+       
 
        protected JPanel shoulderTab() {
                JPanel panel = new JPanel(new MigLayout("fill"));
@@ -621,35 +627,6 @@ public class RocketComponentConfig extends JPanel {
        }
        
        
-       private class ColorIcon implements Icon {
-               private final Color color;
-               
-               public ColorIcon(Color c) {
-                       this.color = c;
-               }
-               
-               @Override
-               public int getIconHeight() {
-                       return 15;
-               }
-               
-               @Override
-               public int getIconWidth() {
-                       return 25;
-               }
-               
-               @Override
-               public void paintIcon(Component c, Graphics g, int x, int y) {
-                       if (color == null) {
-                               g.setColor(Prefs.getDefaultColor(component.getClass()));
-                       } else {
-                               g.setColor(color);
-                       }
-                       g.fill3DRect(x, y, getIconWidth(), getIconHeight(), false);
-               }
-               
-       }
-       
        protected void register(Invalidatable model) {
                this.invalidatables.add(model);
        }
@@ -660,4 +637,4 @@ public class RocketComponentConfig extends JPanel {
                }
        }
        
-}
+}
\ No newline at end of file
index f65749a205247695de68842418782b5324f25304..5eb2fde972b4565e04ae4778abc491dc16100a72 100644 (file)
@@ -10,7 +10,9 @@ import javax.swing.JLabel;
 import javax.swing.JPanel;
 
 import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.components.DescriptionArea;
 import net.sf.openrocket.gui.components.StyledLabel;
+import net.sf.openrocket.gui.components.StyledLabel.Style;
 import net.sf.openrocket.gui.components.URLLabel;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.startup.Application;
@@ -24,6 +26,16 @@ public class AboutDialog extends JDialog {
        public static final String OPENROCKET_URL = "http://openrocket.sourceforge.net/";
        private static final Translator trans = Application.getTranslator();
        
+       private static final String CREDITS = "<html><center>" +
+                       "<font size=\"+1\"><b>OpenRocket has been developed by:</b></font><br><br>" +
+                       "Sampo Niskanen (main developer)<br>" +
+                       "Doug Pedrick (RockSim file format, printing)<br>" +
+                       "Boris du Reau (internationalization, translation lead)<br><br>" +
+                       "<b>OpenRocket utilizes the following libraries:</b><br><br>" +
+                       "MiG Layout (http://www.miglayout.com/)<br>" +
+                       "JFreeChart (http://www.jfree.org/jfreechart/)<br>" +
+                       "iText (http://www.itextpdf.com/)";
+       
        
        public AboutDialog(JFrame parent) {
                super(parent, true);
@@ -31,38 +43,63 @@ public class AboutDialog extends JDialog {
                final String version = Prefs.getVersion();
                
                JPanel panel = new JPanel(new MigLayout("fill"));
+               JPanel sub;
                
-               panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")),
-                               "spany, top");
+
+               // OpenRocket logo
+               panel.add(new JLabel(Icons.loadImageIcon("pix/icon/icon-about.png", "OpenRocket")), "top");
                
-               panel.add(new StyledLabel("OpenRocket", 20), "ax 50%, growy, wrap para");
-               panel.add(new StyledLabel("Version " + version, 3), "ax 50%, growy, wrap rel");
+
+               // OpenRocket version info + copyright
+               sub = new JPanel(new MigLayout("fill"));
                
-               //              String source = Prefs.getBuildSource();
-               //              if (!Prefs.DEFAULT_BUILD_SOURCE.equalsIgnoreCase(source)) {
-               //                      panel.add(new StyledLabel("Distributed by " + source, -1), 
-               //                                      "ax 50%, growy, wrap para");
-               //              } else {
-               //                      panel.add(new StyledLabel(" ", -1), "ax 50%, growy, wrap para");
-               //              }
+               sub.add(new StyledLabel("OpenRocket", 20), "ax 50%, growy, wrap para");
+               sub.add(new StyledLabel(trans.get("lbl.version").trim() + " " + version, 3), "ax 50%, growy, wrap rel");
+               sub.add(new StyledLabel("Copyright " + Chars.COPY + " 2007-2011 Sampo Niskanen"), "ax 50%, growy, wrap para");
                
-               panel.add(new StyledLabel("Copyright " + Chars.COPY + " 2007-2010 Sampo Niskanen"),
-                               "ax 50%, growy, wrap para");
+               sub.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para");
+               panel.add(sub, "grow");
                
-               panel.add(new URLLabel(OPENROCKET_URL), "ax 50%, growy, wrap para");
+
+               // Translation information (if present)
+               String translation = trans.get("lbl.translation").trim();
+               String translator = trans.get("lbl.translator").trim();
+               String translatorWebsite = trans.get("lbl.translatorWebsite").trim();
+               String translatorIcon = trans.get("lbl.translatorIcon").trim();
                
-               //// translations author if language is not English
-               String translator = trans.get("about.dlg.lbl.translation").trim();
-               String translatorUrl = trans.get("about.dlg.lbl.transwebsite").trim();
-               if (translator.length() > 0) {
-                       panel.add(new StyledLabel(translator), "ax 50%, growy, wrap para");
-                       if (translatorUrl.length() > 0) {
-                               panel.add(new URLLabel(translatorUrl), "ax 50%, growy, wrap para");
+               if (translator.length() > 0 || translatorWebsite.length() > 0 || translatorIcon.length() > 0) {
+                       sub = new JPanel(new MigLayout("fill"));
+                       
+                       sub.add(new StyledLabel(translation, Style.BOLD), "ax 50%, growy, wrap para");
+                       
+                       if (translatorIcon.length() > 0) {
+                               sub.add(new JLabel(Icons.loadImageIcon("pix/translators/" + translatorIcon, translator)),
+                                               "ax 50%, growy, wrap para");
+                       }
+                       if (translator.length() > 0) {
+                               sub.add(new JLabel(translator), "ax 50%, growy, wrap rel");
+                       }
+                       if (translatorWebsite.length() > 0) {
+                               sub.add(new URLLabel(translatorWebsite), "ax 50%, growy, wrap para");
                        }
+                       
+                       panel.add(sub);
                }
                
+
+               DescriptionArea info = new DescriptionArea(5);
+               info.setText(CREDITS);
+               panel.add(info, "newline, width 10px, height 100lp, grow, spanx, wrap para");
+               
+               //              JTextArea area = new JTextArea(CREATORS);
+               //              area.setEditable(false);
+               //              area.setLineWrap(true);
+               //              area.setWrapStyleWord(true);
+               //              panel.add(new JScrollPane(area), "width 10px, height 100lp, grow, spanx, wrap para");
+               
+
                //Close button
-               JButton close = new JButton(trans.get("dlg.but.close"));
+               JButton close = new JButton(trans.get("button.close"));
                close.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
@@ -80,5 +117,4 @@ public class AboutDialog extends JDialog {
                GUIUtil.setDisposableDialogOptions(this, close);
        }
        
-
 }
index 06119318337be94e42fc64a8938024d6d62b23b5..c820c071e59e7358628c0a2f635b0d3d557eae8d 100644 (file)
 /*
- * PrintDialog.java
+ * PrintPanel.java
  */
 package net.sf.openrocket.gui.dialogs;
 
+import java.awt.Desktop;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.util.Enumeration;
+import java.util.Iterator;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JFileChooser;
+import javax.swing.JLabel;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.event.TreeSelectionEvent;
+import javax.swing.event.TreeSelectionListener;
+import javax.swing.filechooser.FileFilter;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import net.miginfocom.swing.MigLayout;
 import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.print.PDFPrintStreamDoc;
-import net.sf.openrocket.gui.print.PrintUtilities;
+import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.gui.print.PrintController;
+import net.sf.openrocket.gui.print.PrintSettings;
+import net.sf.openrocket.gui.print.PrintableContext;
+import net.sf.openrocket.gui.print.TemplateProperties;
+import net.sf.openrocket.gui.print.components.CheckTreeManager;
+import net.sf.openrocket.gui.print.components.RocketPrintTree;
 import net.sf.openrocket.l10n.Translator;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.rocketcomponent.Rocket;
 import net.sf.openrocket.startup.Application;
-import sun.print.ServiceDialog;
-
-import javax.print.DocFlavor;
-import javax.print.DocPrintJob;
-import javax.print.PrintException;
-import javax.print.PrintService;
-import javax.print.PrintServiceLookup;
-import javax.print.attribute.Attribute;
-import javax.print.attribute.AttributeSet;
-import javax.print.attribute.HashPrintRequestAttributeSet;
-import javax.print.attribute.PrintRequestAttributeSet;
-import javax.print.attribute.standard.Destination;
-import javax.print.attribute.standard.Fidelity;
-import javax.swing.JDialog;
-import javax.swing.JMenu;
-import javax.swing.JTabbedPane;
-import java.awt.Component;
-import java.awt.Container;
-import java.awt.Dialog;
-import java.awt.GraphicsConfiguration;
-import java.awt.GraphicsDevice;
-import java.awt.GraphicsEnvironment;
-import java.awt.HeadlessException;
-import java.awt.Rectangle;
-import java.io.ByteArrayOutputStream;
+import net.sf.openrocket.util.GUIUtil;
+import net.sf.openrocket.util.Prefs;
 
 /**
- * This class is not a dialog by inheritance, but is by delegation.  It front-ends a java print dialog by
- * augmenting it with application specific (rocket) settings.
+ * This class isolates the Swing components used to create a panel that is added to a standard Java print dialog.
  */
-public class PrintDialog {
-
-    /**
-     * The service UI dialog.
-     */
-    private ServiceDialog dialog;
-
-    /**
-     * A javax doc flavor specific for printing PDF documents.
-     */
-    private static final DocFlavor.INPUT_STREAM PDF = DocFlavor.INPUT_STREAM.PDF;
-
-    private static final Translator trans = Application.getTranslator();
-
-    /**
-     * Construct a print dialog using an Open Rocket document - which contains the rocket data to ultimately be
-     * printed.
-     *
-     * @param orDocument the rocket container
-     */
-    public PrintDialog (OpenRocketDocument orDocument) {
-        PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
-        PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
-        PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
-        attrs.add(PrintUtilities.getDefaultMedia().getMediaSizeName());
-
-        final PrintPanel panel = new PrintPanel(orDocument, this);
-        PrintService ps = printDialog(null, 100, 100, services, svc, PDF, attrs, panel);
-        if (ps != null) {
-            DocPrintJob dpj = ps.createPrintJob();
-            try {
-                System.err.println(attrs.size());
-                ByteArrayOutputStream baos = panel.generateReport();
-                dpj.print(new PDFPrintStreamDoc(baos, null), attrs);
-            }
-            catch (PrintException e) {
-                e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
-            }
-        }
-    }
-
-    /**
-     * Get the set of attributes from the service ui print dialog.
-     *
-     * @return a set of print attributes
-     */
-    PrintRequestAttributeSet getAttributes () {
-        return dialog.getAttributes();
-    }
-
-    /**
-     * Get the service ui dialog.  This is the actual dialog that gets displayed - we co-opt it for adding a rocket
-     * specific tab.
-     *
-     * @return the Java service ui print dialog
-     */
-    JDialog getDialog () {
-        return dialog;
-    }
-
-    /**
-     * Mimics the ServiceUI.printDialog method, but with enhancements for our own print settings tab.
-     *
-     * @param gc             used to select screen. null means primary or default screen.
-     * @param x              location of dialog including border in screen coordinates
-     * @param y              location of dialog including border in screen coordinates
-     * @param services       to be browsable, must be non-null.
-     * @param defaultService - initial PrintService to display.
-     * @param flavor         - the flavor to be printed, or null.
-     * @param attributes     on input is the initial application supplied preferences. This cannot be null but may be
-     *                       empty. On output the attributes reflect changes made by the user.
-     * @param addnl          a panel to be added, as a tab, to the internal tabbed pane of the resulting print dialog
-     *
-     * @return print service selected by the user, or null if the user cancelled the dialog.
-     *
-     * @throws HeadlessException        if GraphicsEnvironment.isHeadless() returns true.
-     * @throws IllegalArgumentException if services is null or empty, or attributes is null, or the initial PrintService
-     *                                  is not in the list of browsable services.
-     */
-    private PrintService printDialog (GraphicsConfiguration gc,
-                                      int x, int y,
-                                      PrintService[] services,
-                                      PrintService defaultService,
-                                      DocFlavor flavor,
-                                      PrintRequestAttributeSet attributes,
-                                      PrintPanel addnl)
-            throws HeadlessException {
-        int defaultIndex = -1;
-
-        if (GraphicsEnvironment.isHeadless()) {
-            throw new HeadlessException();
-        }
-        else if ((services == null) || (services.length == 0)) {
-            throw new IllegalArgumentException("services must be non-null " +
-                                               "and non-empty");
-        }
-        else if (attributes == null) {
-            throw new IllegalArgumentException("attributes must be non-null");
-        }
-
-        if (defaultService != null) {
-            for (int i = 0; i < services.length; i++) {
-                if (services[i].equals(defaultService)) {
-                    defaultIndex = i;
-                    break;
-                }
-            }
-
-            if (defaultIndex < 0) {
-                throw new IllegalArgumentException("services must contain " +
-                                                   "defaultService");
-            }
-        }
-        else {
-            defaultIndex = 0;
-        }
-
-        Rectangle gcBounds = (gc == null) ? GraphicsEnvironment.
-                getLocalGraphicsEnvironment().getDefaultScreenDevice().
-                getDefaultConfiguration().getBounds() : gc.getBounds();
-
-        dialog = new ServiceDialog(gc,
-                                   x + gcBounds.x,
-                                   y + gcBounds.y,
-                                   services, defaultIndex,
-                                   flavor, attributes,
-                                   (Dialog) null);
-        Rectangle dlgBounds = dialog.getBounds();
-
-        // get union of all GC bounds
-        GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
-        GraphicsDevice[] gs = ge.getScreenDevices();
-        for (GraphicsDevice g : gs) {
-            gcBounds = gcBounds.union(g.getDefaultConfiguration().getBounds());
-        }
-
-        // if portion of dialog is not within the gc boundary
-        if (!gcBounds.contains(dlgBounds)) {
-            // put in the center relative to parent frame/dialog
-            dialog.setLocationRelativeTo(null);
-        }
-        if (addnl != null && addnl.getTitle() != null) {
-            JTabbedPane tp = (JTabbedPane) getDescendantOfClass(JTabbedPane.class, dialog);
-            tp.add(addnl, addnl.getTitle(), 0);
-            tp.setSelectedIndex(0);
-        }
+public class PrintDialog extends JDialog implements TreeSelectionListener {
+       
+       private static final LogHelper log = Application.getLogger();
+       private static final Translator trans = Application.getTranslator();
+       
+       private final Desktop desktop;
+       
+       private final RocketPrintTree stagedTree;
+       private final RocketPrintTree noStagedTree;
+       private OpenRocketDocument document;
+       private RocketPrintTree currentTree;
+       
+       private JButton previewButton;
+       private JButton saveAsPDF;
+       private JButton cancel;
+       
+       /**
+        * Constructor.
+        *
+        * @param orDocument the OR rocket container
+        */
+       public PrintDialog(Window parent, OpenRocketDocument orDocument) {
+               super(parent, trans.get("title"), ModalityType.APPLICATION_MODAL);
+               
 
-        dialog.setVisible(true);
+               JPanel panel = new JPanel(new MigLayout("fill, gap rel unrel"));
+               this.add(panel);
+               
 
-        if (dialog.getStatus() == ServiceDialog.APPROVE) {
-            PrintRequestAttributeSet newas = dialog.getAttributes();
-            Class dstCategory = Destination.class;
-            Class fdCategory = Fidelity.class;
+               // before any Desktop APIs are used, first check whether the API is
+               // supported by this particular VM on this particular host
+               if (Desktop.isDesktopSupported()) {
+                       desktop = Desktop.getDesktop();
+               } else {
+                       desktop = null;
+               }
+               
+               document = orDocument;
+               Rocket rocket = orDocument.getRocket();
+               
+               noStagedTree = RocketPrintTree.create(rocket.getName());
+               noStagedTree.setShowsRootHandles(false);
+               CheckTreeManager ctm = new net.sf.openrocket.gui.print.components.CheckTreeManager(noStagedTree);
+               ctm.addTreeSelectionListener(this);
+               
+               final int stages = rocket.getStageCount();
+               
 
-            if (attributes.containsKey(dstCategory) &&
-                !newas.containsKey(dstCategory)) {
-                attributes.remove(dstCategory);
-            }
+               JLabel label = new JLabel(trans.get("lbl.selectElements"));
+               panel.add(label, "wrap unrel");
+               
+               // Create the tree
+               if (stages > 1) {
+                       stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
+                       ctm = new CheckTreeManager(stagedTree);
+                       stagedTree.setShowsRootHandles(false);
+                       ctm.addTreeSelectionListener(this);
+               } else {
+                       stagedTree = noStagedTree;
+               }
+               currentTree = stagedTree;
+               
+               // Add the tree to the UI
+               final JScrollPane scrollPane = new JScrollPane(stagedTree);
+               panel.add(scrollPane, "width 400lp, height 200lp, grow, wrap para");
+               
 
-            attributes.addAll(newas);
+               // Checkboxes and buttons
+               final JCheckBox sortByStage = new JCheckBox(trans.get("checkbox.showByStage"));
+               sortByStage.setEnabled(stages > 1);
+               sortByStage.setSelected(stages > 1);
+               sortByStage.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (sortByStage.isEnabled()) {
+                                       if (((JCheckBox) e.getSource()).isSelected()) {
+                                               scrollPane.setViewportView(stagedTree);
+                                               stagedTree.setExpandsSelectedPaths(true);
+                                               currentTree = stagedTree;
+                                       }
+                                       else {
+                                               scrollPane.setViewportView(noStagedTree);
+                                               noStagedTree.setExpandsSelectedPaths(true);
+                                               currentTree = noStagedTree;
+                                       }
+                               }
+                       }
+               });
+               panel.add(sortByStage, "aligny top, split");
+               
 
-            Fidelity fd = (Fidelity) attributes.get(fdCategory);
-            if (fd != null) {
-                if (fd == Fidelity.FIDELITY_TRUE) {
-                    removeUnsupportedAttributes(dialog.getPrintService(),
-                                                flavor, attributes);
-                }
-            }
-            return dialog.getPrintService();
-        }
-        else {
-            return null;
-        }
-    }
+               panel.add(new JPanel(), "growx");
+               
 
-    private Component getDescendantOfClass (Class c, Container cont) {
-        if (c == null || cont == null) {
-            return null;
-        }
-        Component[] children = (cont instanceof JMenu)
-                               ? ((JMenu) cont).getMenuComponents()
-                               : cont.getComponents();
-        for (int i = 0, n = children.length; i < n; i++) {
-            Component comp = children[i];
-            if (c.isInstance(comp)) {
-                return comp;
-            }
-            comp = getDescendantOfClass(c, (Container) comp);
-            if (comp != null) {
-                return comp;
-            }
-        }
-        return null;
-    }
+               JButton settingsButton = new JButton(trans.get("printdlg.but.settings"));
+               settingsButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               PrintSettings settings = Prefs.getPrintSettings();
+                               log.debug("settings=" + settings);
+                               PrintSettingsDialog settingsDialog = new PrintSettingsDialog(PrintDialog.this, settings);
+                               settingsDialog.setVisible(true);
+                               Prefs.setPrintSettings(settings);
+                       }
+               });
+               panel.add(settingsButton, "wrap para");
+               
 
-    /**
-     * Removes any attributes from the given AttributeSet that are unsupported by the given PrintService/DocFlavor
-     * combination.
-     * 
-     * @param ps      the print service for which unsupported attributes will be determined
-     * @param flavor  the document flavor; PDF in our case
-     * @param aset    the set of attributes requested
-     */
-    private static void removeUnsupportedAttributes (PrintService ps,
-                                                     DocFlavor flavor,
-                                                     AttributeSet aset) {
-        AttributeSet asUnsupported = ps.getUnsupportedAttributes(flavor,
-                                                                 aset);
+               previewButton = new JButton(trans.get("but.previewAndPrint"));
+               previewButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               onPreview();
+                               PrintDialog.this.setVisible(false);
+                       }
+               });
+               panel.add(previewButton, "split, right, gap para");
+               
 
-        if (asUnsupported != null) {
-            Attribute[] usAttrs = asUnsupported.toArray();
+               saveAsPDF = new JButton(trans.get("printdlg.but.saveaspdf"));
+               saveAsPDF.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               if (onSavePDF()) {
+                                       PrintDialog.this.setVisible(false);
+                               }
+                       }
+               });
+               panel.add(saveAsPDF, "right, gap para");
+               
 
-            for (Attribute usAttr : usAttrs) {
-                Class<? extends Attribute> category = usAttr.getCategory();
+               cancel = new JButton(trans.get("button.cancel"));
+               cancel.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               PrintDialog.this.setVisible(false);
+                       }
+               });
+               panel.add(cancel, "right, gap para");
+               
 
-                if (ps.isAttributeCategorySupported(category)) {
-                    Attribute attr =
-                            (Attribute) ps.getDefaultAttributeValue(category);
+               expandAll(currentTree, true);
+               if (currentTree != noStagedTree) {
+                       expandAll(noStagedTree, true);
+               }
+               
 
-                    if (attr != null) {
-                        aset.add(attr);
-                    }
-                    else {
-                        aset.remove(category);
-                    }
-                }
-                else {
-                    aset.remove(category);
-                }
-            }
-        }
-    }
+               GUIUtil.setDisposableDialogOptions(this, previewButton);
+               
+       }
+       
+       
+       @Override
+       public void valueChanged(final TreeSelectionEvent e) {
+               final TreePath path = e.getNewLeadSelectionPath();
+               if (path != null) {
+                       previewButton.setEnabled(true);
+                       saveAsPDF.setEnabled(true);
+               } else {
+                       previewButton.setEnabled(false);
+                       saveAsPDF.setEnabled(false);
+               }
+       }
+       
+       /**
+        * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
+        *
+        * @param theTree   the tree to expand/contract
+        * @param expand expand if true, contract if not
+        */
+       public void expandAll(RocketPrintTree theTree, boolean expand) {
+               TreeNode root = (TreeNode) theTree.getModel().getRoot();
+               // Traverse theTree from root
+               expandAll(theTree, new TreePath(root), expand);
+       }
+       
+       /**
+        * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
+        * the theTree.
+        *
+        * @param theTree   the tree to expand/contract
+        * @param parent the node to iterate/recurse over
+        * @param expand expand if true, contract if not
+        */
+       private void expandAll(RocketPrintTree theTree, TreePath parent, boolean expand) {
+               theTree.addSelectionPath(parent);
+               // Traverse children
+               TreeNode node = (TreeNode) parent.getLastPathComponent();
+               if (node.getChildCount() >= 0) {
+                       for (Enumeration<?> e = node.children(); e.hasMoreElements();) {
+                               TreeNode n = (TreeNode) e.nextElement();
+                               TreePath path = parent.pathByAddingChild(n);
+                               expandAll(theTree, path, expand);
+                       }
+               }
+               // Expansion or collapse must be done bottom-up
+               if (expand) {
+                       theTree.expandPath(parent);
+               } else {
+                       theTree.collapsePath(parent);
+               }
+       }
+       
+       
 
+       /**
+        * Generate a report using a temporary file.  The file will be deleted upon JVM exit.
+        *
+        * @param paper the name of the paper size
+        *
+        * @return a file, populated with the "printed" output (the rocket info)
+        *
+        * @throws IOException thrown if the file could not be generated
+        */
+       private File generateReport(PrintSettings settings) throws IOException {
+               final File f = File.createTempFile("openrocket-", ".pdf");
+               f.deleteOnExit();
+               return generateReport(f, settings);
+       }
+       
+       /**
+        * Generate a report to a specified file.
+        *
+        * @param f     the file to which rocket data will be written
+        * @param paper the name of the paper size
+        *
+        * @return a file, populated with the "printed" output (the rocket info)
+        *
+        * @throws IOException thrown if the file could not be generated
+        */
+       private File generateReport(File f, PrintSettings settings) throws IOException {
+               Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
+               new PrintController().print(document, toBePrinted, new FileOutputStream(f), settings);
+               return f;
+       }
+       
+       
+       /**
+        * Handler for when the Preview button is clicked.
+        */
+       private void onPreview() {
+               if (desktop != null) {
+                       try {
+                               PrintSettings settings = Prefs.getPrintSettings();
+                               // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
+                               TemplateProperties.setColors(settings);
+                               File f = generateReport(settings);
+                               desktop.open(f);
+                       } catch (IOException e) {
+                               log.error("Could not open preview.", e);
+                               JOptionPane.showMessageDialog(this, new String[] {
+                                               trans.get("error.preview.desc1"),
+                                               trans.get("error.preview.desc2") },
+                                               trans.get("error.preview.title"),
+                                               JOptionPane.ERROR_MESSAGE);
+                       }
+               } else {
+                       JOptionPane.showMessageDialog(this, new String[] {
+                                       trans.get("error.preview.desc1"),
+                                       trans.get("error.preview.desc2") },
+                                       trans.get("error.preview.title"),
+                                       JOptionPane.INFORMATION_MESSAGE);
+               }
+       }
+       
+       /**
+        * Handler for when the "Save as PDF" button is clicked.
+        *
+        * @return      true if the PDF was saved
+        */
+       private boolean onSavePDF() {
+               
+               JFileChooser chooser = new JFileChooser();
+               // Note: source for ExampleFileFilter can be found in FileChooserDemo,
+               // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
+               FileFilter filter = new FileFilter() {
+                       
+                       //Accept all directories and all pdf files.
+                       @Override
+                       public boolean accept(File f) {
+                               if (f.isDirectory())
+                                       return true;
+                               return f.getName().toLowerCase().endsWith(".pdf");
+                       }
+                       
+                       //The description of this filter
+                       @Override
+                       public String getDescription() {
+                               return trans.get("filetypes.pdf");
+                       }
+               };
+               chooser.setFileFilter(filter);
+               int returnVal = chooser.showSaveDialog(this);
+               if (returnVal == JFileChooser.APPROVE_OPTION) {
+                       
+                       try {
+                               String fname = chooser.getSelectedFile().getCanonicalPath();
+                               if (!getExtension(fname).equals("pdf")) {
+                                       fname = fname + ".pdf";
+                               }
+                               File f = new File(fname);
+                               PrintSettings settings = Prefs.getPrintSettings();
+                               // TODO: HIGH: Remove UIManager, and pass settings to the actual printing methods
+                               TemplateProperties.setColors(settings);
+                               generateReport(f, settings);
+                       } catch (IOException e) {
+                               ExceptionHandler.handleErrorCondition(e);
+                       }
+                       return true;
+               } else {
+                       return false;
+               }
+       }
+       
+       /**
+        * Get the extension of a file.
+        */
+       private static String getExtension(String s) {
+               String ext = null;
+               int i = s.lastIndexOf('.');
+               
+               if (i > 0 && i < s.length() - 1) {
+                       ext = s.substring(i + 1).toLowerCase();
+               }
+               return ext != null ? ext : "";
+       }
 }
diff --git a/src/net/sf/openrocket/gui/dialogs/PrintPanel.java b/src/net/sf/openrocket/gui/dialogs/PrintPanel.java
deleted file mode 100644 (file)
index 0044bfe..0000000
+++ /dev/null
@@ -1,442 +0,0 @@
-/*
- * PrintPanel.java
- */
-package net.sf.openrocket.gui.dialogs;
-
-import net.miginfocom.swing.MigLayout;
-import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.components.ColorChooser;
-import net.sf.openrocket.gui.print.PrintController;
-import net.sf.openrocket.gui.print.PrintUtilities;
-import net.sf.openrocket.gui.print.PrintableContext;
-import net.sf.openrocket.gui.print.TemplateProperties;
-import net.sf.openrocket.gui.print.components.CheckTreeManager;
-import net.sf.openrocket.gui.print.components.RocketPrintTree;
-import net.sf.openrocket.l10n.Translator;
-import net.sf.openrocket.logging.LogHelper;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.startup.Application;
-
-import javax.print.attribute.PrintRequestAttributeSet;
-import javax.print.attribute.standard.MediaSizeName;
-import javax.swing.JButton;
-import javax.swing.JCheckBox;
-import javax.swing.JColorChooser;
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JScrollPane;
-import javax.swing.UIManager;
-import javax.swing.event.TreeSelectionEvent;
-import javax.swing.event.TreeSelectionListener;
-import javax.swing.filechooser.FileFilter;
-import javax.swing.tree.TreeNode;
-import javax.swing.tree.TreePath;
-import java.awt.Color;
-import java.awt.Desktop;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.ByteArrayOutputStream;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.util.Enumeration;
-import java.util.Iterator;
-
-/**
- * This class isolates the Swing components used to create a panel that is added to the standard Java print dialog.
- */
-public class PrintPanel extends JPanel implements TreeSelectionListener {
-
-    private static final LogHelper log = Application.getLogger();
-       private static final Translator trans = Application.getTranslator();
-
-    private final RocketPrintTree stagedTree;
-    private final RocketPrintTree noStagedTree;
-    private OpenRocketDocument rocDoc;
-    private RocketPrintTree currentTree;
-    private boolean bDesktopSupported = false;
-    private Desktop desktop;
-    private PrintDialog printDialog;
-
-    JButton previewButton;
-    JButton saveAsPDF;
-    
-    /**
-     * Constructor.
-     *
-     * @param orDocument the OR rocket container
-     * @param theParent  the OR parent print dialog
-     */
-    public PrintPanel (OpenRocketDocument orDocument, PrintDialog theParent) {
-
-        super(new MigLayout("fill, gap rel unrel"));
-
-        // before any Desktop APIs are used, first check whether the API is
-        // supported by this particular VM on this particular host
-        if (Desktop.isDesktopSupported()) {
-            bDesktopSupported = true;
-            desktop = Desktop.getDesktop();
-        }
-
-        printDialog = theParent;
-        rocDoc = orDocument;
-        Rocket rocket = orDocument.getRocket();
-
-        noStagedTree = RocketPrintTree.create(rocket.getName());
-        noStagedTree.setShowsRootHandles(false);
-        CheckTreeManager ctm = new net.sf.openrocket.gui.print.components.CheckTreeManager(noStagedTree);
-        ctm.addTreeSelectionListener(this);
-
-        final int stages = rocket.getStageCount();
-
-        if (stages > 1) {
-            stagedTree = RocketPrintTree.create(rocket.getName(), rocket.getChildren());
-            ctm = new CheckTreeManager(stagedTree);
-            stagedTree.setShowsRootHandles(false);
-            ctm.addTreeSelectionListener(this);
-        }
-        else {
-            stagedTree = noStagedTree;
-        }
-        currentTree = stagedTree;
-
-        final JScrollPane scrollPane = new JScrollPane(stagedTree);
-        add(scrollPane, "width 475!, wrap");
-
-        final JCheckBox sortByStage = new JCheckBox("Show By Stage");
-        sortByStage.setEnabled(stages > 1);
-        sortByStage.setSelected(stages > 1);
-        sortByStage.addActionListener(new ActionListener() {
-            public void actionPerformed (ActionEvent e) {
-                if (sortByStage.isEnabled()) {
-                    if (((JCheckBox) e.getSource()).isSelected()) {
-                        scrollPane.setViewportView(stagedTree);
-                        stagedTree.setExpandsSelectedPaths(true);
-                        currentTree = stagedTree;
-                    }
-                    else {
-                        scrollPane.setViewportView(noStagedTree);
-                        noStagedTree.setExpandsSelectedPaths(true);
-                        currentTree = noStagedTree;
-                    }
-                }
-            }
-        });
-        add(sortByStage, "wrap");
-
-        //// Save as PDF button        
-        saveAsPDF = new JButton(trans.get("printdlg.but.saveaspdf"));
-        saveAsPDF.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed (ActionEvent e) {
-                onSavePDF(PrintPanel.this);
-            }
-        });
-        add(saveAsPDF, "span 2, tag save");
-
-        //// Preview button
-        previewButton = new JButton(trans.get("printdlg.but.preview"));
-        previewButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed (ActionEvent e) {
-                onPreview();
-            }
-        });
-        add(previewButton, "x 150");
-
-        //// Settings button
-        JButton settingsButton = new JButton(trans.get("printdlg.but.settings"));
-        settingsButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed (ActionEvent e) {
-                PrintSettingsDialog settingsDialog = new PrintSettingsDialog(printDialog.getDialog());
-                settingsDialog.setVisible(true);
-            }
-        });
-        add(settingsButton, "x 400");
-
-        expandAll(currentTree, true);
-        if (currentTree != noStagedTree) {
-            expandAll(noStagedTree, true);
-        }
-        setVisible(true);
-    }
-
-    /**
-     * The title of the tab that gets displayed for this panel, when placed in the print dialog.
-     *
-     * @return a title
-     */
-    public String getTitle () {
-        return "Rocket";
-    }
-
-    @Override 
-    public void valueChanged (final TreeSelectionEvent e) {
-        final TreePath path = e.getNewLeadSelectionPath();
-        if (path != null){
-            previewButton.setEnabled(true);
-            saveAsPDF.setEnabled(true);
-        }
-        else {
-            previewButton.setEnabled(false);
-            saveAsPDF.setEnabled(false);
-        }
-    }
-
-    /**
-     * If expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in the theTree.
-     *
-     * @param theTree   the tree to expand/contract
-     * @param expand expand if true, contract if not
-     */
-    public void expandAll (RocketPrintTree theTree, boolean expand) {
-        TreeNode root = (TreeNode) theTree.getModel().getRoot();
-        // Traverse theTree from root 
-        expandAll(theTree, new TreePath(root), expand);
-    }
-
-    /**
-     * Recursively walk a tree, and if expand is true, expands all nodes in the tree. Otherwise, collapses all nodes in
-     * the theTree.
-     *
-     * @param theTree   the tree to expand/contract
-     * @param parent the node to iterate/recurse over
-     * @param expand expand if true, contract if not
-     */
-    private void expandAll (RocketPrintTree theTree, TreePath parent, boolean expand) {
-        theTree.addSelectionPath(parent);
-        // Traverse children 
-        TreeNode node = (TreeNode) parent.getLastPathComponent();
-        if (node.getChildCount() >= 0) {
-            for (Enumeration e = node.children(); e.hasMoreElements();) {
-                TreeNode n = (TreeNode) e.nextElement();
-                TreePath path = parent.pathByAddingChild(n);
-                expandAll(theTree, path, expand);
-            }
-        }
-        // Expansion or collapse must be done bottom-up 
-        if (expand) {
-            theTree.expandPath(parent);
-        }
-        else {
-            theTree.collapsePath(parent);
-        }
-    }
-
-    /**
-     * Get a media size name (the name of a paper size).  If no page size is selected, it will default to the locale
-     * specific page size (LETTER in North America, A4 elsewhere).
-     *
-     * @return the name of a page size
-     */
-    private MediaSizeName getMediaSize () {
-        MediaSizeName paperSize = getMediaSize(printDialog.getAttributes());
-        if (paperSize == null) {
-            paperSize = PrintUtilities.getDefaultMedia().getMediaSizeName();
-        }
-        return paperSize;
-    }
-
-    /**
-     * Get the media size name (the name of a paper size) as selected by the user.
-     *
-     * @param atts the set of selected printer attributes
-     *
-     * @return a media size name, may be null
-     */
-    private MediaSizeName getMediaSize (PrintRequestAttributeSet atts) {
-        return (MediaSizeName) atts.get(javax.print.attribute.standard.Media.class);
-    }
-
-    /**
-     * Generate a report using a temporary file.  The file will be deleted upon JVM exit.
-     *
-     * @param paper the name of the paper size
-     *
-     * @return a file, populated with the "printed" output (the rocket info)
-     *
-     * @throws IOException thrown if the file could not be generated
-     */
-    private File generateReport (MediaSizeName paper) throws IOException {
-        final File f = File.createTempFile("oro", ".pdf");
-        f.deleteOnExit();
-        return generateReport(f, paper);
-    }
-
-    /**
-     * Generate a report to a specified file.
-     *
-     * @param f     the file to which rocket data will be written
-     * @param paper the name of the paper size
-     *
-     * @return a file, populated with the "printed" output (the rocket info)
-     *
-     * @throws IOException thrown if the file could not be generated
-     */
-    private File generateReport (File f, MediaSizeName paper) throws IOException {
-        Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
-        new PrintController().print(rocDoc, toBePrinted, new FileOutputStream(f), paper);
-        return f;
-    }
-
-    /**
-     * Generate a report to a byte array output stream.
-     *
-     * @return a stream populated with the "printed" output (the rocket info)
-     */
-    public ByteArrayOutputStream generateReport() {
-        Iterator<PrintableContext> toBePrinted = currentTree.getToBePrinted();
-        ByteArrayOutputStream baos = new ByteArrayOutputStream();
-        new PrintController().print(rocDoc, toBePrinted, baos, getMediaSize ());
-        return baos;
-    }
-
-    /**
-     * Handler for when the Preview button is clicked.
-     */
-    private void onPreview () {
-        if (bDesktopSupported) {
-            try {
-                MediaSizeName paperSize = getMediaSize();
-                File f = generateReport(paperSize);
-                desktop.open(f);
-            }
-            catch (IOException e) {
-                log.error("Could not create temporary file for previewing.", e);
-                JOptionPane.showMessageDialog(this, "Could not create a temporary file for previewing.",
-                                              "Error creating file", JOptionPane.ERROR_MESSAGE);
-            }
-        }
-        else {
-            JOptionPane.showMessageDialog(this,
-                                          "Your environment does not support automatically opening the default PDF viewer.",
-                                          "Error creating file", JOptionPane.INFORMATION_MESSAGE);
-        }
-    }
-
-    /**
-     * Handler for when the "Save as PDF" button is clicked.
-     *
-     * @param p the component to parent the save dialog to
-     */
-    private void onSavePDF (JComponent p) {
-
-        JFileChooser chooser = new JFileChooser();
-        // Note: source for ExampleFileFilter can be found in FileChooserDemo,
-        // under the demo/jfc directory in the Java 2 SDK, Standard Edition.
-        FileFilter filter = new FileFilter() {
-
-            //Accept all directories and all pdf files.
-            public boolean accept (File f) {
-                return true;
-            }
-
-            //The description of this filter
-            public String getDescription () {
-                return "pdf";
-            }
-        };
-        chooser.setFileFilter(filter);
-        int returnVal = chooser.showSaveDialog(p);
-        if (returnVal == JFileChooser.APPROVE_OPTION) {
-
-            try {
-                String fname = chooser.getSelectedFile().getCanonicalPath();
-                if (!getExtension(fname).equals("pdf")) {
-                    fname = fname + ".pdf";
-                }
-                File f = new File(fname);
-                generateReport(f, getMediaSize());
-            }
-            catch (IOException e) {
-                e.printStackTrace();
-            }
-        }
-    }
-
-    /**
-     * Get the extension of a file.
-     */
-    private static String getExtension (String s) {
-        String ext = null;
-        int i = s.lastIndexOf('.');
-
-        if (i > 0 && i < s.length() - 1) {
-            ext = s.substring(i + 1).toLowerCase();
-        }
-        return ext != null ? ext : "";
-    }
-}
-
-/**
- * This class is a dialog for displaying advanced settings for printing rocket related info.
- */
-class PrintSettingsDialog extends JDialog {
-
-    /**
-     * The fill color chooser.
-     */
-    private ColorChooser fill;
-
-    /**
-     * The line color chooser.
-     */
-    private ColorChooser line;
-    private static final Translator trans = Application.getTranslator();
-
-    /**
-     * Construct a dialog for setting the advanced rocket print settings.
-     *
-     * @param parent the owning dialog
-     */
-    public PrintSettingsDialog (JDialog parent) {
-
-       super(parent, "Advanced Settings", true);
-        setLayout(new MigLayout("fill"));
-
-        JPanel settingsPanel = new JPanel();
-        settingsPanel.setLayout(new MigLayout("gap rel"));
-
-        fill = addColorChooser(settingsPanel, "Template Fill", TemplateProperties.getFillColor());
-        line = addColorChooser(settingsPanel, "Template Line", TemplateProperties.getLineColor());
-
-        settingsPanel.add(fill);
-        settingsPanel.add(line);
-
-        add(settingsPanel, "wrap");
-
-      //Close button
-               JButton closeButton = new JButton(trans.get("dlg.but.close"));
-        closeButton.addActionListener(new ActionListener() {
-            @Override
-            public void actionPerformed (ActionEvent e) {
-                UIManager.put(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY, fill.getCurrentColor());
-                UIManager.put(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY, line.getCurrentColor());
-                dispose();
-            }
-        });
-        add(closeButton, "right, gapright para");
-
-        setSize(400, 200);
-    }
-
-    /**
-     * Add a color chooser to a panel.
-     *
-     * @param panel        the parent panel to add the color chooser.
-     * @param label        the label that indicates which color property is being changed
-     * @param initialColor the initial, or current, color to display
-     *
-     * @return a swing component containing a label, a colorized field, and a button that when clicked opens a color
-     *         chooser dialog
-     */
-    private ColorChooser addColorChooser (JPanel panel, String label, Color initialColor) {
-        final JColorChooser colorChooser = new JColorChooser(initialColor);
-        return new ColorChooser(panel, colorChooser, label);
-    }
-
-}
diff --git a/src/net/sf/openrocket/gui/dialogs/PrintSettingsDialog.java b/src/net/sf/openrocket/gui/dialogs/PrintSettingsDialog.java
new file mode 100644 (file)
index 0000000..8020bf4
--- /dev/null
@@ -0,0 +1,113 @@
+package net.sf.openrocket.gui.dialogs;
+
+import java.awt.Color;
+import java.awt.Window;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.gui.adaptors.EnumModel;
+import net.sf.openrocket.gui.components.ColorChooserButton;
+import net.sf.openrocket.gui.print.PaperOrientation;
+import net.sf.openrocket.gui.print.PaperSize;
+import net.sf.openrocket.gui.print.PrintSettings;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
+import net.sf.openrocket.util.GUIUtil;
+
+/**
+ * This class is a dialog for displaying advanced settings for printing rocket related info.
+ */
+public class PrintSettingsDialog extends JDialog {
+       private static final LogHelper log = Application.getLogger();
+       
+       
+       /**
+        * Construct a dialog for setting the advanced rocket print settings.
+        *
+        * @param parent the owning dialog
+        */
+       public PrintSettingsDialog(Window parent, final PrintSettings settings) {
+               super(parent, "Print settings", ModalityType.APPLICATION_MODAL);
+               
+
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+
+               panel.add(new JLabel("Template fill color:"));
+               final ColorChooserButton fillColorButton = new ColorChooserButton(settings.getTemplateFillColor());
+               fillColorButton.addColorPropertyChangeListener(new PropertyChangeListener() {
+                       @Override
+                       public void propertyChange(PropertyChangeEvent evt) {
+                               Color c = (Color) evt.getNewValue();
+                               log.info("Template fill color changed to " + c);
+                               settings.setTemplateFillColor(c);
+                       }
+               });
+               panel.add(fillColorButton, "wrap para");
+               
+
+               panel.add(new JLabel("Template border color:"));
+               final ColorChooserButton borderColorButton = new ColorChooserButton(settings.getTemplateBorderColor());
+               borderColorButton.addColorPropertyChangeListener(new PropertyChangeListener() {
+                       @Override
+                       public void propertyChange(PropertyChangeEvent evt) {
+                               Color c = (Color) evt.getNewValue();
+                               log.info("Template border color changed to " + c);
+                               settings.setTemplateBorderColor(c);
+                       }
+               });
+               panel.add(borderColorButton, "wrap para*2");
+               
+
+
+               JComboBox combo = new JComboBox(new EnumModel<PaperSize>(settings, "PaperSize"));
+               panel.add(new JLabel("Paper size:"));
+               panel.add(combo, "growx, wrap para");
+               
+
+               combo = new JComboBox(new EnumModel<PaperOrientation>(settings, "PaperOrientation"));
+               panel.add(new JLabel("Paper orientation:"));
+               panel.add(combo, "growx, wrap para*2");
+               
+
+
+
+
+               JButton button = new JButton("Reset");
+               button.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               log.user("Resetting print setting values to defaults");
+                               PrintSettings defaults = new PrintSettings();
+                               settings.loadFrom(defaults);
+                               fillColorButton.setSelectedColor(settings.getTemplateFillColor());
+                               borderColorButton.setSelectedColor(settings.getTemplateBorderColor());
+                       }
+               });
+               panel.add(button, "spanx, split, right");
+               
+
+               JButton closeButton = new JButton("Close");
+               closeButton.addActionListener(new ActionListener() {
+                       @Override
+                       public void actionPerformed(ActionEvent e) {
+                               PrintSettingsDialog.this.setVisible(false);
+                       }
+               });
+               panel.add(closeButton, "right");
+               
+               this.add(panel);
+               GUIUtil.setDisposableDialogOptions(this, closeButton);
+       }
+       
+
+}
index 966354bf8a1f0b3ab3f20c0307a28a5027bf269f..55df50394d49fbd9b3b5cbb00d384b031852ae69 100644 (file)
@@ -41,7 +41,6 @@ import javax.swing.JMenuItem;
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 import javax.swing.JScrollPane;
-import javax.swing.JSeparator;
 import javax.swing.JSplitPane;
 import javax.swing.JTabbedPane;
 import javax.swing.JTextField;
@@ -127,7 +126,7 @@ public class BasicFrame extends JFrame {
                        new SimpleFileFilter(trans.get("BasicFrame.SimpleFileFilter3"), ".rkt", ".rkt.gz");
        
 
-       
+
 
        public static final int COMPONENT_TAB = 0;
        public static final int SIMULATION_TAB = 1;
@@ -509,6 +508,7 @@ public class BasicFrame extends JFrame {
                item.addActionListener(new ActionListener() {
                        @Override
                        public void actionPerformed(ActionEvent e) {
+                               log.user("Print action selected");
                                printAction();
                        }
                });
@@ -1146,10 +1146,10 @@ public class BasicFrame extends JFrame {
                        log.info("Warnings while reading file: " + warnings);
                        WarningDialog.showWarnings(parent,
                                        new Object[] {
-                                       //// The following problems were encountered while opening
-                                       trans.get("BasicFrame.WarningDialog.txt1") +" " + filename + ".",
+                                                       //// The following problems were encountered while opening
+                                                       trans.get("BasicFrame.WarningDialog.txt1") + " " + filename + ".",
                                                        //// Some design features may not have been loaded correctly.
-                                       trans.get("BasicFrame.WarningDialog.txt2")
+                                                       trans.get("BasicFrame.WarningDialog.txt2")
                                        },
                                        //// Warnings while opening file
                                        trans.get("BasicFrame.WarningDialog.title"), warnings);
@@ -1306,9 +1306,9 @@ public class BasicFrame extends JFrame {
                        log.info("Confirming whether to save the design");
                        ComponentConfigDialog.hideDialog();
                        int result = JOptionPane.showConfirmDialog(this,
-                                       trans.get("BasicFrame.dlg.lbl1") + rocket.getName() + 
-                                       trans.get("BasicFrame.dlg.lbl2")+ "  " +
-                                       trans.get("BasicFrame.dlg.lbl3"),
+                                       trans.get("BasicFrame.dlg.lbl1") + rocket.getName() +
+                                                       trans.get("BasicFrame.dlg.lbl2") + "  " +
+                                                       trans.get("BasicFrame.dlg.lbl3"),
                                        trans.get("BasicFrame.dlg.title"), JOptionPane.YES_NO_CANCEL_OPTION,
                                        JOptionPane.QUESTION_MESSAGE);
                        if (result == JOptionPane.YES_OPTION) {
@@ -1349,7 +1349,14 @@ public class BasicFrame extends JFrame {
         * 
         */
        public void printAction() {
-               new PrintDialog(document);
+               if (!Prefs.getBoolean("printing.experimental.communicated", false)) {
+                       log.info("Showing printing is experimental warning to the user");
+                       JOptionPane.showMessageDialog(this, "Printing is an currently an experimental feature " +
+                                       "and might not fully work on all platforms",
+                                       "Experimental feature", JOptionPane.WARNING_MESSAGE);
+                       Prefs.putBoolean("printing.experimental.communicated", true);
+               }
+               new PrintDialog(this, document).setVisible(true);
        }
        
        /**
index cd4af70bad5fbfdc2bf97330383aaf34b4f79f0a..15e0cbf06b4a926952edf7df0b5cbc959916bb0b 100644 (file)
@@ -338,6 +338,22 @@ public class ExceptionHandler implements Thread.UncaughtExceptionHandler {
                        }
                }
                
+
+               /*
+                * Detect and ignore bug 6933331 in Sun JRE 1.6.0_18 and others
+                */
+               if (t instanceof IllegalStateException) {
+                       StackTraceElement[] trace = t.getStackTrace();
+                       
+                       if (trace.length > 1 &&
+                                       trace[0].getClassName().equals("sun.awt.windows.WComponentPeer") &&
+                                       trace[0].getMethodName().equals("getBackBuffer")) {
+                               log.warn("Ignoring Sun JRE bug 6933331 " +
+                                               "(see http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6933331): " + t);
+                               return true;
+                       }
+               }
+               
                /*
                 * Detect and ignore bug in Sun JRE 1.6.0_19
                 */
diff --git a/src/net/sf/openrocket/gui/print/ConceptPrintDialog.java b/src/net/sf/openrocket/gui/print/ConceptPrintDialog.java
new file mode 100644 (file)
index 0000000..9347ecb
--- /dev/null
@@ -0,0 +1,70 @@
+package net.sf.openrocket.gui.print;
+
+import java.awt.Window;
+import java.lang.reflect.InvocationTargetException;
+
+import javax.swing.JButton;
+import javax.swing.JCheckBox;
+import javax.swing.JDialog;
+import javax.swing.JLabel;
+import javax.swing.JList;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.SwingUtilities;
+
+import net.miginfocom.swing.MigLayout;
+import net.sf.openrocket.util.GUIUtil;
+
+public class ConceptPrintDialog extends JDialog {
+       
+       public ConceptPrintDialog() {
+               super((Window) null, "Print");
+               
+               JPanel panel = new JPanel(new MigLayout("fill"));
+               
+               JList list = new JList(new Object[] {
+                               "Model name",
+                               "Parts detail",
+                               "Fin templates",
+                               "Design report"
+               });
+               panel.add(new JScrollPane(list), "spanx, growx, wrap");
+               
+               JCheckBox checkbox = new JCheckBox("Show by stage");
+               panel.add(checkbox, "");
+               
+               JButton button = new JButton("Settings");
+               panel.add(button, "right, wrap para");
+               
+               JLabel label = new JLabel("<html>Printer: LaserJet 6L<br>Paper size: A4 Portrait");
+               panel.add(label);
+               
+               button = new JButton("Change");
+               panel.add(button, "right, wrap 20lp");
+               
+               panel.add(new JButton("Save as PDF"), "split, spanx, right");
+               panel.add(new JButton("Preview"), "right");
+               panel.add(new JButton("Print"), "right");
+               panel.add(new JButton("Close"), "right");
+               
+
+               this.add(panel);
+               
+       }
+       
+       
+
+       public static void main(String[] args) throws InterruptedException, InvocationTargetException {
+               SwingUtilities.invokeAndWait(new Runnable() {
+                       @Override
+                       public void run() {
+                               GUIUtil.setBestLAF();
+                               JDialog dialog = new ConceptPrintDialog();
+                               GUIUtil.setDisposableDialogOptions(dialog, null);
+                               dialog.setSize(450, 350);
+                               dialog.setVisible(true);
+                       }
+               });
+       }
+       
+}
index 247881e8071f9b9f0050bec8a2d975cb45d762a1..8bc1f0fd19dfd85f545cc6c52b49dbac29ec70ba 100644 (file)
@@ -3,37 +3,29 @@
  */
 package net.sf.openrocket.gui.print;
 
-import com.itextpdf.text.Document;
-import com.itextpdf.text.DocumentException;
-import com.itextpdf.text.Element;
-import com.itextpdf.text.Paragraph;
+import com.itextpdf.text.*;
 import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.pdf.BaseFont;
-import com.itextpdf.text.pdf.DefaultFontMapper;
-import com.itextpdf.text.pdf.PdfContentByte;
-import com.itextpdf.text.pdf.PdfPCell;
-import com.itextpdf.text.pdf.PdfPTable;
-import com.itextpdf.text.pdf.PdfWriter;
+import com.itextpdf.text.pdf.*;
 import net.sf.openrocket.document.OpenRocketDocument;
 import net.sf.openrocket.document.Simulation;
 import net.sf.openrocket.gui.figureelements.FigureElement;
 import net.sf.openrocket.gui.figureelements.RocketInfo;
-import net.sf.openrocket.gui.print.visitor.BaseVisitorStrategy;
-import net.sf.openrocket.gui.print.visitor.MotorMountVisitorStrategy;
-import net.sf.openrocket.gui.print.visitor.StageVisitorStrategy;
 import net.sf.openrocket.gui.scalefigure.RocketPanel;
+import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.motor.Motor;
-import net.sf.openrocket.rocketcomponent.ComponentVisitor;
-import net.sf.openrocket.rocketcomponent.Configuration;
-import net.sf.openrocket.rocketcomponent.Rocket;
+import net.sf.openrocket.rocketcomponent.*;
 import net.sf.openrocket.simulation.FlightData;
+import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.unit.Unit;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Prefs;
 
-import java.awt.Graphics2D;
+import java.awt.*;
 import java.io.IOException;
 import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
 import java.util.List;
 
 /**
@@ -67,7 +59,12 @@ import java.util.List;
  * <p/>
  * </pre>
  */
-public class DesignReport extends BaseVisitorStrategy {
+public class DesignReport {
+
+    /**
+     * The logger.
+     */
+    private static final LogHelper log = Application.getLogger();
 
     /**
      * The OR Document.
@@ -80,9 +77,34 @@ public class DesignReport extends BaseVisitorStrategy {
     final RocketPanel panel;
 
     /**
-     * A stage visitor.
+     * The iText document.
      */
-    private StageVisitorStrategy svs = new StageVisitorStrategy();
+    protected Document document;
+
+    /** The displayed strings. */
+    private static final String STAGES = "Stages: ";
+    private static final String MASS_WITH_MOTORS = "Mass (with motors): ";
+    private static final String MASS_WITH_MOTOR = "Mass (with motor): ";
+    private static final String MASS_EMPTY = "Mass (Empty): ";
+    private static final String STABILITY = "Stability: ";
+    private static final String CG = "Cg: ";
+    private static final String CP = "Cp: ";
+    private static final String MOTOR = "Motor";
+    private static final String AVG_THRUST = "Avg Thrust";
+    private static final String BURN_TIME = "Burn Time";
+    private static final String MAX_THRUST = "Max Thrust";
+    private static final String TOTAL_IMPULSE = "Total Impulse";
+    private static final String THRUST_TO_WT = "Thrust to Wt";
+    private static final String PROPELLANT_WT = "Propellant Wt";
+    private static final String SIZE = "Size";
+    private static final String ALTITUDE = "Altitude";
+    private static final String FLIGHT_TIME = "Flight Time";
+    private static final String TIME_TO_APOGEE = "Time to Apogee";
+    private static final String VELOCITY_OFF_PAD = "Velocity off Pad";
+    private static final String MAX_VELOCITY = "Max Velocity";
+    private static final String LANDING_VELOCITY = "Landing Velocity";
+    private static final String ROCKET_DESIGN = "Rocket Design";
+    private static final double GRAVITY_CONSTANT = 9.80665d;
 
     /**
      * Constructor.
@@ -91,7 +113,7 @@ public class DesignReport extends BaseVisitorStrategy {
      * @param theIDoc   the iText document
      */
     public DesignReport (OpenRocketDocument theRocDoc, Document theIDoc) {
-        super(theIDoc, null);
+        document = theIDoc;
         rocketDocument = theRocDoc;
         panel = new RocketPanel(rocketDocument);
     }
@@ -101,7 +123,7 @@ public class DesignReport extends BaseVisitorStrategy {
      *
      * @param writer a direct byte writer
      */
-    public void print (PdfWriter writer) {
+    public void writeToDocument (PdfWriter writer) {
         if (writer == null) {
             return;
         }
@@ -109,7 +131,7 @@ public class DesignReport extends BaseVisitorStrategy {
         int pageImageableWidth = (int) pageSize.getWidth() - (int) pageSize.getBorderWidth() * 2;
         int pageImageableHeight = (int) pageSize.getHeight() / 2 - (int) pageSize.getBorderWidthTop();
 
-        PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, "Rocket Design");
+        PrintUtilities.addText(document, PrintUtilities.BIG_BOLD, ROCKET_DESIGN);
 
         Rocket rocket = rocketDocument.getRocket();
         final Configuration configuration = rocket.getDefaultConfiguration();
@@ -130,10 +152,10 @@ public class DesignReport extends BaseVisitorStrategy {
                                                       BaseFont.EMBEDDED), PrintUtilities.NORMAL_FONT_SIZE);
         }
         catch (DocumentException e) {
-            e.printStackTrace();
+            log.error("Could not set font.", e);
         }
         catch (IOException e) {
-            e.printStackTrace();
+            log.error("Could not create font.", e);
         }
         int figHeightPts = (int) (PrintUnit.METERS.toPoints(figure.getFigureHeight()) * 0.4 * (scale / PrintUnit.METERS
                 .toPoints(1)));
@@ -145,25 +167,30 @@ public class DesignReport extends BaseVisitorStrategy {
 
         canvas.showText(rocketDocument.getRocket().getName());
 
-        canvas.newlineShowText("Stages: ");
+        canvas.newlineShowText(STAGES);
         canvas.showText("" + rocket.getStageCount());
 
 
         if (configuration.hasMotors()) {
-            canvas.newlineShowText("Mass (with motor" + ((configuration.getStageCount() > 1) ? "s): " : "): "));
+            if (configuration.getStageCount() > 1) {
+                canvas.newlineShowText(MASS_WITH_MOTORS);
+            }
+            else {
+                canvas.newlineShowText(MASS_WITH_MOTOR);
+            }
         }
         else {
-            canvas.newlineShowText("Mass (Empty): ");
+            canvas.newlineShowText(MASS_EMPTY);
         }
         canvas.showText(text.getMass(UnitGroup.UNITS_MASS.getDefaultUnit()));
 
-        canvas.newlineShowText("Stability: ");
+        canvas.newlineShowText(STABILITY);
         canvas.showText(text.getStability());
 
-        canvas.newlineShowText("Cg: ");
+        canvas.newlineShowText(CG);
         canvas.showText(text.getCg());
 
-        canvas.newlineShowText("Cp: ");
+        canvas.newlineShowText(CP);
         canvas.showText(text.getCp());
         canvas.endText();
 
@@ -180,12 +207,11 @@ public class DesignReport extends BaseVisitorStrategy {
 
             List<Double> stages = getStageWeights(rocket);
 
-
             for (int j = 0; j < mids.length; j++) {
                 String mid = mids[j];
                 if (mid != null) {
-                    MotorMountVisitorStrategy mmvs = new MotorMountVisitorStrategy(document, mid);
-                    rocket.accept(new ComponentVisitor(mmvs));
+                    final List<Motor> motorList = getMotorList(rocket, mid);
+
                     PdfPTable parent = new PdfPTable(2);
                     parent.setWidthPercentage(100);
                     parent.setHorizontalAlignment(Element.ALIGN_LEFT);
@@ -197,14 +223,38 @@ public class DesignReport extends BaseVisitorStrategy {
                         leading = 25;
                     }
                     addFlightData(rocket, mid, parent, leading);
-                    addMotorData(mmvs.getMotors(), parent, stages);
+                    addMotorData(motorList, parent, stages);
                     document.add(parent);
                 }
             }
         }
         catch (DocumentException e) {
-            e.printStackTrace();
+            log.error("Could not modify document.", e);
+        }
+    }
+
+    /**
+     * Get the motor list for all motor mounts.
+     *
+     * @param theRocket the rocket object
+     * @param theMid    the motor id
+     *
+     * @return a list of Motor
+     */
+    private List<Motor> getMotorList (final Rocket theRocket, final String theMid) {
+        Iterator<RocketComponent> components = theRocket.deepIterator();
+        final List<Motor> motorList = new ArrayList<Motor>();
+        while (components.hasNext()) {
+            RocketComponent rocketComponent = components.next();
+            if (rocketComponent instanceof MotorMount) {
+                MotorMount mm = (MotorMount) rocketComponent;
+                final Motor motor = mm.getMotor(theMid);
+                if (motor != null) {
+                    motorList.add(motor);
+                }
+            }
         }
+        return motorList;
     }
 
     /**
@@ -262,17 +312,17 @@ public class DesignReport extends BaseVisitorStrategy {
         motorTable.setWidthPercentage(68);
         motorTable.setHorizontalAlignment(Element.ALIGN_LEFT);
 
-        final PdfPCell motorCell = ITextHelper.createCell("Motor", PdfPCell.BOTTOM);
+        final PdfPCell motorCell = ITextHelper.createCell(MOTOR, PdfPCell.BOTTOM);
         final int mPad = 10;
         motorCell.setPaddingLeft(mPad);
         motorTable.addCell(motorCell);
-        motorTable.addCell(ITextHelper.createCell("Avg Thrust", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Burn Time", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Max Thrust", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Total Impulse", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Thrust to Wt", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Propellant Wt", PdfPCell.BOTTOM));
-        motorTable.addCell(ITextHelper.createCell("Size", PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(AVG_THRUST, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(BURN_TIME, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(MAX_THRUST, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(TOTAL_IMPULSE, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(THRUST_TO_WT, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(PROPELLANT_WT, PdfPCell.BOTTOM));
+        motorTable.addCell(ITextHelper.createCell(SIZE, PdfPCell.BOTTOM));
 
         DecimalFormat df = new DecimalFormat("#,##0.0#");
         for (int i = 0; i < motors.size(); i++) {
@@ -299,7 +349,7 @@ public class DesignReport extends BaseVisitorStrategy {
                     .UNITS_IMPULSE
                     .getDefaultUnit().toString(), border));
             double ttw = motor.getAverageThrustEstimate() / (getStageWeight(stageWeights, i) + (motor
-                    .getLaunchCG().weight * 9.80665));
+                    .getLaunchCG().weight * GRAVITY_CONSTANT));
             motorTable.addCell(ITextHelper.createCell(df.format(ttw) + ":1", border));
 
             motorTable.addCell(ITextHelper.createCell(df.format(motorWeight) + " " + UnitGroup.UNITS_MASS
@@ -330,7 +380,7 @@ public class DesignReport extends BaseVisitorStrategy {
     private void addFlightData (final Rocket theRocket, final String mid, final PdfPTable parent, int leading) {
         FlightData flight = null;
         if (theRocket.getMotorConfigurationIDs().length > 1) {
-            Rocket duplicate= theRocket.copyWithOriginalID();
+            Rocket duplicate = theRocket.copyWithOriginalID();
             Simulation simulation = Prefs.getBackgroundSimulation(duplicate);
             simulation.getConditions().setMotorConfigurationID(mid);
 
@@ -353,30 +403,30 @@ public class DesignReport extends BaseVisitorStrategy {
 
                     DecimalFormat df = new DecimalFormat("#,##0.0#");
 
-                    final PdfPCell cell = ITextHelper.createCell("Altitude", 2, 2);
+                    final PdfPCell cell = ITextHelper.createCell(ALTITUDE, 2, 2);
                     cell.setUseBorderPadding(false);
                     cell.setBorderWidthTop(0f);
                     labelTable.addCell(cell);
                     labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxAltitude()) + " " + distanceUnit,
                                                               2, 2));
 
-                    labelTable.addCell(ITextHelper.createCell("Flight Time", 2, 2));
+                    labelTable.addCell(ITextHelper.createCell(FLIGHT_TIME, 2, 2));
                     labelTable.addCell(ITextHelper.createCell(df.format(flight.getFlightTime()) + " " + flightUnit, 2,
                                                               2));
 
-                    labelTable.addCell(ITextHelper.createCell("Time to Apogee", 2, 2));
+                    labelTable.addCell(ITextHelper.createCell(TIME_TO_APOGEE, 2, 2));
                     labelTable.addCell(ITextHelper.createCell(df.format(flight.getTimeToApogee()) + " " + flightUnit, 2,
                                                               2));
 
-                    labelTable.addCell(ITextHelper.createCell("Velocity off Pad", 2, 2));
+                    labelTable.addCell(ITextHelper.createCell(VELOCITY_OFF_PAD, 2, 2));
                     labelTable.addCell(ITextHelper.createCell(df.format(
                             flight.getLaunchRodVelocity()) + " " + velocityUnit, 2, 2));
 
-                    labelTable.addCell(ITextHelper.createCell("Max Velocity", 2, 2));
+                    labelTable.addCell(ITextHelper.createCell(MAX_VELOCITY, 2, 2));
                     labelTable.addCell(ITextHelper.createCell(df.format(flight.getMaxVelocity()) + " " + velocityUnit,
                                                               2, 2));
 
-                    labelTable.addCell(ITextHelper.createCell("Landing Velocity", 2, 2));
+                    labelTable.addCell(ITextHelper.createCell(LANDING_VELOCITY, 2, 2));
                     labelTable.addCell(ITextHelper.createCell(df.format(
                             flight.getGroundHitVelocity()) + " " + velocityUnit, 2, 2));
 
@@ -388,7 +438,7 @@ public class DesignReport extends BaseVisitorStrategy {
                     parent.addCell(c);
                 }
                 catch (DocumentException e) {
-                    e.printStackTrace();
+                    log.error("Could not add flight data to document.", e);
                 }
             }
         }
@@ -428,20 +478,48 @@ public class DesignReport extends BaseVisitorStrategy {
     }
 
     /**
-     * Use a visitor to get the sorted list of Stage references, then from those get the stage masses and convert to
-     * weight.
+     * From a list of Stages get the stage masses and convert to weight.
      *
      * @param rocket the rocket
      *
      * @return a sorted list of Stage weights (mass * gravity), in Newtons
      */
     private List<Double> getStageWeights (Rocket rocket) {
-        rocket.accept(new ComponentVisitor(svs));
-        svs.close();
-        List<Double> stages = svs.getStages();
+        List<Double> stages = getStageMasses(rocket);
+
         for (int i = 0; i < stages.size(); i++) {
             Double stage = stages.get(i);
-            stages.set(i, stage * 9.80665);
+            stages.set(i, stage * GRAVITY_CONSTANT);
+        }
+        return stages;
+    }
+
+    /**
+     * From a list of Stages get the stage masses.
+     *
+     * @param rocket  the rocket
+     *
+     * @return a sorted list of Stage masses
+     */
+    private List<Double> getStageMasses (final Rocket rocket) {
+        Double mass = 0d;
+
+        List<Double> stages = new ArrayList<Double>();
+        Iterator<RocketComponent> iter = rocket.deepIterator();
+        while (iter.hasNext()) {
+            RocketComponent rocketComponent = iter.next();
+            if (rocketComponent instanceof Stage) {
+                if (mass > 0d) {
+                    stages.add(mass);
+                    mass = 0d;
+                }
+            }
+            else {
+                mass += rocketComponent.getMass();
+            }
+        }
+        if (mass > 0d) {
+            stages.add(mass);
         }
         return stages;
     }
index 4b6a56b972c0804cec839105cf39d07683230655..90cf46105c3c482be9982c46a75fe8c4e662d577 100644 (file)
@@ -7,19 +7,25 @@ import javax.print.Doc;
 import javax.print.DocFlavor;
 import javax.print.attribute.AttributeSetUtilities;
 import javax.print.attribute.DocAttributeSet;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.Reader;
+import java.io.*;
 
 /**
+ * This class implements a javax Doc specifically for PDF printing. All reports in OpenRocket are PDF (iText) based.
  */
 public class PDFPrintStreamDoc implements Doc {
 
+    /** The source stream of the PDF document. */
     private InputStream stream;
+
+    /** The document's attributes. */
     private DocAttributeSet attributeSet;
 
+    /**
+     * Constructor.
+     *
+     * @param ostream  an output stream representing the pdf doc
+     * @param attributes the attributes of the document
+     */
     public PDFPrintStreamDoc (ByteArrayOutputStream ostream, DocAttributeSet attributes) {
         stream = new ByteArrayInputStream(ostream.toByteArray());
         if (attributes != null) {
@@ -27,10 +33,17 @@ public class PDFPrintStreamDoc implements Doc {
         }
     }
 
+    /**
+     * Flavor is PDF.
+     *
+     * @return PDF flavor
+     */
+    @Override
     public DocFlavor getDocFlavor () {
         return DocFlavor.INPUT_STREAM.PDF;
     }
 
+    @Override
     public DocAttributeSet getAttributes () {
         return attributeSet;
     }
@@ -38,11 +51,17 @@ public class PDFPrintStreamDoc implements Doc {
     /* Since the data is to be supplied as an InputStream delegate to
      * getStreamForBytes().
      */
-
+    @Override
     public Object getPrintData () throws IOException {
         return getStreamForBytes();
     }
 
+    /**
+     * Intentionally null since the flavor is PDF.
+     *
+     * @return null
+     */
+    @Override
     public Reader getReaderForText () {
         return null;
     }
@@ -50,7 +69,7 @@ public class PDFPrintStreamDoc implements Doc {
     /* Return the print data as an InputStream.
      * Always return the same instance.
      */
-
+    @Override
     public InputStream getStreamForBytes () throws IOException {
         return stream;
     }
diff --git a/src/net/sf/openrocket/gui/print/PaperOrientation.java b/src/net/sf/openrocket/gui/print/PaperOrientation.java
new file mode 100644 (file)
index 0000000..0e973e0
--- /dev/null
@@ -0,0 +1,42 @@
+package net.sf.openrocket.gui.print;
+
+import com.itextpdf.text.Rectangle;
+import com.itextpdf.text.RectangleReadOnly;
+
+public enum PaperOrientation {
+       
+       PORTRAIT("Portrait") {
+               @Override
+               public Rectangle orient(Rectangle rect) {
+                       return new RectangleReadOnly(rect);
+               }
+       },
+       LANDSCAPE("Landscape") {
+               @Override
+               public Rectangle orient(Rectangle rect) {
+                       return new RectangleReadOnly(new Rectangle(rect).rotate());
+               }
+       };
+       
+
+       private final String name;
+       
+       private PaperOrientation(String name) {
+               this.name = name;
+       }
+       
+       /**
+        * Change the orientation of a portrait paper to the orientation represented by this
+        * orientation.
+        *  
+        * @param rect  the original paper size rectangle
+        * @return              the oriented paper size rectangle
+        */
+       public abstract Rectangle orient(Rectangle rect);
+       
+       
+       @Override
+       public String toString() {
+               return name;
+       }
+}
index e40d6f1cf569deae51f49c25799d1933daee6298..d3d07410570570791288f481d48ff3e4b3aa21f0 100644 (file)
-/*
- * PaperSize.java
- */
 package net.sf.openrocket.gui.print;
 
-import com.itextpdf.text.PageSize;
-import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.RectangleReadOnly;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.Locale;
 
-import javax.print.attribute.standard.MediaSizeName;
-import java.util.HashMap;
-import java.util.Map;
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
 
-/**
- * Various mappings of paper sizes.
- */
-public class PaperSize {
-    
-    private static Map<String, MediaSizeName> paperNames = new HashMap<String, MediaSizeName>();
-    private static Map<MediaSizeName, Rectangle> paperItext = new HashMap<MediaSizeName, Rectangle>();
-    
-    static {
-        populateNameMap();
-        populateITextSizeMap();
-    }
-    private PaperSize() {}
-    
-    public static MediaSizeName convert(String name) {
-        return paperNames.get(name);
-    }
+import com.itextpdf.text.PageSize;
+import com.itextpdf.text.Rectangle;
 
-    public static Rectangle convert(MediaSizeName name) {
-        return paperItext.get(name);
-    }
-    
-    private static void populateNameMap() {
-        paperNames.put("iso-a0", MediaSizeName.ISO_A0);
-        paperNames.put("iso-a1", MediaSizeName.ISO_A1);
-        paperNames.put("iso-a2", MediaSizeName.ISO_A2);
-        paperNames.put("iso-a3", MediaSizeName.ISO_A3);
-        paperNames.put("iso-a4", MediaSizeName.ISO_A4);
-        paperNames.put("iso-a5", MediaSizeName.ISO_A5);
-        paperNames.put("iso-a6", MediaSizeName.ISO_A6);
-        paperNames.put("iso-a7", MediaSizeName.ISO_A7);
-        paperNames.put("iso-a8", MediaSizeName.ISO_A8);
-        paperNames.put("iso-a9", MediaSizeName.ISO_A9);
-        paperNames.put("iso-a10", MediaSizeName.ISO_A10);
-        paperNames.put("iso-b0", MediaSizeName.ISO_B0);
-        paperNames.put("iso-b1", MediaSizeName.ISO_B1);
-        paperNames.put("iso-b2", MediaSizeName.ISO_B2);
-        paperNames.put("iso-b3", MediaSizeName.ISO_B3);
-        paperNames.put("iso-b4", MediaSizeName.ISO_B4);
-        paperNames.put("iso-b5", MediaSizeName.ISO_B5);
-        paperNames.put("iso-b6", MediaSizeName.ISO_B6);
-        paperNames.put("iso-b7", MediaSizeName.ISO_B7);
-        paperNames.put("iso-b8", MediaSizeName.ISO_B8);
-        paperNames.put("iso-b9", MediaSizeName.ISO_B9);
-        paperNames.put("iso-b10", MediaSizeName.ISO_B10);
-        paperNames.put("na-letter", MediaSizeName.NA_LETTER);
-        paperNames.put("na-legal", MediaSizeName.NA_LEGAL);
-        paperNames.put("na-8x10", MediaSizeName.NA_8X10);
-        paperNames.put("na-5x7", MediaSizeName.NA_5X7);
-        paperNames.put("executive", MediaSizeName.EXECUTIVE);
-        paperNames.put("folio", MediaSizeName.FOLIO);
-        paperNames.put("invoice", MediaSizeName.INVOICE);
-        paperNames.put("tabloid", MediaSizeName.TABLOID);
-        paperNames.put("ledger", MediaSizeName.LEDGER);
-        paperNames.put("quarto", MediaSizeName.QUARTO);
-        paperNames.put("iso-c0", MediaSizeName.ISO_C0);
-        paperNames.put("iso-c1", MediaSizeName.ISO_C1);
-        paperNames.put("iso-c2", MediaSizeName.ISO_C2);
-        paperNames.put("iso-c3", MediaSizeName.ISO_C3);
-        paperNames.put("iso-c4", MediaSizeName.ISO_C4);
-        paperNames.put("iso-c5", MediaSizeName.ISO_C5);
-        paperNames.put("iso-c6", MediaSizeName.ISO_C6);
-        paperNames.put("iso-designated-long", MediaSizeName.ISO_DESIGNATED_LONG);
-        paperNames.put("jis-b0", MediaSizeName.JIS_B0);
-        paperNames.put("jis-b1", MediaSizeName.JIS_B1);
-        paperNames.put("jis-b2", MediaSizeName.JIS_B2);
-        paperNames.put("jis-b3", MediaSizeName.JIS_B3);
-        paperNames.put("jis-b4", MediaSizeName.JIS_B4);
-        paperNames.put("jis-b5", MediaSizeName.JIS_B5);
-        paperNames.put("jis-b6", MediaSizeName.JIS_B6);
-        paperNames.put("jis-b7", MediaSizeName.JIS_B7);
-        paperNames.put("jis-b8", MediaSizeName.JIS_B8);
-        paperNames.put("jis-b9", MediaSizeName.JIS_B9);
-        paperNames.put("jis-b10", MediaSizeName.JIS_B10);
-        paperNames.put("a", MediaSizeName.A);
-        paperNames.put("b", MediaSizeName.B);
-        paperNames.put("c", MediaSizeName.C);
-        paperNames.put("d", MediaSizeName.D);
-        paperNames.put("e", MediaSizeName.E);
-    }
+public enum PaperSize {
+       A3("A3", PageSize.A3),
+       A4("A4", PageSize.A4),
+       A5("A5", PageSize.A5),
+       LETTER("Letter", PageSize.LETTER),
+       LEGAL("Legal", PageSize.LEGAL);
+       
+       private final String name;
+       private final Rectangle size;
+       
+       private PaperSize(String name, Rectangle size) {
+               this.name = name;
+               this.size = size;
+       }
+       
+       public Rectangle getSize() {
+               return size;
+       }
+       
+       @Override
+       public String toString() {
+               return name;
+       }
+       
+       
 
-    private static void populateITextSizeMap() {
-        paperItext.put(MediaSizeName.ISO_A0, PageSize.A0);
-        paperItext.put(MediaSizeName.ISO_A1, PageSize.A1);
-        paperItext.put(MediaSizeName.ISO_A2, PageSize.A2);
-        paperItext.put(MediaSizeName.ISO_A3, PageSize.A3);
-        paperItext.put(MediaSizeName.ISO_A4, PageSize.A4);
-        paperItext.put(MediaSizeName.ISO_A5, PageSize.A5);
-        paperItext.put(MediaSizeName.ISO_A6, PageSize.A6);
-        paperItext.put(MediaSizeName.ISO_A7, PageSize.A7);
-        paperItext.put(MediaSizeName.ISO_A8, PageSize.A8);
-        paperItext.put(MediaSizeName.ISO_A9, PageSize.A9);
-        paperItext.put(MediaSizeName.ISO_A10, PageSize.A10);
-        paperItext.put(MediaSizeName.ISO_B0, PageSize.B0);
-        paperItext.put(MediaSizeName.ISO_B1, PageSize.B1);
-        paperItext.put(MediaSizeName.ISO_B2, PageSize.B2);
-        paperItext.put(MediaSizeName.ISO_B3, PageSize.B3);
-        paperItext.put(MediaSizeName.ISO_B4, PageSize.B4);
-        paperItext.put(MediaSizeName.ISO_B5, PageSize.B5);
-        paperItext.put(MediaSizeName.ISO_B6, PageSize.B6);
-        paperItext.put(MediaSizeName.ISO_B7, PageSize.B7);
-        paperItext.put(MediaSizeName.ISO_B8, PageSize.B8);
-        paperItext.put(MediaSizeName.ISO_B9, PageSize.B9);
-        paperItext.put(MediaSizeName.ISO_B10, PageSize.B10);
-        paperItext.put(MediaSizeName.NA_LETTER, PageSize.LETTER);
-        paperItext.put(MediaSizeName.NA_LEGAL, PageSize.LEGAL);
-        paperItext.put(MediaSizeName.EXECUTIVE, PageSize.EXECUTIVE);
-        paperItext.put(MediaSizeName.A, PageSize.LETTER);
-        paperItext.put(MediaSizeName.B, PageSize._11X17);
-        paperItext.put(MediaSizeName.C, new RectangleReadOnly(PrintUnit.INCHES.toPoints(17), PrintUnit.INCHES.toPoints(22)));
-        paperItext.put(MediaSizeName.D, new RectangleReadOnly(PrintUnit.INCHES.toPoints(22), PrintUnit.INCHES.toPoints(34)));
-        paperItext.put(MediaSizeName.E, new RectangleReadOnly(PrintUnit.INCHES.toPoints(34), PrintUnit.INCHES.toPoints(44)));
-    }
-    
+       //////////////////////////
+       
+       private static final LogHelper log = Application.getLogger();
+       private static PaperSize defaultSize = null;
+       
+       /**
+        * Return the default paper size for the current system.
+        * @return      the default paper size
+        */
+       public static PaperSize getDefault() {
+               if (defaultSize == null) {
+                       
+                       // Test environment variable "PAPERSIZE" (Unix)
+                       defaultSize = getDefaultFromEnvironmentVariable();
+                       if (defaultSize != null) {
+                               log.info("Selecting default paper size from PAPERSIZE environment variable: " + defaultSize);
+                               return defaultSize;
+                       }
+                       
+                       // Test /etc/papersize (Unix)
+                       defaultSize = getDefaultFromEtcPapersize();
+                       if (defaultSize != null) {
+                               log.info("Selecting default paper size from /etc/papersize: " + defaultSize);
+                               return defaultSize;
+                       }
+                       
+                       // Test user.country
+                       defaultSize = getDefaultForCountry(System.getProperty("user.country"));
+                       if (defaultSize != null) {
+                               log.info("Selecting default paper size based on user.country: " + defaultSize);
+                               return defaultSize;
+                       }
+                       
+                       // Test locale country
+                       defaultSize = getDefaultForCountry(Locale.getDefault().getCountry());
+                       if (defaultSize != null) {
+                               log.info("Selecting default paper size based on locale country: " + defaultSize);
+                               return defaultSize;
+                       }
+                       
+                       // Fallback to A4
+                       defaultSize = A4;
+                       log.info("Selecting default paper size fallback: " + defaultSize);
+               }
+               
+               return defaultSize;
+       }
+       
+       
+       /**
+        * Attempt to read the default paper size from the "PAPERSIZE" environment variable.
+        * 
+        * @return      the default paper size if successful, or <code>null</code> if unable to read/parse file.
+        */
+       private static PaperSize getDefaultFromEnvironmentVariable() {
+               String str = System.getenv("PAPERSIZE");
+               return getSizeFromString(str);
+       }
+       
+       /**
+        * Attempt to read the default paper size from the file defined by the environment variable
+        * PAPERCONF or from /etc/papersize.
+        * 
+        * @return      the default paper size if successful, or <code>null</code> if unable to read/parse file.
+        */
+       private static PaperSize getDefaultFromEtcPapersize() {
+               
+               // Find file to read
+               String file = System.getenv("PAPERCONF");
+               if (file == null) {
+                       file = "/etc/papersize";
+               }
+               
+               // Attempt to read the file
+               BufferedReader in = null;
+               try {
+                       
+                       String str;
+                       in = new BufferedReader(new FileReader(file));
+                       while ((str = in.readLine()) != null) {
+                               if (str.matches("^\\s*(#.*|$)")) {
+                                       continue;
+                               }
+                               break;
+                       }
+                       
+                       return getSizeFromString(str);
+                       
+               } catch (IOException e) {
+                       
+                       // Could not read file
+                       return null;
+                       
+               } finally {
+                       if (in != null) {
+                               try {
+                                       in.close();
+                               } catch (IOException e) {
+                               }
+                       }
+               }
+       }
+       
+       
+       /**
+        * Get a paper size based on a string.  The string is trimmed and case-insensitively 
+        * compared to the base names of the paper sizes.
+        * 
+        * @param size  the size string (may be null)
+        * @return              the corresponding paper size, or null if unknown
+        */
+       static PaperSize getSizeFromString(String size) {
+               if (size == null) {
+                       return null;
+               }
+               
+               size = size.trim();
+               for (PaperSize p : PaperSize.values()) {
+                       if (p.name.equalsIgnoreCase(size)) {
+                               return p;
+                       }
+               }
+               return null;
+       }
+       
+       
+       /**
+        * Get default paper size for a specific country.  This method falls back to A4 for
+        * any country not known to use Letter.
+        * 
+        * @param country       the 2-char country code (may be null)
+        * @return                      the paper size, or <code>null</code> if country is not a country code
+        */
+       static PaperSize getDefaultForCountry(String country) {
+               /*
+                * List is based on info from http://unicode.org/repos/cldr-tmp/trunk/diff/supplemental/territory_language_information.html
+                * OpenOffice.org agrees with this:  http://wiki.services.openoffice.org/wiki/DefaultPaperSize#Summary
+                */
+               final String[] letterCountries = { "BZ", "CA", "CL", "CO", "CR", "SV", "GT", "MX", "NI", "PA", "PH", "PR", "US", "VE" };
+               
+               if (country == null || !country.matches("^[a-zA-Z][a-zA-Z]$")) {
+                       return null;
+               }
+               
+               country = country.toUpperCase();
+               for (String c : letterCountries) {
+                       if (c.equals(country)) {
+                               return LETTER;
+                       }
+               }
+               return A4;
+       }
+       
 }
index 1cb64c430e65dde7ec2316f779e62ec504465f96..0a71bbbe1cca1655d02710d07163016033a8442c 100644 (file)
@@ -4,6 +4,15 @@
  */
 package net.sf.openrocket.gui.print;
 
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.Iterator;
+import java.util.Set;
+
+import net.sf.openrocket.document.OpenRocketDocument;
+import net.sf.openrocket.gui.print.visitor.FinSetVisitorStrategy;
+import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy;
+
 import com.itextpdf.text.Document;
 import com.itextpdf.text.DocumentException;
 import com.itextpdf.text.ExceptionConverter;
@@ -11,112 +20,92 @@ import com.itextpdf.text.Rectangle;
 import com.itextpdf.text.pdf.PdfBoolean;
 import com.itextpdf.text.pdf.PdfName;
 import com.itextpdf.text.pdf.PdfWriter;
-import net.sf.openrocket.document.OpenRocketDocument;
-import net.sf.openrocket.gui.print.visitor.FinSetVisitorStrategy;
-import net.sf.openrocket.gui.print.visitor.PartsDetailVisitorStrategy;
-import net.sf.openrocket.rocketcomponent.ComponentVisitor;
-
-import javax.print.attribute.standard.MediaSizeName;
-import java.io.OutputStream;
-import java.util.Iterator;
-import java.util.Set;
 
 /**
  * This is the main active object for printing.  It performs all actions necessary to create and populate the print
  * file.
  */
 public class PrintController {
-
-    /**
-     * Print the selected components to a PDF document.
-     *
-     * @param doc         the OR document
-     * @param toBePrinted the user chosen items to print
-     * @param outputFile  the file being written to
-     * @param msn         the paper size
-     */
-    public void print (OpenRocketDocument doc, Iterator<PrintableContext> toBePrinted, OutputStream outputFile, MediaSizeName msn) {
-
-        Document idoc = new Document(convertWithDefault(msn));
-        PdfWriter writer = null;
-        try {
-            writer = PdfWriter.getInstance(idoc, outputFile);
-            writer.setStrictImageSequence(true);
-
-            writer.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
-            writer.addViewerPreference(PdfName.PICKTRAYBYPDFSIZE, PdfBoolean.PDFTRUE);
-            try {
-                idoc.open();
-                Thread.sleep(1000);
-            }
-            catch (InterruptedException e) {
-            }
-            while (toBePrinted.hasNext()) {
-                PrintableContext printableContext = toBePrinted.next();
-
-                Set<Integer> stages = printableContext.getStageNumber();
-
-                switch (printableContext.getPrintable()) {
-                    case DESIGN_REPORT:
-                        DesignReport dp = new DesignReport(doc, idoc);
-                        dp.print(writer);
-                        idoc.newPage();
-                        break;
-                    case FIN_TEMPLATE:
-                        final ComponentVisitor finVisitor = new ComponentVisitor(new FinSetVisitorStrategy(idoc,
-                                                                                                           writer,
-                                                                                                           stages));
-                        finVisitor.visit(doc.getRocket());
-                        finVisitor.close();
-                        break;
-                    case PARTS_DETAIL:
-                        final ComponentVisitor detailVisitor = new ComponentVisitor(new PartsDetailVisitorStrategy(idoc,
-                                                                                                                   writer,
-                                                                                                                   stages));
-                        detailVisitor.visit(doc.getRocket());
-                        detailVisitor.close();
-                        idoc.newPage();
-                        break;
-                    /*     case PARTS_LIST:
-                            final ComponentVisitor partsVisitor = new ComponentVisitor(new PartsListVisitorStrategy(idoc,
-                                                                                                                    writer,
-                                                                                                                    stages));
-                            partsVisitor.visit(doc.getRocket());
-                            partsVisitor.close();
-                            idoc.newPage();
-                            break;
-                    */
-                }
-            }
-            //Stupid iText throws a really nasty exception if there is no data when close is called.
-            if (writer.getCurrentDocumentSize() <= 140) {
-                writer.setPageEmpty(false);
-            }
-            writer.close();
-            idoc.close();
-        }
-        catch (DocumentException e) {
-        }
-        catch (ExceptionConverter ec) {
-        }
-/*        finally {
-            if (fileOutputStream != null) {
-                try {
-                    fileOutputStream.close();
-                }
-                catch (IOException e) {
-                }
-            }
-        }
-  */
-    }
-
-    private Rectangle convertWithDefault (final MediaSizeName msn) {
-        Rectangle result = PaperSize.convert(msn);
-        if (result == null) {
-            result = PaperSize.convert(PrintUtilities.getDefaultMedia().getMediaSizeName());
-        }
-        return result;
-    }
-
+       
+       /**
+        * Print the selected components to a PDF document.
+        *
+        * @param doc         the OR document
+        * @param toBePrinted the user chosen items to print
+        * @param outputFile  the file being written to
+        * @param msn         the paper size
+        */
+       public void print(OpenRocketDocument doc, Iterator<PrintableContext> toBePrinted, OutputStream outputFile,
+                                               PrintSettings settings) {
+               
+               Document idoc = new Document(getSize(settings));
+               PdfWriter writer = null;
+               try {
+                       writer = PdfWriter.getInstance(idoc, outputFile);
+                       writer.setStrictImageSequence(true);
+                       
+                       writer.addViewerPreference(PdfName.PRINTSCALING, PdfName.NONE);
+                       writer.addViewerPreference(PdfName.PICKTRAYBYPDFSIZE, PdfBoolean.PDFTRUE);
+                       try {
+                               idoc.open();
+                               Thread.sleep(1000);
+                       } catch (InterruptedException e) {
+                       }
+                       while (toBePrinted.hasNext()) {
+                               PrintableContext printableContext = toBePrinted.next();
+                               
+                               Set<Integer> stages = printableContext.getStageNumber();
+                               
+                               switch (printableContext.getPrintable()) {
+                               case DESIGN_REPORT:
+                                       DesignReport dp = new DesignReport(doc, idoc);
+                                       dp.writeToDocument(writer);
+                                       idoc.newPage();
+                                       break;
+                               case FIN_TEMPLATE:
+                                       final FinSetVisitorStrategy finWriter = new FinSetVisitorStrategy(idoc,
+                                                                                                                                                                                       writer,
+                                                                                                                                                                                       stages);
+                                       finWriter.writeToDocument(doc.getRocket());
+                                       break;
+                               case PARTS_DETAIL:
+                                       final PartsDetailVisitorStrategy detailVisitor = new PartsDetailVisitorStrategy(idoc,
+                                                                                                                                                                                                               writer,
+                                                                                                                                                                                                               stages);
+                                       detailVisitor.writeToDocument(doc.getRocket());
+                                       detailVisitor.close();
+                                       idoc.newPage();
+                                       break;
+                               }
+                       }
+                       //Stupid iText throws a really nasty exception if there is no data when close is called.
+                       if (writer.getCurrentDocumentSize() <= 140) {
+                               writer.setPageEmpty(false);
+                       }
+                       writer.close();
+                       idoc.close();
+               } catch (DocumentException e) {
+               } catch (ExceptionConverter ec) {
+               } finally {
+                       if (outputFile != null) {
+                               try {
+                                       outputFile.close();
+                               } catch (IOException e) {
+                               }
+                       }
+               }
+       }
+       
+       /**
+        * Get the correct paper size from the print settings.
+        * 
+        * @param settings      the print settings
+        * @return                      the paper size
+        */
+       private Rectangle getSize(PrintSettings settings) {
+               PaperSize size = settings.getPaperSize();
+               PaperOrientation orientation = settings.getPaperOrientation();
+               return orientation.orient(size.getSize());
+       }
+       
 }
diff --git a/src/net/sf/openrocket/gui/print/PrintSettings.java b/src/net/sf/openrocket/gui/print/PrintSettings.java
new file mode 100644 (file)
index 0000000..6537f40
--- /dev/null
@@ -0,0 +1,89 @@
+package net.sf.openrocket.gui.print;
+
+import java.awt.Color;
+
+import net.sf.openrocket.util.AbstractChangeSource;
+
+/**
+ * A class containing all printing settings.
+ */
+public class PrintSettings extends AbstractChangeSource {
+       
+       private Color templateFillColor = Color.LIGHT_GRAY;
+       private Color templateBorderColor = Color.DARK_GRAY;
+       
+       private PaperSize paperSize = PaperSize.getDefault();
+       private PaperOrientation paperOrientation = PaperOrientation.PORTRAIT;
+       
+       
+       public Color getTemplateFillColor() {
+               return templateFillColor;
+       }
+       
+       public void setTemplateFillColor(Color templateFillColor) {
+               // Implicitly tests against setting null
+               if (templateFillColor.equals(this.templateFillColor)) {
+                       return;
+               }
+               this.templateFillColor = templateFillColor;
+               fireChangeEvent();
+       }
+       
+       public Color getTemplateBorderColor() {
+               return templateBorderColor;
+       }
+       
+       public void setTemplateBorderColor(Color templateBorderColor) {
+               // Implicitly tests against setting null
+               if (templateBorderColor.equals(this.templateBorderColor)) {
+                       return;
+               }
+               this.templateBorderColor = templateBorderColor;
+               fireChangeEvent();
+       }
+       
+       public PaperSize getPaperSize() {
+               return paperSize;
+       }
+       
+       public void setPaperSize(PaperSize paperSize) {
+               if (paperSize.equals(this.paperSize)) {
+                       return;
+               }
+               this.paperSize = paperSize;
+               fireChangeEvent();
+       }
+       
+       public PaperOrientation getPaperOrientation() {
+               return paperOrientation;
+       }
+       
+       public void setPaperOrientation(PaperOrientation orientation) {
+               if (orientation.equals(paperOrientation)) {
+                       return;
+               }
+               this.paperOrientation = orientation;
+               fireChangeEvent();
+       }
+       
+       
+
+       /**
+        * Load settings from the specified print settings.
+        * @param settings      the settings to load
+        */
+       public void loadFrom(PrintSettings settings) {
+               this.templateFillColor = settings.templateFillColor;
+               this.templateBorderColor = settings.templateBorderColor;
+               this.paperSize = settings.paperSize;
+               this.paperOrientation = settings.paperOrientation;
+               fireChangeEvent();
+       }
+       
+       
+       @Override
+       public String toString() {
+               return "PrintSettings [templateFillColor=" + templateFillColor + ", templateBorderColor=" + templateBorderColor + ", paperSize=" + paperSize + ", paperOrientation=" + paperOrientation + "]";
+       }
+       
+}
index b2bc5c53ab22b95bdfaf2462ba3b69666b2ae363..7f096efcf03da2c8888056788fe1c80c34db0941 100644 (file)
 package net.sf.openrocket.gui.print;
 
 
-import com.itextpdf.text.Chunk;
-import com.itextpdf.text.Document;
-import com.itextpdf.text.DocumentException;
-import com.itextpdf.text.Font;
-import com.itextpdf.text.Paragraph;
-
-import javax.print.DocFlavor;
-import javax.print.PrintService;
-import javax.print.PrintServiceLookup;
-import javax.print.ServiceUI;
-import javax.print.attribute.HashPrintRequestAttributeSet;
-import javax.print.attribute.PrintRequestAttributeSet;
-import javax.print.attribute.standard.MediaSize;
-import javax.swing.RepaintManager;
 import java.awt.Component;
 import java.awt.Graphics;
 import java.awt.Graphics2D;
 import java.awt.print.PageFormat;
 import java.awt.print.Printable;
-import java.awt.print.PrinterException;
-import java.awt.print.PrinterJob;
-import java.util.Locale;
-
-public class PrintUtilities implements Printable {
-
-    public static final int NORMAL_FONT_SIZE = Font.DEFAULTSIZE - 3;
-    public static final int SMALL_FONT_SIZE = NORMAL_FONT_SIZE - 3;
-
-    public static final Font BOLD = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE, Font.BOLD);
-    public static final Font BIG_BOLD = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE + 3, Font.BOLD);
-    public static final Font BOLD_UNDERLINED = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE,
-                                                        Font.BOLD | Font.UNDERLINE);
-    public static final Font NORMAL = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE);
-    public static final Font SMALL = new Font(Font.FontFamily.HELVETICA, SMALL_FONT_SIZE);
-
-
-    private Component componentToBePrinted;
-
-    public static void printComponent (Component c) {
-        new PrintUtilities(c).print();
-    }
-
-    public PrintUtilities (Component componentToBePrinted) {
-        this.componentToBePrinted = componentToBePrinted;
-    }
 
-    public void print () {
-        PrinterJob printJob = PrinterJob.getPrinterJob();
-        printJob.setPrintable(this);
-        if (printJob.printDialog()) {
-            try {
-                printJob.print();
-            }
-            catch (PrinterException pe) {
-                System.out.println("Error printing: " + pe);
-            }
-        }
-    }
-
-    public int print (Graphics g, PageFormat pageFormat, int pageIndex) {
-        if (pageIndex > 0) {
-            return (NO_SUCH_PAGE);
-        }
-        else {
-            Graphics2D g2d = (Graphics2D) g;
-            translateToJavaOrigin(g2d, pageFormat);
-            disableDoubleBuffering(componentToBePrinted);
-            componentToBePrinted.paint(g2d);
-            enableDoubleBuffering(componentToBePrinted);
-            return (PAGE_EXISTS);
-        }
-    }
-
-    public static void disableDoubleBuffering (Component c) {
-        RepaintManager currentManager = RepaintManager.currentManager(c);
-        currentManager.setDoubleBufferingEnabled(false);
-    }
-
-    public static void enableDoubleBuffering (Component c) {
-        RepaintManager currentManager = RepaintManager.currentManager(c);
-        currentManager.setDoubleBufferingEnabled(true);
-    }
-
-    public static PrintService askUserForPDFPrintService () {
-        return askUserForPrintService(DocFlavor.INPUT_STREAM.PDF);
-    }
-
-    public static PrintService askUserForPrintService (DocFlavor flavor) {
-        PrintService[] services = PrintServiceLookup.lookupPrintServices(null, null);
-        PrintService svc = PrintServiceLookup.lookupDefaultPrintService();
-        PrintRequestAttributeSet attrs = new HashPrintRequestAttributeSet();
-        attrs.add(getDefaultMedia().getMediaSizeName());
-
-        return ServiceUI.printDialog(null, 100, 100, services, svc, flavor, attrs);
-    }
+import javax.swing.RepaintManager;
 
-    /**
-     * Sets the paper size for pages using these attributes to the default size for the default locale. The default size
-     * for locales in the United States and Canada is MediaType.NA_LETTER. The default size for all other locales is
-     * MediaType.ISO_A4.
-     */
-    public static MediaSize getDefaultMedia () {
-        String defaultCountry = Locale.getDefault().getCountry();
-        if (defaultCountry != null &&
-            (defaultCountry.equals(Locale.US.getCountry()) ||
-             defaultCountry.equals(Locale.CANADA.getCountry()))) {
-            return MediaSize.NA.LETTER;
-        }
-        else {
-            return MediaSize.ISO.A4;
-        }
-    }
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
 
-    /**
-     * Translate the page format coordinates onto the graphics object using Java's origin (top left).
-     *
-     * @param g2d        the graphics object
-     * @param pageFormat the print page format
-     */
-    public static void translateToJavaOrigin (Graphics2D g2d, PageFormat pageFormat) {
-        g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
-    }
+import com.itextpdf.text.Chunk;
+import com.itextpdf.text.Document;
+import com.itextpdf.text.DocumentException;
+import com.itextpdf.text.Font;
+import com.itextpdf.text.Paragraph;
 
-    public static void addText (Document document, com.itextpdf.text.Font font, String title) {
-        Chunk sectionHeader = new Chunk(title);
-        sectionHeader.setFont(font);
-        try {
-            Paragraph p = new Paragraph();
-            p.add(sectionHeader);
-            document.add(p);
-        }
-        catch (DocumentException e) {
-            e.printStackTrace();
-        }
-    }
+/**
+ * Utilities methods and fonts used for printing.
+ */
+public class PrintUtilities implements Printable {
+       
+       /**
+        * The logger.
+        */
+       private static final LogHelper log = Application.getLogger();
+       
+       public static final int NORMAL_FONT_SIZE = Font.DEFAULTSIZE - 3;
+       public static final int SMALL_FONT_SIZE = NORMAL_FONT_SIZE - 3;
+       
+       public static final Font BOLD = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE, Font.BOLD);
+       public static final Font BIG_BOLD = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE + 3, Font.BOLD);
+       public static final Font BOLD_UNDERLINED = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE,
+                                                                                                               Font.BOLD | Font.UNDERLINE);
+       public static final Font NORMAL = new Font(Font.FontFamily.HELVETICA, NORMAL_FONT_SIZE);
+       public static final Font SMALL = new Font(Font.FontFamily.HELVETICA, SMALL_FONT_SIZE);
+       
+
+       private Component componentToBePrinted;
+       
+       public PrintUtilities(Component componentToBePrinted) {
+               this.componentToBePrinted = componentToBePrinted;
+       }
+       
+       @Override
+       public int print(Graphics g, PageFormat pageFormat, int pageIndex) {
+               if (pageIndex > 0) {
+                       return (NO_SUCH_PAGE);
+               } else {
+                       Graphics2D g2d = (Graphics2D) g;
+                       translateToJavaOrigin(g2d, pageFormat);
+                       disableDoubleBuffering(componentToBePrinted);
+                       componentToBePrinted.paint(g2d);
+                       enableDoubleBuffering(componentToBePrinted);
+                       return (PAGE_EXISTS);
+               }
+       }
+       
+       public static void disableDoubleBuffering(Component c) {
+               RepaintManager currentManager = RepaintManager.currentManager(c);
+               currentManager.setDoubleBufferingEnabled(false);
+       }
+       
+       public static void enableDoubleBuffering(Component c) {
+               RepaintManager currentManager = RepaintManager.currentManager(c);
+               currentManager.setDoubleBufferingEnabled(true);
+       }
+       
+       
+       /**
+        * Translate the page format coordinates onto the graphics object using Java's origin (top left).
+        *
+        * @param g2d        the graphics object
+        * @param pageFormat the print page format
+        */
+       public static void translateToJavaOrigin(Graphics2D g2d, PageFormat pageFormat) {
+               g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
+       }
+       
+       /**
+        * Add text as a new paragraph in a given font to the document.
+        *
+        * @param document  the document
+        * @param font      the font
+        * @param title     the title
+        */
+       public static void addText(Document document, com.itextpdf.text.Font font, String title) {
+               Chunk sectionHeader = new Chunk(title);
+               sectionHeader.setFont(font);
+               try {
+                       Paragraph p = new Paragraph();
+                       p.add(sectionHeader);
+                       document.add(p);
+               } catch (DocumentException e) {
+                       log.error("Could not add paragraph.", e);
+               }
+       }
 }
index 6645b6960b010ac19b14d99fed749d98ee6811cf..1f37435dae318219a381b115d450694ca34a81bc 100644 (file)
@@ -4,11 +4,7 @@
  */
 package net.sf.openrocket.gui.print;
 
-import java.util.Iterator;
-import java.util.Map;
-import java.util.Set;
-import java.util.TreeMap;
-import java.util.TreeSet;
+import java.util.*;
 
 /**
  * Instances of this class are meant to keep track of what the user has selected to be printed.
@@ -25,9 +21,14 @@ public class PrintableContext implements Comparable<PrintableContext>, Iterable<
      */
     private OpenRocketPrintable printable;
 
+    /**
+     * Sort of a reverse map that tracks each type of printable item and the stages for which that item is to be printed.
+     */
     private final Map<OpenRocketPrintable, Set<Integer>> previous = new TreeMap<OpenRocketPrintable, Set<Integer>>();
 
-    
+    /**
+     * Constructor.
+     */
     public PrintableContext () {
     }
 
@@ -48,8 +49,13 @@ public class PrintableContext implements Comparable<PrintableContext>, Iterable<
         printable = thePrintable;
     }
 
-    public void add (final Integer theStageNumber, final OpenRocketPrintable thePrintable)
-            throws IllegalArgumentException {
+    /**
+     * Add a type of printable to a stage (number).
+     *
+     * @param theStageNumber  the stage number
+     * @param thePrintable    the printable to associate with the stage
+     */
+    public void add (final Integer theStageNumber, final OpenRocketPrintable thePrintable) {
         Set<Integer> stages = previous.get(thePrintable);
         if (stages == null) {
             stages = new TreeSet<Integer>();
@@ -60,7 +66,7 @@ public class PrintableContext implements Comparable<PrintableContext>, Iterable<
         }
     }
 
-
+    /** PrintableContext iterator. */
     public Iterator<PrintableContext> iterator () {
         return new Iterator<PrintableContext>() {
 
index f98f7ea36c265c54a51414f8a4d0aacb78e49130..6f88e576bc2a1c0bc1d64b166a63a8dfd0996d6d 100644 (file)
@@ -3,47 +3,74 @@
  */
 package net.sf.openrocket.gui.print;
 
-import javax.swing.UIManager;
 import java.awt.Color;
 
+import javax.swing.UIManager;
+
 /**
  * This class is responsible for managing various properties of print templates (fin, nose cone, transitions, etc.).
+ * 
+ * TODO: HIGH:  Remove this entire class, and instead pass the PrintSettings object to the print methods.
  */
 public class TemplateProperties {
-
-    /**
-     * The property that defines the fill color.
-     */
-    public static final String TEMPLATE_FILL_COLOR_PROPERTY = "template.fill.color";
-
-    /**
-     * The property that defines the line color.
-     */
-    public static final String TEMPLATE_LINE_COLOR_PROPERTY = "template.line.color";
-
-    /**
-     * Get the current fill color.
-     * 
-     * @return  a color to be used as the fill in template shapes
-     */
-    public static Color getFillColor () {
-        Color fillColor = UIManager.getColor(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY);
-        if (fillColor == null) {
-            fillColor = Color.lightGray;
-        }
-        return fillColor;
-    }
-
-    /**
-     * Get the current line color.
-     * 
-     * @return  a color to be used as the line in template shapes
-     */
-    public static Color getLineColor () {
-        Color lineColor = UIManager.getColor(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY);
-        if (lineColor == null) {
-            lineColor = Color.darkGray;
-        }
-        return lineColor;
-    }
+       
+       /**
+        * The property that defines the fill color.
+        */
+       public static final String TEMPLATE_FILL_COLOR_PROPERTY = "template.fill.color";
+       
+       /**
+        * The property that defines the line color.
+        */
+       public static final String TEMPLATE_LINE_COLOR_PROPERTY = "template.line.color";
+       
+       /**
+        * Get the current fill color.
+        * 
+        * @return  a color to be used as the fill in template shapes
+        */
+       public static Color getFillColor() {
+               Color fillColor = UIManager.getColor(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY);
+               if (fillColor == null) {
+                       fillColor = Color.lightGray;
+               }
+               return fillColor;
+       }
+       
+       
+       /**
+        * Set the template fill color.
+        */
+       public static void setFillColor(Color c) {
+               UIManager.put(TemplateProperties.TEMPLATE_FILL_COLOR_PROPERTY, c);
+       }
+       
+       
+       /**
+        * Get the current line color.
+        * 
+        * @return  a color to be used as the line in template shapes
+        */
+       public static Color getLineColor() {
+               Color lineColor = UIManager.getColor(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY);
+               if (lineColor == null) {
+                       lineColor = Color.darkGray;
+               }
+               return lineColor;
+       }
+       
+       /**
+        * Set the template line color.
+        */
+       public static void setLineColor(Color c) {
+               UIManager.put(TemplateProperties.TEMPLATE_LINE_COLOR_PROPERTY, c);
+       }
+       
+       /**
+        * Set the template colors from the print settings.
+        */
+       public static void setColors(PrintSettings settings) {
+               setFillColor(settings.getTemplateFillColor());
+               setLineColor(settings.getTemplateBorderColor());
+       }
 }
diff --git a/src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/BaseVisitorStrategy.java
deleted file mode 100644 (file)
index 8998a83..0000000
+++ /dev/null
@@ -1,346 +0,0 @@
-/*
- * BaseVisitorStrategy.java
- */
-package net.sf.openrocket.gui.print.visitor;
-
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
-
-import net.sf.openrocket.rocketcomponent.BodyComponent;
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.ComponentVisitor;
-import net.sf.openrocket.rocketcomponent.ComponentVisitorStrategy;
-import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
-import net.sf.openrocket.rocketcomponent.ExternalComponent;
-import net.sf.openrocket.rocketcomponent.FreeformFinSet;
-import net.sf.openrocket.rocketcomponent.InnerTube;
-import net.sf.openrocket.rocketcomponent.LaunchLug;
-import net.sf.openrocket.rocketcomponent.MassObject;
-import net.sf.openrocket.rocketcomponent.NoseCone;
-import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
-import net.sf.openrocket.rocketcomponent.RingComponent;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.rocketcomponent.Stage;
-import net.sf.openrocket.rocketcomponent.Transition;
-import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
-
-import com.itextpdf.text.Document;
-import com.itextpdf.text.pdf.PdfWriter;
-
-/**
- * This abstract class contains boilerplate functionality to support visiting the components of a rocket. It is a
- * visitor strategy, not a visitor per se.
- */
-public abstract class BaseVisitorStrategy implements ComponentVisitorStrategy {
-       
-       /**
-        * The owning visitor.
-        */
-       protected ComponentVisitor parent;
-       
-       /**
-        * The iText document.
-        */
-       protected Document document;
-       
-       /**
-        * The direct iText writer.
-        */
-       protected PdfWriter writer;
-       
-       /**
-        * The stages selected.
-        */
-       protected Set<Integer> stages;
-       
-       /**
-        * State variable to track the level of hierarchy.
-        */
-       protected int level = 0;
-       
-       /**
-        * Default no-arg constructor.
-        */
-       public BaseVisitorStrategy() {
-       }
-       
-       /**
-        * Constructor.
-        *
-        * @param doc the iText document
-        */
-       public BaseVisitorStrategy(Document doc) {
-               this(doc, null);
-       }
-       
-       /**
-        * Constructor.
-        *
-        * @param doc       the iText document
-        * @param theWriter an iText byte writer
-        */
-       public BaseVisitorStrategy(Document doc, PdfWriter theWriter) {
-               this(doc, theWriter, new HashSet<Integer>());
-       }
-       
-       /**
-        * Constructor.
-        *
-        * @param doc       the iText document
-        * @param theWriter an iText byte writer
-        * @param theStages a set of stage numbers
-        */
-       public BaseVisitorStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStages) {
-               document = doc;
-               writer = theWriter;
-               stages = theStages;
-       }
-       
-       /**
-        * Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage.
-        *
-        * @param stageNumber a stage number
-        *
-        * @return true if the visitor strategy contains the stage number provided
-        */
-       public boolean shouldVisitStage(int stageNumber) {
-               if (stages == null || stages.isEmpty()) {
-                       return false;
-               }
-               
-               for (final Integer stage : stages) {
-                       if (stage == stageNumber) {
-                               return true;
-                       }
-               }
-               
-               return false;
-       }
-       
-       /**
-        * Recurse through the given rocket component.
-        *
-        * @param root the root component; all children will be visited recursively
-        */
-       protected void goDeep(final RocketComponent root) {
-               List<RocketComponent> rc = root.getChildren();
-               goDeep(rc);
-       }
-       
-       
-       /**
-        * Recurse through the given rocket component.
-        *
-        * @param theRc an array of rocket components; all children will be visited recursively
-        */
-       protected void goDeep(final List<RocketComponent> theRc) {
-               level++;
-               for (RocketComponent rocketComponent : theRc) {
-                       rocketComponent.accept(parent);
-               }
-               level--;
-       }
-       
-       /**
-        * Get the dimensions of the paper page.
-        *
-        * @return an internal Dimension
-        */
-       protected Dimension getPageSize() {
-               return new Dimension(document.getPageSize().getWidth(),
-                                                               document.getPageSize().getHeight());
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Rocket visitable) {
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RocketComponent visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Stage visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final ExternalComponent visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyComponent visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RingComponent visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final InnerTube visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final LaunchLug visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Transition visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RadiusRingComponent visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final MassObject visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final NoseCone visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyTube visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final TrapezoidFinSet visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final EllipticalFinSet visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final FreeformFinSet visitable) {
-               if (shouldVisitStage(visitable.getStageNumber())) {
-                       goDeep(visitable);
-               }
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setParent(final ComponentVisitor theParent) {
-               parent = theParent;
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void close() {
-       }
-}
-
-class Dimension {
-       public float width;
-       public float height;
-       
-       public Dimension(float w, float h) {
-               width = w;
-               height = h;
-       }
-       
-       public float getWidth() {
-               return width;
-       }
-       
-       public float getHeight() {
-               return height;
-       }
-}
\ No newline at end of file
index 112ddb86c9643e26ca261864a8652a89cdb249d6..a079bb80ae65e90e80d31a48f6e1a0994c073fa8 100644 (file)
@@ -10,19 +10,40 @@ import com.itextpdf.text.pdf.PdfContentByte;
 import com.itextpdf.text.pdf.PdfWriter;
 import net.sf.openrocket.gui.print.ITextHelper;
 import net.sf.openrocket.gui.print.PrintableFinSet;
-import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
+import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.rocketcomponent.FinSet;
-import net.sf.openrocket.rocketcomponent.FreeformFinSet;
-import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.rocketcomponent.RocketComponent;
+import net.sf.openrocket.startup.Application;
 
-import java.awt.Graphics2D;
+import java.awt.*;
 import java.awt.image.BufferedImage;
+import java.util.List;
 import java.util.Set;
 
 /**
- * A visitor strategy for drawing fin templates.
+ * A strategy for drawing fin templates.
  */
-public class FinSetVisitorStrategy extends BaseVisitorStrategy {
+public class FinSetVisitorStrategy {
+
+    /**
+     * The logger.
+     */
+    private static final LogHelper log = Application.getLogger();
+
+    /**
+     * The iText document.
+     */
+    protected Document document;
+
+    /**
+     * The direct iText writer.
+     */
+    protected PdfWriter writer;
+
+    /**
+     * The stages selected.
+     */
+    protected Set<Integer> stages;
 
     /**
      * Constructor.
@@ -32,31 +53,36 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy {
      * @param theStagesToVisit The stages to be visited by this strategy
      */
     public FinSetVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
-        super(doc, theWriter, theStagesToVisit);
+        document = doc;
+        writer = theWriter;
+        stages = theStagesToVisit;
     }
 
     /**
-     * {@inheritDoc}
+     * Recurse through the given rocket component.
+     *
+     * @param root the root component; all children will be visited recursively
      */
-    @Override
-    public void visit (final TrapezoidFinSet visitable) {
-        doVisit(visitable);
+    public void writeToDocument (final RocketComponent root) {
+        List<RocketComponent> rc = root.getChildren();
+        goDeep(rc);
     }
 
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void visit (final EllipticalFinSet visitable) {
-        doVisit(visitable);
-    }
 
     /**
-     * {@inheritDoc}
+     * Recurse through the given rocket component.
+     *
+     * @param theRc an array of rocket components; all children will be visited recursively
      */
-    @Override
-    public void visit (final FreeformFinSet visitable) {
-        doVisit(visitable);
+    protected void goDeep (final List<RocketComponent> theRc) {
+        for (RocketComponent rocketComponent : theRc) {
+            if (rocketComponent instanceof FinSet) {
+                doVisit((FinSet)rocketComponent);
+            }
+            else if (rocketComponent.getChildCount() > 0) {
+                goDeep(rocketComponent.getChildren());
+            }
+        }
     }
 
     /**
@@ -81,11 +107,32 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy {
                 }
             }
             catch (DocumentException e) {
-                e.printStackTrace();
+                log.error("Could not render fin.", e);
             }
         }
     }
 
+    /**
+     * Determine if the visitor strategy's set of stage numbers (to print) contains the specified stage.
+     *
+     * @param stageNumber a stage number
+     *
+     * @return true if the visitor strategy contains the stage number provided
+     */
+    public boolean shouldVisitStage (int stageNumber) {
+        if (stages == null || stages.isEmpty()) {
+            return false;
+        }
+
+        for (final Integer stage : stages) {
+            if (stage == stageNumber) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Determine if the image will fit on the given page.
      *
@@ -118,5 +165,52 @@ public class FinSetVisitorStrategy extends BaseVisitorStrategy {
         g2.dispose();
         document.newPage();
     }
-}
 
+    /**
+     * Get the dimensions of the paper page.
+     *
+     * @return an internal Dimension
+     */
+    protected Dimension getPageSize () {
+        return new Dimension(document.getPageSize().getWidth(),
+                             document.getPageSize().getHeight());
+    }
+
+    /**
+     * Convenience class to model a dimension.
+     */
+    class Dimension {
+        /** Width, in points. */
+        public float width;
+        /** Height, in points. */
+        public float height;
+
+        /**
+         * Constructor.
+         * @param w width
+         * @param h height
+         */
+        public Dimension (float w, float h) {
+            width = w;
+            height = h;
+        }
+
+        /**
+         * Get the width.
+         *
+         * @return  the width
+         */
+        public float getWidth () {
+            return width;
+        }
+
+        /**
+         * Get the height.
+         *
+         * @return the height
+         */
+        public float getHeight () {
+            return height;
+        }
+    }
+}
diff --git a/src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/MotorMountVisitorStrategy.java
deleted file mode 100644 (file)
index 579cb2d..0000000
+++ /dev/null
@@ -1,100 +0,0 @@
-/*
- * MotorMountVisitor.java
- */
-package net.sf.openrocket.gui.print.visitor;
-
-import com.itextpdf.text.Document;
-import net.sf.openrocket.motor.Motor;
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.InnerTube;
-import net.sf.openrocket.rocketcomponent.MotorMount;
-
-import java.util.ArrayList;
-import java.util.List;
-
-/**
- * A visitor strategy for finding data about motor configurations.  This visitor accumulates information about the
- * motors currently 'installed' into each motor mount in the rocket.  When the visitor is complete, invoke {@link
- * #getMotors()} to obtain the list of Motor instances that correspond to the motor configuration.
- */
-public class MotorMountVisitorStrategy extends BaseVisitorStrategy {
-
-    /**
-     * The motor configuration identifier.
-     */
-    private String mid;
-
-    /** The accumulating list of motors. */
-    private List<Motor> motors = new ArrayList<Motor>();
-
-    /**
-     * Constructor.
-     *
-     * @param doc           the iText document
-     * @param motorConfigID the motor configuration ID
-     */
-    public MotorMountVisitorStrategy (Document doc,
-                                      String motorConfigID) {
-        super(doc);
-        mid = motorConfigID;
-    }
-
-    /**
-     * Override the method that determines if the visiting should be going deep.
-     *
-     * @param stageNumber a stage number
-     *
-     * @return true, always
-     */
-    public boolean shouldVisitStage (int stageNumber) {
-        return true;
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void visit (final BodyTube visitable) {
-        if (visitable.isMotorMount()) {
-            doVisit(visitable);
-        }
-        else {
-            goDeep(visitable);
-        }
-    }
-
-    /**
-     * {@inheritDoc}
-     */
-    @Override
-    public void visit (final InnerTube visitable) {
-        if (visitable.isMotorMount()) {
-            doVisit(visitable);
-        }
-    }
-
-    /**
-     * The core behavior of this visitor.
-     *
-     * @param visitable the object to extract info about; a graphical image of the fin shape is drawn to the document
-     */
-    private void doVisit (final MotorMount visitable) {
-        final Motor motor = visitable.getMotor(mid);
-        if (motor != null) {
-            motors.add(motor);
-        }
-    }
-
-    /**
-     * Answer with the list of motors that have been accumulated from visiting all of the motor mount components in the
-     * rocket component hierarchy.
-     *
-     * @return a list of motors
-     */
-    public List<Motor> getMotors () {
-        return motors;
-    }
-
-}
-
-
index 6151149cfd187bfa96eee46ffa2d95f9aa754965..cdeb9247c17742590137596438842d2892884c3f 100644 (file)
  */
 package net.sf.openrocket.gui.print.visitor;
 
-import java.io.IOException;
-import java.text.NumberFormat;
-import java.util.Collection;
-import java.util.List;
-import java.util.Set;
-
-import javax.swing.ImageIcon;
-
+import com.itextpdf.text.*;
+import com.itextpdf.text.pdf.PdfPCell;
+import com.itextpdf.text.pdf.PdfPTable;
+import com.itextpdf.text.pdf.PdfWriter;
 import net.sf.openrocket.gui.main.ComponentIcons;
 import net.sf.openrocket.gui.print.ITextHelper;
 import net.sf.openrocket.gui.print.PrintUtilities;
 import net.sf.openrocket.gui.print.PrintableFinSet;
+import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.material.Material;
-import net.sf.openrocket.rocketcomponent.BodyComponent;
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.Coaxial;
-import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
-import net.sf.openrocket.rocketcomponent.ExternalComponent;
-import net.sf.openrocket.rocketcomponent.FinSet;
-import net.sf.openrocket.rocketcomponent.FreeformFinSet;
-import net.sf.openrocket.rocketcomponent.InnerTube;
-import net.sf.openrocket.rocketcomponent.LaunchLug;
-import net.sf.openrocket.rocketcomponent.MassObject;
-import net.sf.openrocket.rocketcomponent.NoseCone;
-import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
-import net.sf.openrocket.rocketcomponent.RingComponent;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.rocketcomponent.Stage;
-import net.sf.openrocket.rocketcomponent.Transition;
-import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
+import net.sf.openrocket.rocketcomponent.*;
+import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.unit.Unit;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Coordinate;
 
-import com.itextpdf.text.BadElementException;
-import com.itextpdf.text.Chunk;
-import com.itextpdf.text.Document;
-import com.itextpdf.text.DocumentException;
-import com.itextpdf.text.Element;
-import com.itextpdf.text.Font;
-import com.itextpdf.text.Image;
-import com.itextpdf.text.Phrase;
-import com.itextpdf.text.Rectangle;
-import com.itextpdf.text.pdf.PdfPCell;
-import com.itextpdf.text.pdf.PdfPTable;
-import com.itextpdf.text.pdf.PdfWriter;
+import javax.swing.*;
+import java.text.NumberFormat;
+import java.util.Collection;
+import java.util.List;
+import java.util.Set;
 
 /**
  * A visitor strategy for creating documentation about parts details.
  */
-public class PartsDetailVisitorStrategy extends BaseVisitorStrategy {
-       
-       /**
-        * The number of columns in the table.
-        */
-       private static final int TABLE_COLUMNS = 7;
-       
-       /**
-        * The parts detail is represented as an iText table.
-        */
-       PdfPTable grid;
-       
-       /**
-        * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts.
-        *
-        * @param doc              The iText document
-        * @param theWriter        The direct iText writer
-        * @param theStagesToVisit The stages to be visited by this strategy
-        */
-       public PartsDetailVisitorStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
-               super(doc, theWriter, theStagesToVisit);
-               PrintUtilities.addText(doc, PrintUtilities.BIG_BOLD, "Parts Detail");
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Stage visitable) {
-               try {
-                       if (grid != null) {
-                               document.add(grid);
-                       }
-                       document.add(ITextHelper.createPhrase(visitable.getName()));
-                       grid = new PdfPTable(TABLE_COLUMNS);
-                       grid.setWidthPercentage(100);
-                       grid.setHorizontalAlignment(Element.ALIGN_LEFT);
-               } catch (DocumentException e) {
-               }
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final ExternalComponent visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(ITextHelper.createCell());
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyComponent visitable) {
-               grid.addCell(visitable.getName());
-               grid.completeRow();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RingComponent visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(createOuterDiaCell(visitable));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final InnerTube visitable) {
-               grid.addCell(iconToImage(visitable));
-               final PdfPCell pCell = createNameCell(visitable.getName(), true);
-               grid.addCell(pCell);
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(createOuterDiaCell(visitable));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final LaunchLug visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(createOuterDiaCell(visitable));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Transition visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               
-               Chunk fore = new Chunk("Fore Dia: " + appendLength(visitable.getForeRadius() * 2));
-               fore.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               Chunk aft = new Chunk("Aft Dia: " + appendLength(visitable.getAftRadius() * 2));
-               aft.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               final PdfPCell cell = ITextHelper.createCell();
-               cell.addElement(fore);
-               cell.addElement(aft);
-               grid.addCell(cell);
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RadiusRingComponent visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(createOuterDiaCell(visitable));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final MassObject visitable) {
-               PdfPCell cell = ITextHelper.createCell();
-               cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
-               cell.setPaddingBottom(12f);
-               
-               grid.addCell(iconToImage(visitable));
-               final PdfPCell nameCell = createNameCell(visitable.getName(), true);
-               nameCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
-               nameCell.setPaddingBottom(12f);
-               grid.addCell(nameCell);
-               grid.addCell(cell);
-               grid.addCell(cell);
-               grid.addCell(cell);
-               grid.addCell(createMassCell(visitable.getMass()));
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final NoseCone visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(ITextHelper.createCell(visitable.getType().getName(), PdfPCell.BOTTOM));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyTube visitable) {
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName(), true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(createOuterDiaCell(visitable));
-               grid.addCell(createLengthCell(visitable.getLength()));
-               grid.addCell(createMassCell(visitable.getMass()));
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final TrapezoidFinSet visitable) {
-               visitFins(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final EllipticalFinSet visitable) {
-               visitFins(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final FreeformFinSet visitable) {
-               visitFins(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void close() {
-               try {
-                       if (grid != null) {
-                               document.add(grid);
-                       }
-               } catch (DocumentException e) {
-                       e.printStackTrace();
-               }
-       }
-       
-       private PdfPCell createOuterDiaCell(final Coaxial visitable) {
-               PdfPCell result = new PdfPCell();
-               Phrase p = new Phrase();
-               p.setLeading(12f);
-               result.setVerticalAlignment(Element.ALIGN_TOP);
-               result.setBorder(Rectangle.BOTTOM);
-               Chunk c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               c.append("Dia");
-               p.add(c);
-               
-               c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
-               c.append("out");
-               p.add(c);
-               
-               c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               c.append(" " + appendLength(visitable.getOuterRadius() * 2));
-               p.add(c);
-               createInnerDiaCell(visitable, result);
-               result.addElement(p);
-               return result;
-       }
-       
-       private void createInnerDiaCell(final Coaxial visitable, PdfPCell cell) {
-               Phrase p = new Phrase();
-               p.setLeading(14f);
-               Chunk c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               c.append("Dia");
-               p.add(c);
-               
-               c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
-               c.append("in ");
-               p.add(c);
-               
-               c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               c.append("  " + appendLength(visitable.getInnerRadius() * 2));
-               p.add(c);
-               cell.addElement(p);
-       }
-       
-       private void visitFins(FinSet visitable) {
-               
-               Image img = null;
-               java.awt.Image awtImage = new PrintableFinSet(visitable).createImage();
-               
-               Collection<Coordinate> x = visitable.getComponentBounds();
-               
-               try {
-                       img = Image.getInstance(writer, awtImage, 0.25f);
-               } catch (BadElementException e) {
-                       e.printStackTrace();
-               } catch (IOException e) {
-                       e.printStackTrace();
-               }
-               
-               grid.addCell(iconToImage(visitable));
-               grid.addCell(createNameCell(visitable.getName() + " (" + visitable.getFinCount() + ")", true));
-               grid.addCell(createMaterialCell(visitable.getMaterial()));
-               grid.addCell(ITextHelper.createCell("Thick: " + appendLength(visitable.getThickness()), PdfPCell.BOTTOM));
-               final PdfPCell pCell = new PdfPCell();
-               pCell.setBorder(Rectangle.BOTTOM);
-               pCell.addElement(img);
-               
-               grid.addCell(ITextHelper.createCell());
-               grid.addCell(createMassCell(visitable.getMass()));
-               
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       protected PdfPCell createLengthCell(double length) {
-               return ITextHelper.createCell("Len: " + appendLength(length), PdfPCell.BOTTOM);
-       }
-       
-       protected PdfPCell createMassCell(double mass) {
-               return ITextHelper.createCell("Mass: " + appendMass(mass), PdfPCell.BOTTOM);
-       }
-       
-       protected PdfPCell createNameCell(String v, boolean withIndent) {
-               PdfPCell result = new PdfPCell();
-               result.setBorder(Rectangle.BOTTOM);
-               Chunk c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               if (withIndent) {
-                       for (int x = 0; x < (level - 2) * 10; x++) {
-                               c.append(" ");
-                       }
-               }
-               c.append(v);
-               result.setColspan(2);
-               result.addElement(c);
-               return result;
-       }
-       
-       protected PdfPCell createMaterialCell(Material material) {
-               PdfPCell cell = ITextHelper.createCell();
-               cell.setLeading(13f, 0);
-               
-               Chunk c = new Chunk();
-               c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
-               c.append(appendMaterial(material));
-               cell.addElement(c);
-               Chunk density = new Chunk();
-               density.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
-               density.append(appendMaterialDensity(material));
-               cell.addElement(density);
-               return cell;
-       }
-       
-       protected PdfPCell iconToImage(final RocketComponent visitable) {
-               final ImageIcon icon = (ImageIcon) ComponentIcons.getLargeIcon(visitable.getClass());
-               try {
-                       Image im = Image.getInstance(icon.getImage(), null);
-                       im.scaleToFit(icon.getIconWidth() * 0.6f, icon.getIconHeight() * 0.6f);
-                       PdfPCell cell = new PdfPCell(im);
-                       cell.setFixedHeight(icon.getIconHeight() * 0.6f);
-                       cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
-                       cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
-                       cell.setBorder(PdfPCell.NO_BORDER);
-                       return cell;
-               } catch (BadElementException e) {
-               } catch (IOException e) {
-               }
-               return null;
-       }
-       
-       protected String appendLength(double length) {
-               final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
-               return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString();
-       }
-       
-       protected String appendMass(double mass) {
-               final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit();
-               return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString();
-       }
-       
-       protected String appendMaterial(Material material) {
-               return material.getName();
-       }
-       
-       protected String appendMaterialDensity(Material material) {
-               return " (" + material.getType().getUnitGroup().getDefaultUnit().toStringUnit(material.getDensity()) + ")";
-       }
-       
+public class PartsDetailVisitorStrategy {
+
+    /**
+     * The logger.
+     */
+    private static final LogHelper log = Application.getLogger();
+
+    /**
+     * The number of columns in the table.
+     */
+    private static final int TABLE_COLUMNS = 7;
+
+    /**
+     * The parts detail is represented as an iText table.
+     */
+    PdfPTable grid;
+
+    /**
+     * The iText document.
+     */
+    protected Document document;
+
+    /**
+     * The direct iText writer.
+     */
+    protected PdfWriter writer;
+
+    /**
+     * The stages selected.
+     */
+    protected Set<Integer> stages;
+
+    /**
+     * State variable to track the level of hierarchy.
+     */
+    protected int level = 0;
+
+    private static final String LINES = "Lines: ";
+    private static final String MASS = "Mass: ";
+    private static final String LEN = "Len: ";
+    private static final String THICK = "Thick: ";
+    private static final String INNER = "in ";
+    private static final String DIAMETER = "Dia";
+    private static final String OUTER = "out";
+    private static final String WIDTH = "Width";
+    private static final String LENGTH = "Length";
+    private static final String SHROUD_LINES = "Shroud Lines";
+    private static final String AFT_DIAMETER = "Aft Dia: ";
+    private static final String FORE_DIAMETER = "Fore Dia: ";
+    private static final String PARTS_DETAIL = "Parts Detail";
+
+    /**
+     * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     * @param theStagesToVisit The stages to be visited by this strategy
+     */
+    public PartsDetailVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
+        document = doc;
+        writer = theWriter;
+        stages = theStagesToVisit;
+        PrintUtilities.addText(doc, PrintUtilities.BIG_BOLD, PARTS_DETAIL);
+    }
+
+    /**
+     * Print the parts detail.
+     *
+     * @param root  the root component
+     */
+    public void writeToDocument (final RocketComponent root) {
+        goDeep(root.getChildren());
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param theRc an array of rocket components; all children will be visited recursively
+     */
+    protected void goDeep (final List<RocketComponent> theRc) {
+        level++;
+        for (RocketComponent rocketComponent : theRc) {
+            handle(rocketComponent);
+        }
+        level--;
+    }
+
+    /**
+     * Add a line to the detail report based upon the type of the component.
+     *
+     * @param component  the component to print the detail for
+     */
+    private void handle (RocketComponent component) {
+        //This ugly if-then-else construct is not object oriented.  Originally it was an elegant, and very OO savy, design
+        //using the Visitor pattern.  Unfortunately, it was misunderstood and was removed.
+        if (component instanceof Stage) {
+            try {
+                if (grid != null) {
+                    document.add(grid);
+                }
+                document.add(ITextHelper.createPhrase(component.getName()));
+                grid = new PdfPTable(TABLE_COLUMNS);
+                grid.setWidthPercentage(100);
+                grid.setHorizontalAlignment(Element.ALIGN_LEFT);
+            }
+            catch (DocumentException e) {
+            }
+
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof LaunchLug) {
+            LaunchLug ll = (LaunchLug) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+
+            grid.addCell(createMaterialCell(ll.getMaterial()));
+            grid.addCell(createOuterInnerDiaCell(ll));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+        }
+        else if (component instanceof NoseCone) {
+            NoseCone nc = (NoseCone) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(nc.getMaterial()));
+            grid.addCell(ITextHelper.createCell(nc.getType().getName(), PdfPCell.BOTTOM));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof Transition) {
+            Transition tran = (Transition) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(tran.getMaterial()));
+
+            Chunk fore = new Chunk(FORE_DIAMETER + toLength(tran.getForeRadius() * 2));
+            fore.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+            Chunk aft = new Chunk(AFT_DIAMETER + toLength(tran.getAftRadius() * 2));
+            aft.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+            final PdfPCell cell = ITextHelper.createCell();
+            cell.addElement(fore);
+            cell.addElement(aft);
+            grid.addCell(cell);
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof BodyTube) {
+            BodyTube bt = (BodyTube) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(bt.getMaterial()));
+            grid.addCell(createOuterInnerDiaCell(bt));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof FinSet) {
+            handleFins((FinSet) component);
+        }
+        else if (component instanceof BodyComponent) {
+            grid.addCell(component.getName());
+            grid.completeRow();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof ExternalComponent) {
+            ExternalComponent ext = (ExternalComponent) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+
+            grid.addCell(createMaterialCell(ext.getMaterial()));
+            grid.addCell(ITextHelper.createCell());
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof InnerTube) {
+            InnerTube it = (InnerTube) component;
+            grid.addCell(iconToImage(component));
+            final PdfPCell pCell = createNameCell(component.getName(), true);
+            grid.addCell(pCell);
+            grid.addCell(createMaterialCell(it.getMaterial()));
+            grid.addCell(createOuterInnerDiaCell(it));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof RadiusRingComponent) {
+            RadiusRingComponent rrc = (RadiusRingComponent) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(rrc.getMaterial()));
+            if (component instanceof Bulkhead) {
+                grid.addCell(createDiaCell(rrc.getOuterRadius()*2));
+            }
+            else {
+                grid.addCell(createOuterInnerDiaCell(rrc));
+            }
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof RingComponent) {
+            RingComponent ring = (RingComponent) component;
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(ring.getMaterial()));
+            grid.addCell(createOuterInnerDiaCell(ring));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof ShockCord) {
+            ShockCord ring = (ShockCord) component;
+            PdfPCell cell = ITextHelper.createCell();
+            cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            cell.setPaddingBottom(12f);
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(ring.getMaterial()));
+            grid.addCell(cell);
+            grid.addCell(createLengthCell(ring.getCordLength()));
+            grid.addCell(createMassCell(component.getMass()));
+        }
+        else if (component instanceof Parachute) {
+            Parachute chute = (Parachute) component;
+            PdfPCell cell = ITextHelper.createCell();
+            cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            cell.setPaddingBottom(12f);
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(chute.getMaterial()));
+            grid.addCell(createDiaCell(chute.getDiameter()));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+
+            grid.addCell(iconToImage(null));
+            grid.addCell(createNameCell(SHROUD_LINES, true));
+            grid.addCell(createMaterialCell(chute.getLineMaterial()));
+            grid.addCell(createLinesCell(chute.getLineCount()));
+            grid.addCell(createLengthCell(chute.getLineLength()));
+            grid.addCell(cell);
+        }
+        else if (component instanceof Streamer) {
+            Streamer ring = (Streamer) component;
+            PdfPCell cell = ITextHelper.createCell();
+            cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            cell.setPaddingBottom(12f);
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(ring.getMaterial()));
+            grid.addCell(createStrip(ring));
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+        }
+        else if (component instanceof RecoveryDevice) {
+            RecoveryDevice device = (RecoveryDevice) component;
+            PdfPCell cell = ITextHelper.createCell();
+            cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            cell.setPaddingBottom(12f);
+            grid.addCell(iconToImage(component));
+            grid.addCell(createNameCell(component.getName(), true));
+            grid.addCell(createMaterialCell(device.getMaterial()));
+            grid.addCell(cell);
+            grid.addCell(createLengthCell(component.getLength()));
+            grid.addCell(createMassCell(component.getMass()));
+        }
+        else if (component instanceof MassObject) {
+            PdfPCell cell = ITextHelper.createCell();
+            cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            cell.setPaddingBottom(12f);
+
+            grid.addCell(iconToImage(component));
+            final PdfPCell nameCell = createNameCell(component.getName(), true);
+            nameCell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+            nameCell.setPaddingBottom(12f);
+            grid.addCell(nameCell);
+            grid.addCell(cell);
+            grid.addCell(createDiaCell(((MassObject) component).getRadius() * 2));
+            grid.addCell(cell);
+            grid.addCell(createMassCell(component.getMass()));
+        }
+    }
+
+    /**
+     * Close the strategy by adding the last grid to the document.
+     */
+    public void close () {
+        try {
+            if (grid != null) {
+                document.add(grid);
+            }
+        }
+        catch (DocumentException e) {
+            log.error("Could not write last cell to document.", e);
+        }
+    }
+
+    /**
+     * Create a cell to document an outer 'diameter'.  This is used for components that have no inner diameter, such as
+     * a solid parachute or bulkhead.
+     *
+     * @param diameter  the diameter in default length units
+     *
+     * @return a formatted cell containing the diameter
+     */
+    private PdfPCell createDiaCell (final double diameter) {
+        PdfPCell result = new PdfPCell();
+        Phrase p = new Phrase();
+        p.setLeading(12f);
+        result.setVerticalAlignment(Element.ALIGN_TOP);
+        result.setBorder(Rectangle.BOTTOM);
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(DIAMETER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
+        c.append(OUTER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(" " + toLength(diameter));
+        p.add(c);
+        result.addElement(p);
+        return result;
+    }
+
+    /**
+     * Create a PDF cell for a streamer.
+     *
+     * @param component  a component that is a Coaxial
+     * @return  the PDF cell that has the streamer documented
+     */
+    private PdfPCell createStrip (final Streamer component) {
+        PdfPCell result = new PdfPCell();
+        Phrase p = new Phrase();
+        p.setLeading(12f);
+        result.setVerticalAlignment(Element.ALIGN_TOP);
+        result.setBorder(Rectangle.BOTTOM);
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(LENGTH);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(" " + toLength(component.getStripLength()));
+        p.add(c);
+        result.addElement(p);
+
+        Phrase pw = new Phrase();
+        pw.setLeading(14f);
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(WIDTH);
+        pw.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append("  " + toLength(component.getStripWidth()));
+        pw.add(c);
+        result.addElement(pw);
+
+        return result;
+    }
+
+    /**
+     * Create a PDF cell that documents both an outer and an inner diameter of a component.
+     *
+     * @param component  a component that is a Coaxial
+     * @return  the PDF cell that has the outer and inner diameters documented
+     */
+    private PdfPCell createOuterInnerDiaCell (final Coaxial component) {
+        PdfPCell result = new PdfPCell();
+        Phrase p = new Phrase();
+        p.setLeading(12f);
+        result.setVerticalAlignment(Element.ALIGN_TOP);
+        result.setBorder(Rectangle.BOTTOM);
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(DIAMETER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
+        c.append(OUTER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(" " + toLength(component.getOuterRadius() * 2));
+        p.add(c);
+        createInnerDiaCell(component, result);
+        result.addElement(p);
+        return result;
+    }
+
+    /**
+     * Add inner diameter data to a cell.
+     *
+     * @param component  a component that is a Coaxial
+     * @param cell       the PDF cell to add the inner diameter data to
+     */
+    private void createInnerDiaCell (final Coaxial component, PdfPCell cell) {
+        Phrase p = new Phrase();
+        p.setLeading(14f);
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(DIAMETER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
+        c.append(INNER);
+        p.add(c);
+
+        c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append("  " + toLength(component.getInnerRadius() * 2));
+        p.add(c);
+        cell.addElement(p);
+    }
+
+    /**
+     * Add PDF cells for a fin set.
+     *
+     * @param theFinSet  the fin set
+     */
+    private void handleFins (FinSet theFinSet) {
+
+        Image img = null;
+        java.awt.Image awtImage = new PrintableFinSet(theFinSet).createImage();
+
+        Collection<Coordinate> x = theFinSet.getComponentBounds();
+
+        try {
+            img = Image.getInstance(writer, awtImage, 0.25f);
+        }
+        catch (Exception e) {
+            log.error("Could not write image to document.", e);
+        }
+
+        grid.addCell(iconToImage(theFinSet));
+        grid.addCell(createNameCell(theFinSet.getName() + " (" + theFinSet.getFinCount() + ")", true));
+        grid.addCell(createMaterialCell(theFinSet.getMaterial()));
+        grid.addCell(ITextHelper.createCell(THICK + toLength(theFinSet.getThickness()), PdfPCell.BOTTOM));
+        final PdfPCell pCell = new PdfPCell();
+        pCell.setBorder(Rectangle.BOTTOM);
+        pCell.addElement(img);
+
+        grid.addCell(ITextHelper.createCell());
+        grid.addCell(createMassCell(theFinSet.getMass()));
+
+        List<RocketComponent> rc = theFinSet.getChildren();
+        goDeep(rc);
+    }
+
+    /**
+     * Create a length formatted cell.
+     *
+     * @param length  the length, in default length units
+     *
+     * @return a PdfPCell that is formatted with the length
+     */
+    protected PdfPCell createLengthCell (double length) {
+        return ITextHelper.createCell(LEN + toLength(length), PdfPCell.BOTTOM);
+    }
+
+    /**
+     * Create a mass formatted cell.
+     *
+     * @param mass  the mass, in default mass units
+     *
+     * @return a PdfPCell that is formatted with the mass
+     */
+    protected PdfPCell createMassCell (double mass) {
+        return ITextHelper.createCell(MASS + toMass(mass), PdfPCell.BOTTOM);
+    }
+
+    /**
+     * Create a (shroud) line count formatted cell.
+     *
+     * @param count  the number of shroud lines
+     *
+     * @return a PdfPCell that is formatted with the line count
+     */
+    protected PdfPCell createLinesCell (int count) {
+        return ITextHelper.createCell(LINES + count, PdfPCell.BOTTOM);
+    }
+
+    /**
+     * Create a cell formatted for a name (or any string for that matter).
+     *
+     * @param v  the string to format into a PDF cell
+     * @param withIndent  if true, then an indention is made scaled to the level of the part in the parent hierarchy
+     *
+     * @return a PdfPCell that is formatted with the string <code>v</code>
+     */
+    protected PdfPCell createNameCell (String v, boolean withIndent) {
+        PdfPCell result = new PdfPCell();
+        result.setBorder(Rectangle.BOTTOM);
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        if (withIndent) {
+            for (int x = 0; x < (level - 2) * 10; x++) {
+                c.append(" ");
+            }
+        }
+        c.append(v);
+        result.setColspan(2);
+        result.addElement(c);
+        return result;
+    }
+
+    /**
+     * Create a cell that describes a material.
+     *
+     * @param material  the material
+     *
+     * @return a PdfPCell that is formatted with a description of the material
+     */
+    protected PdfPCell createMaterialCell (Material material) {
+        PdfPCell cell = ITextHelper.createCell();
+        cell.setLeading(13f, 0);
+
+        Chunk c = new Chunk();
+        c.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.NORMAL_FONT_SIZE));
+        c.append(toMaterialName(material));
+        cell.addElement(c);
+        Chunk density = new Chunk();
+        density.setFont(new Font(Font.FontFamily.HELVETICA, PrintUtilities.SMALL_FONT_SIZE));
+        density.append(toMaterialDensity(material));
+        cell.addElement(density);
+        return cell;
+    }
+
+    /**
+     * Get the icon of the particular type of rocket component and conver it to an image in a PDF cell.
+     *
+     * @param visitable  the rocket component to create a cell with it's image
+     *
+     * @return a PdfPCell that is just an image that can be put into a PDF
+     */
+    protected PdfPCell iconToImage (final RocketComponent visitable) {
+        if (visitable != null) {
+            final ImageIcon icon = (ImageIcon) ComponentIcons.getLargeIcon(visitable.getClass());
+            try {
+                if (icon != null) {
+                    Image im = Image.getInstance(icon.getImage(), null);
+                    if (im != null) {
+                        im.scaleToFit(icon.getIconWidth() * 0.6f, icon.getIconHeight() * 0.6f);
+                        PdfPCell cell = new PdfPCell(im);
+                        cell.setFixedHeight(icon.getIconHeight() * 0.6f);
+                        cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
+                        cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+                        cell.setBorder(PdfPCell.NO_BORDER);
+                        return cell;
+                    }
+                }
+            }
+            catch (Exception e) {
+            }
+        }
+        PdfPCell cell = new PdfPCell();
+        cell.setHorizontalAlignment(PdfPCell.ALIGN_CENTER);
+        cell.setVerticalAlignment(PdfPCell.ALIGN_MIDDLE);
+        cell.setBorder(PdfPCell.NO_BORDER);
+        return cell;
+    }
+
+    /**
+     * Format the length as a displayable string.
+     *
+     * @param length the length (assumed to be in default length units)
+     *
+     * @return a string representation of the length with unit abbreviation
+     */
+    protected String toLength (double length) {
+        final Unit defaultUnit = UnitGroup.UNITS_LENGTH.getDefaultUnit();
+        return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(length)) + defaultUnit.toString();
+    }
+
+    /**
+     * Format the mass as a displayable string.
+     *
+     * @param mass  the mass (assumed to be in default mass units)
+     *
+     * @return a string representation of the mass with mass abbreviation
+     */
+    protected String toMass (double mass) {
+        final Unit defaultUnit = UnitGroup.UNITS_MASS.getDefaultUnit();
+        return NumberFormat.getNumberInstance().format(defaultUnit.toUnit(mass)) + defaultUnit.toString();
+    }
+
+    /**
+     * Get a displayable string of the material's name.
+     *
+     * @param material  the material to output
+     *
+     * @return the material name
+     */
+    protected String toMaterialName (Material material) {
+        return material.getName();
+    }
+
+    /**
+     * Format the material density as a displayable string.
+     *
+     * @param material  the material to output
+     *
+     * @return a string representation of the material density
+     */
+    protected String toMaterialDensity (Material material) {
+        return " (" + material.getType().getUnitGroup().getDefaultUnit().toStringUnit(material.getDensity()) + ")";
+    }
+
 }
index 699a4263fd8a66cad3183cde5434925aad63865b..e8ecc0baf7238261fb296f5b5d31871fc00eedfe 100644 (file)
  */
 package net.sf.openrocket.gui.print.visitor;
 
+import com.itextpdf.text.Document;
+import com.itextpdf.text.pdf.PdfWriter;
+import net.sf.openrocket.rocketcomponent.*;
+
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.Coaxial;
-import net.sf.openrocket.rocketcomponent.ComponentVisitor;
-import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
-import net.sf.openrocket.rocketcomponent.FreeformFinSet;
-import net.sf.openrocket.rocketcomponent.InnerTube;
-import net.sf.openrocket.rocketcomponent.LaunchLug;
-import net.sf.openrocket.rocketcomponent.NoseCone;
-import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
-import net.sf.openrocket.rocketcomponent.RingComponent;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.rocketcomponent.Transition;
-import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
-
-import com.itextpdf.text.Document;
-import com.itextpdf.text.pdf.PdfWriter;
-
 /**
  * A visitor strategy for creating documentation about a parts list.
  */
-public class PartsListVisitorStrategy extends BaseVisitorStrategy {
-       
-       /**
-        * Accumulator for parts data.
-        */
-       private Map<PartsAccumulator, PartsAccumulator> crap = new HashMap<PartsAccumulator, PartsAccumulator>();
-       
-       /**
-        * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts.
-        *
-        * @param doc              The iText document
-        * @param theWriter        The direct iText writer
-        * @param theStagesToVisit The stages to be visited by this strategy
-        */
-       public PartsListVisitorStrategy(Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
-               super(doc, theWriter, theStagesToVisit);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RingComponent visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final InnerTube visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final LaunchLug visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Transition visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RadiusRingComponent visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final NoseCone visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyTube visitable) {
-               final PartsAccumulator key = new PartsAccumulator(visitable);
-               PartsAccumulator pa = crap.get(key);
-               if (pa == null) {
-                       pa = key;
-                       crap.put(pa, pa);
-               }
-               pa.increment();
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final TrapezoidFinSet visitable) {
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final EllipticalFinSet visitable) {
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final FreeformFinSet visitable) {
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setParent(final ComponentVisitor theParent) {
-               parent = theParent;
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void close() {
-               for (PartsAccumulator partsAccumulator : crap.keySet()) {
-                       System.err.println(partsAccumulator.component.getComponentName() + " " + partsAccumulator.quantity);
-               }
-       }
-       
+public class PartsListVisitorStrategy {
+
+    /**
+     * Accumulator for parts data.
+     */
+    private Map<PartsAccumulator, PartsAccumulator> crap = new HashMap<PartsAccumulator, PartsAccumulator>();
+
+    /**
+     * The iText document.
+     */
+    protected Document document;
+
+    /**
+     * The direct iText writer.
+     */
+    protected PdfWriter writer;
+
+    /**
+     * The stages selected.
+     */
+    protected Set<Integer> stages;
+
+
+    /**
+     * Construct a strategy for visiting a parts hierarchy for the purposes of collecting details on those parts.
+     *
+     * @param doc              The iText document
+     * @param theWriter        The direct iText writer
+     * @param theStagesToVisit The stages to be visited by this strategy
+     */
+    public PartsListVisitorStrategy (Document doc, PdfWriter theWriter, Set<Integer> theStagesToVisit) {
+        document = doc;
+        writer = theWriter;
+        stages = theStagesToVisit;
+    }
+
+
+    /**
+     * Print the parts detail.
+     *
+     * @param root the root component
+     */
+    public void doVisit (final RocketComponent root) {
+        goDeep(root.getChildren());
+    }
+
+    /**
+     * Recurse through the given rocket component.
+     *
+     * @param theRc an array of rocket components; all children will be visited recursively
+     */
+    protected void goDeep (final List<RocketComponent> theRc) {
+        for (RocketComponent rocketComponent : theRc) {
+            doIt(rocketComponent);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    private void doIt (final RocketComponent component) {
+        if (component instanceof InnerTube) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof LaunchLug) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+        }
+
+        else if (component instanceof NoseCone) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof Transition) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof RadiusRingComponent) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof RingComponent) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof BodyTube) {
+            final PartsAccumulator key = new PartsAccumulator(component);
+            PartsAccumulator pa = crap.get(key);
+            if (pa == null) {
+                pa = key;
+                crap.put(pa, pa);
+            }
+            pa.increment();
+            List<RocketComponent> rc = component.getChildren();
+            goDeep(rc);
+        }
+        else if (component instanceof TrapezoidFinSet) {
+        }
+        else if (component instanceof EllipticalFinSet) {
+        }
+        else if (component instanceof FreeformFinSet) {
+        }
+    }
+
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close () {
+        for (PartsAccumulator partsAccumulator : crap.keySet()) {
+            System.err.println(partsAccumulator.component.getComponentName() + " " + partsAccumulator.quantity);
+        }
+    }
+
 }
 
 class PartsAccumulator {
-       
-       int quantity = 0;
-       
-       RocketComponent component;
-       
-       PartsAccumulator(RocketComponent theComponent) {
-               component = theComponent;
-       }
-       
-       void increment() {
-               quantity++;
-       }
-       
-       int quantity() {
-               return quantity;
-       }
-       
-       @Override
-       public boolean equals(final Object o1) {
-               if (this == o1) {
-                       return true;
-               }
-               
-               RocketComponent that;
-               if (o1 instanceof net.sf.openrocket.gui.print.visitor.PartsAccumulator) {
-                       that = ((net.sf.openrocket.gui.print.visitor.PartsAccumulator) o1).component;
-               } else if (o1 instanceof RocketComponent) {
-                       that = (RocketComponent) o1;
-               } else {
-                       return false;
-               }
-               
-               if (this.component.getClass().equals(that.getClass())) {
-                       //If 
-                       if (that.getLength() == this.component.getLength()) {
-                               if (that.getMass() == this.component.getMass()) {
-                                       return true;
-                               }
-                       }
-                       if (this.component instanceof Coaxial &&
-                                       that instanceof Coaxial) {
-                               Coaxial cThis = (Coaxial) this.component;
-                               Coaxial cThat = (Coaxial) that;
-                               if (cThis.getInnerRadius() == cThat.getInnerRadius() &&
-                                               cThis.getOuterRadius() == cThat.getOuterRadius()) {
-                                       return true;
-                               }
-                       }
-                       return false;
-               }
-               return false;
-       }
-       
-       @Override
-       public int hashCode() {
-               return component.getComponentName().hashCode();
-       }
+
+    int quantity = 0;
+
+    RocketComponent component;
+
+    PartsAccumulator (RocketComponent theComponent) {
+        component = theComponent;
+    }
+
+    void increment () {
+        quantity++;
+    }
+
+    int quantity () {
+        return quantity;
+    }
+
+    @Override
+    public boolean equals (final Object o1) {
+        if (this == o1) {
+            return true;
+        }
+
+        RocketComponent that;
+        if (o1 instanceof net.sf.openrocket.gui.print.visitor.PartsAccumulator) {
+            that = ((net.sf.openrocket.gui.print.visitor.PartsAccumulator) o1).component;
+        }
+        else if (o1 instanceof RocketComponent) {
+            that = (RocketComponent) o1;
+        }
+        else {
+            return false;
+        }
+
+        if (this.component.getClass().equals(that.getClass())) {
+            //If
+            if (that.getLength() == this.component.getLength()) {
+                if (that.getMass() == this.component.getMass()) {
+                    return true;
+                }
+            }
+            if (this.component instanceof Coaxial &&
+                that instanceof Coaxial) {
+                Coaxial cThis = (Coaxial) this.component;
+                Coaxial cThat = (Coaxial) that;
+                if (cThis.getInnerRadius() == cThat.getInnerRadius() &&
+                    cThis.getOuterRadius() == cThat.getOuterRadius()) {
+                    return true;
+                }
+            }
+            return false;
+        }
+        return false;
+    }
+
+    @Override
+    public int hashCode () {
+        return component.getComponentName().hashCode();
+    }
 }
\ No newline at end of file
diff --git a/src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java b/src/net/sf/openrocket/gui/print/visitor/StageVisitorStrategy.java
deleted file mode 100644 (file)
index 00931b1..0000000
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- * StageVisitor.java
- */
-package net.sf.openrocket.gui.print.visitor;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import net.sf.openrocket.rocketcomponent.BodyComponent;
-import net.sf.openrocket.rocketcomponent.BodyTube;
-import net.sf.openrocket.rocketcomponent.ComponentVisitor;
-import net.sf.openrocket.rocketcomponent.ComponentVisitorStrategy;
-import net.sf.openrocket.rocketcomponent.EllipticalFinSet;
-import net.sf.openrocket.rocketcomponent.ExternalComponent;
-import net.sf.openrocket.rocketcomponent.FreeformFinSet;
-import net.sf.openrocket.rocketcomponent.InnerTube;
-import net.sf.openrocket.rocketcomponent.LaunchLug;
-import net.sf.openrocket.rocketcomponent.MassObject;
-import net.sf.openrocket.rocketcomponent.NoseCone;
-import net.sf.openrocket.rocketcomponent.RadiusRingComponent;
-import net.sf.openrocket.rocketcomponent.RingComponent;
-import net.sf.openrocket.rocketcomponent.Rocket;
-import net.sf.openrocket.rocketcomponent.RocketComponent;
-import net.sf.openrocket.rocketcomponent.Stage;
-import net.sf.openrocket.rocketcomponent.Transition;
-import net.sf.openrocket.rocketcomponent.TrapezoidFinSet;
-
-/**
- * This visitor strategy accumulates Stage references in a Rocket hierarchy.
- */
-public class StageVisitorStrategy implements ComponentVisitorStrategy {
-       
-       /**
-        * The collection of stages, accumulated during a visitation.
-        */
-       private List<Double> stageComponents = new ArrayList<Double>();
-       
-       private Double mass = 0d;
-       
-       /**
-        * The owning visitor.
-        */
-       protected ComponentVisitor parent;
-       
-       /**
-        * Constructor.
-        */
-       public StageVisitorStrategy() {
-       }
-       
-       /**
-        * Override the method that determines if the visiting should be going deep.
-        *
-        * @param stageNumber a stage number
-        *
-        * @return true, always
-        */
-       public boolean shouldVisitStage(int stageNumber) {
-               return true;
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void setParent(final ComponentVisitor theParent) {
-               parent = theParent;
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Rocket visitable) {
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Stage visitable) {
-               
-               if (mass > 0d) {
-                       stageComponents.add(mass);
-               }
-               mass = 0d;
-               List<RocketComponent> rc = visitable.getChildren();
-               goDeep(rc);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RocketComponent visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * Recurse through the given rocket component.
-        *
-        * @param root the root component; all children will be visited recursively
-        */
-       protected void goDeep(final RocketComponent root) {
-               List<RocketComponent> rc = root.getChildren();
-               goDeep(rc);
-       }
-       
-       
-       /**
-        * Recurse through the given rocket component.
-        *
-        * @param theRc an array of rocket components; all children will be visited recursively
-        */
-       protected void goDeep(final List<RocketComponent> theRc) {
-               for (RocketComponent rocketComponent : theRc) {
-                       rocketComponent.accept(parent);
-               }
-       }
-       
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final ExternalComponent visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyComponent visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RingComponent visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final InnerTube visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final LaunchLug visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final Transition visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final RadiusRingComponent visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final MassObject visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final NoseCone visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final BodyTube visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final TrapezoidFinSet visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final EllipticalFinSet visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * {@inheritDoc}
-        */
-       @Override
-       public void visit(final FreeformFinSet visitable) {
-               mass += visitable.getMass();
-               goDeep(visitable);
-       }
-       
-       /**
-        * Get the list of stages, sort from Stage 1 .. Stage N.
-        *
-        * @return a sorted list of stages
-        */
-       public List<Double> getStages() {
-               return stageComponents;
-       }
-       
-       /**
-        * Close by setting the last stage.
-        */
-       @Override
-       public void close() {
-               if (mass > 0d) {
-                       stageComponents.add(mass);
-               }
-       }
-       
-}
diff --git a/src/net/sf/openrocket/l10n/ClassBasedTranslator.java b/src/net/sf/openrocket/l10n/ClassBasedTranslator.java
new file mode 100644 (file)
index 0000000..b4212dc
--- /dev/null
@@ -0,0 +1,92 @@
+package net.sf.openrocket.l10n;
+
+import java.util.MissingResourceException;
+
+import net.sf.openrocket.logging.TraceException;
+import net.sf.openrocket.util.BugException;
+
+/**
+ * A translator that prepends a pre-defined class name in front of a translation key
+ * and retrieves the translator for that key, and only if that is missing reverts to
+ * the base key name.  The base class name can either be provided to the constructor
+ * or retrieved from the stack.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ClassBasedTranslator implements Translator {
+       
+
+       private final Translator translator;
+       private final String className;
+       
+       /**
+        * Construct a translator using a specified class name.
+        * 
+        * @param translator    the translator from which to obtain the translations.
+        * @param className             the base class name to prepend.
+        */
+       public ClassBasedTranslator(Translator translator, String className) {
+               this.translator = translator;
+               this.className = className;
+       }
+       
+       /**
+        * Construct a translator by obtaining the base class name from the stack.
+        * 
+        * @param translator    the translator from which to obtain the translations.
+        * @param levels                the number of levels to move upwards in the stack from the point where this method is called.
+        */
+       public ClassBasedTranslator(Translator translator, int levels) {
+               this(translator, getStackClass(levels));
+       }
+       
+       
+
+       @Override
+       public String get(String key) {
+               String classKey = className + "." + key;
+               
+               try {
+                       return translator.get(classKey);
+               } catch (MissingResourceException e) {
+                       // Ignore
+               }
+               
+               try {
+                       return translator.get(key);
+               } catch (MissingResourceException e) {
+                       MissingResourceException mre = new MissingResourceException(
+                                       "Neither key '" + classKey + "' nor '" + key + "' could be found", e.getClassName(), key);
+                       mre.initCause(e);
+                       throw mre;
+               }
+       }
+       
+       
+
+       private static String getStackClass(int levels) {
+               TraceException trace = new TraceException();
+               StackTraceElement stack[] = trace.getStackTrace();
+               final int index = levels + 2;
+               if (stack.length <= index) {
+                       throw new BugException("Stack trace is too short, length=" + stack.length + ", expected=" + index, trace);
+               }
+               
+               StackTraceElement element = stack[index];
+               String cn = element.getClassName();
+               int pos = cn.lastIndexOf('.');
+               if (pos >= 0) {
+                       cn = cn.substring(pos + 1);
+               }
+               return cn;
+       }
+       
+       
+
+
+       // For unit testing purposes
+       String getClassName() {
+               return className;
+       }
+       
+}
index 19e6c84a7655c742ff9dae14016b3fbd5aa19783..5a2bf5920876a3c3b913f98727d3afd111856a13 100644 (file)
@@ -2,14 +2,32 @@ package net.sf.openrocket.l10n;
 
 /**
  * A translator implementation that returns the logical key in brackets instead
- * of an actual translation.
+ * of an actual translation.  The class optionally verifies that the translation
+ * is actually obtainable from some other translator.
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public class DebugTranslator implements Translator {
        
+       private final Translator translator;
+       
+       
+       /**
+        * Sole constructor.
+        * 
+        * @param translator    the translator to verify the translation exists, or <code>null</code> not to verify.
+        */
+       public DebugTranslator(Translator translator) {
+               this.translator = translator;
+       }
+       
+       
+
        @Override
        public String get(String key) {
+               if (translator != null) {
+                       translator.get(key);
+               }
                return "[" + key + "]";
        }
        
diff --git a/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java b/src/net/sf/openrocket/l10n/ExceptionSuppressingTranslator.java
new file mode 100644 (file)
index 0000000..a4cedc0
--- /dev/null
@@ -0,0 +1,58 @@
+package net.sf.openrocket.l10n;
+
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.MissingResourceException;
+import java.util.Set;
+
+import net.sf.openrocket.gui.main.ExceptionHandler;
+
+/**
+ * A translator that suppresses MissingResourceExceptions and handles them gracefully.
+ * For every missing key this class calls the exception handler exactly once, and
+ * returns the key itself as the translation.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public class ExceptionSuppressingTranslator implements Translator {
+       
+       private static final Set<String> errors = new HashSet<String>();
+       // For unit testing:
+       static int failures = 0;
+       
+       private final Translator translator;
+       
+       
+
+       /**
+        * Sole constructor.
+        * 
+        * @param translator    the translator to use
+        */
+       public ExceptionSuppressingTranslator(Translator translator) {
+               this.translator = translator;
+       }
+       
+       
+
+       @Override
+       public String get(String key) {
+               try {
+                       return translator.get(key);
+               } catch (MissingResourceException e) {
+                       handleError(key, e);
+               }
+               
+               return key;
+       }
+       
+       
+
+       private static synchronized void handleError(String key, MissingResourceException e) {
+               if (errors.add(key)) {
+                       failures++;
+                       ExceptionHandler.handleErrorCondition("Can not find translation for '" + key + "' locale=" + Locale.getDefault(), e);
+               }
+       }
+       
+}
index 5bfdbd28d487cecf1abd761c4c04c9ba62543f2f..241ecefcfba93f62525c9f6bd0734cb2b6d3937e 100644 (file)
@@ -1,26 +1,16 @@
 package net.sf.openrocket.l10n;
 
 import java.util.Locale;
-import java.util.MissingResourceException;
 import java.util.ResourceBundle;
 
-import net.sf.openrocket.logging.LogHelper;
-import net.sf.openrocket.startup.Application;
-
 /**
  * A translator that obtains translated strings from a resource bundle.
- * <p>
- * If a message is not found in any resource bundle, an error is logged and the key itself
- * is returned.
  * 
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public class ResourceBundleTranslator implements Translator {
-       private static final LogHelper log = Application.getLogger();
        
        private final ResourceBundle bundle;
-       private final String baseName;
-       private final Locale locale;
        
        /**
         * Create a ResourceBundleTranslator using the default Locale.
@@ -39,8 +29,6 @@ public class ResourceBundleTranslator implements Translator {
         */
        public ResourceBundleTranslator(String baseName, Locale locale) {
                this.bundle = ResourceBundle.getBundle(baseName, locale);
-               this.baseName = baseName;
-               this.locale = locale;
        }
        
        
@@ -49,12 +37,7 @@ public class ResourceBundleTranslator implements Translator {
         */
        @Override
        public synchronized String get(String key) {
-               try {
-                       return bundle.getString(key);
-               } catch (MissingResourceException e) {
-                       log.error("String not found for key '" + key + "' in bundle '" + baseName + "' with locale " + locale, e);
-               }
-               return key;
+               return bundle.getString(key);
        }
        
 }
index c95d7becd932c337588f5420cbc9ac98ac1aeb87..9eed2cdfa51ada85ce290e1941df03676c1ccfb7 100644 (file)
@@ -1,5 +1,7 @@
 package net.sf.openrocket.l10n;
 
+import java.util.MissingResourceException;
+
 /**
  * An interface for obtaining translations from logical keys.
  * <p>
@@ -15,6 +17,7 @@ public interface Translator {
         * 
         * @param key   the logical string key.
         * @return              the translated string.
+        * @throws MissingResourceException if the translation corresponding to the key is not found.
         * @throws NullPointerException if key is null.
         */
        public String get(String key);
index 97a12406f4e764d00cce6abf580b796f14d56b0e..2bc112fbb11624924fcd699307157bde8764c3b1 100644 (file)
@@ -3,17 +3,17 @@ package net.sf.openrocket.rocketcomponent;
 
 /**
  * Class to represent a body object.  The object can be described as a function of
- * the cylindrical coordinates x and angle theta as  r = f(x,theta).  The component 
+ * the cylindrical coordinates x and angle theta as  r = f(x,theta).  The component
  * need not be symmetrical in any way (e.g. square tube, slanted cone etc).
- * 
+ *
  * It defines the methods getRadius(x,theta) and getInnerRadius(x,theta), as well
  * as get/setLength().
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
 public abstract class BodyComponent extends ExternalComponent {
-       
+
        /**
         * Default constructor.  Sets the relative position to POSITION_RELATIVE_AFTER,
         * i.e. body components come after one another.
@@ -21,32 +21,32 @@ public abstract class BodyComponent extends ExternalComponent {
        public BodyComponent() {
                super(RocketComponent.Position.AFTER);
        }
-       
-       
+
+
        /**
         * Get the outer radius of the component at cylindrical coordinate (x,theta).
-        * 
+        *
         * Note that the return value may be negative for a slanted object.
-        * 
+        *
         * @param x  Distance in x direction
         * @param theta  Angle about the x-axis
         * @return  Distance to the outer edge of the object
         */
        public abstract double getRadius(double x, double theta);
-       
-       
+
+
        /**
         * Get the inner radius of the component at cylindrical coordinate (x,theta).
-        * 
+        *
         * Note that the return value may be negative for a slanted object.
-        * 
+        *
         * @param x  Distance in x direction
         * @param theta  Angle about the x-axis
         * @return  Distance to the inner edge of the object
         */
        public abstract double getInnerRadius(double x, double theta);
-       
-       
+
+
 
        /**
         * Sets the length of the body component.
@@ -57,21 +57,10 @@ public abstract class BodyComponent extends ExternalComponent {
                this.length = Math.max(length, 0);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
+
        @Override
        public boolean allowsChildren() {
                return true;
        }
-    
-    /**
-     * Accept a visitor to this BodyComponent in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this BodyComponent
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-    
-    
+
 }
index 297ec1c3befa93382aa017d1667f240c1b3ee342..0b53eb0266c27ecf4ed87625582a7fadaa91ce22 100644 (file)
@@ -1,25 +1,25 @@
 package net.sf.openrocket.rocketcomponent;
 
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.motor.Motor;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-
 
 /**
  * Rocket body tube component.  Has only two parameters, a radius and length.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
 public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial {
        private static final Translator trans = Application.getTranslator();
-
+       
        private double radius = 0;
        private boolean autoRadius = false; // Radius chosen automatically based on parent component
        
@@ -64,11 +64,11 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        
        /**
         * Return the outer radius of the body tube.
-     * 
-     * @return  the outside radius of the tube
+        *
+        * @return  the outside radius of the tube
         */
-    @Override
-       public double getOuterRadius () {
+       @Override
+       public double getOuterRadius() {
                if (autoRadius) {
                        // Return auto radius from front or rear
                        double r = -1;
@@ -94,11 +94,11 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
         * Set the outer radius of the body tube.  If the radius is less than the wall thickness,
         * the wall thickness is decreased accordingly of the value of the radius.
         * This method sets the automatic radius off.
-     * 
-     * @param radius  the outside radius in standard units
+        *
+        * @param radius  the outside radius in standard units
         */
-    @Override
-       public void setOuterRadius (double radius) {
+       @Override
+       public void setOuterRadius(double radius) {
                if ((this.radius == radius) && (autoRadius == false))
                        return;
                
@@ -120,7 +120,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        }
        
        /**
-        * Sets whether the radius is selected automatically or not.  
+        * Sets whether the radius is selected automatically or not.
         */
        public void setRadiusAutomatic(boolean auto) {
                if (autoRadius == auto)
@@ -132,9 +132,15 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        
        
        @Override
-       public double getAftRadius() {  return getOuterRadius(); }
+       public double getAftRadius() {
+               return getOuterRadius();
+       }
+       
        @Override
-       public double getForeRadius() { return getOuterRadius(); }
+       public double getForeRadius() {
+               return getOuterRadius();
+       }
+       
        @Override
        public boolean isAftRadiusAutomatic() {
                return isRadiusAutomatic();
@@ -178,20 +184,17 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        
 
 
-       
-       
-       
-       
-    @Override  
+
+       @Override
        public double getInnerRadius() {
                if (filled)
                        return 0;
-               return Math.max(getOuterRadius()-thickness, 0);
+               return Math.max(getOuterRadius() - thickness, 0);
        }
        
-    @Override
+       @Override
        public void setInnerRadius(double r) {
-               setThickness(getOuterRadius()-r);
+               setThickness(getOuterRadius() - r);
        }
        
        
@@ -205,18 +208,8 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
                //// Body tube
                return trans.get("BodyTube.BodyTube");
        }
-
-    /**
-     * Accept a visitor to this BodyTube in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this BodyTube
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-
-
+       
+       
        /************ Component calculations ***********/
        
        // From SymmetricComponent
@@ -236,7 +229,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
                if (filled)
                        return 0.0;
                else
-                       return Math.max(getOuterRadius()-thickness,0);
+                       return Math.max(getOuterRadius() - thickness, 0);
        }
        
        
@@ -264,14 +257,13 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        @Override
        public double getLongitudinalUnitInertia() {
                // 1/12 * (3 * (r1^2 + r2^2) + h^2)
-               return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
-                               MathUtil.pow2(getLength())) / 12;
+               return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) + MathUtil.pow2(getLength())) / 12;
        }
        
        @Override
        public double getRotationalUnitInertia() {
                // 1/2 * (r1^2 + r2^2)
-               return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
+               return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius())) / 2;
        }
        
        
@@ -288,7 +280,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        /**
         * Adds bounding coordinates to the given set.  The body tube will fit within the
         * convex hull of the points.
-        * 
+        *
         * Currently the points are simply a rectangular box around the body tube.
         */
        @Override
@@ -305,7 +297,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        /**
         * Check whether the given type can be added to this component.  BodyTubes allow any
         * InternalComponents or ExternalComponents, excluding BodyComponents, to be added.
-        * 
+        *
         * @param type  The RocketComponent class type to add.
         * @return      Whether such a component can be added.
         */
@@ -446,7 +438,7 @@ public class BodyTube extends SymmetricComponent implements MotorMount, Coaxial
        /*
         * (non-Javadoc)
         * Copy the motor and ejection delay HashMaps.
-        * 
+        *
         * @see rocketcomponent.RocketComponent#copy()
         */
        @SuppressWarnings("unchecked")
index 3abcd44a213c0c28a648202c3f3875010b88cf99..21b73e2af1daa65d70611b0f1400e7bf938153d5 100644 (file)
@@ -4,14 +4,14 @@ import net.sf.openrocket.util.Coordinate;
 
 
 public class CenteringRing extends RadiusRingComponent {
-       
+
        public CenteringRing() {
                setOuterRadiusAutomatic(true);
                setInnerRadiusAutomatic(true);
                setLength(0.002);
        }
-       
-       
+
+
        @Override
        public double getInnerRadius() {
                // Implement sibling inner radius automation
@@ -26,55 +26,45 @@ public class CenteringRing extends RadiusRingComponent {
                                         */
                                        if (!(sibling instanceof InnerTube)) // Excludes itself
                                                continue;
-                                       
+
                                        double pos1 = this.toRelative(Coordinate.NUL, sibling)[0].x;
                                        double pos2 = this.toRelative(new Coordinate(getLength()), sibling)[0].x;
                                        if (pos2 < 0 || pos1 > sibling.getLength())
                                                continue;
-                                       
+
                                        innerRadius = Math.max(innerRadius, ((InnerTube) sibling).getOuterRadius());
                                }
                                innerRadius = Math.min(innerRadius, getOuterRadius());
                        }
                }
-               
+
                return super.getInnerRadius();
        }
-       
-       
+
+
        @Override
        public void setOuterRadiusAutomatic(boolean auto) {
                super.setOuterRadiusAutomatic(auto);
        }
-       
+
        @Override
        public void setInnerRadiusAutomatic(boolean auto) {
                super.setInnerRadiusAutomatic(auto);
        }
-       
+
        @Override
        public String getComponentName() {
                return "Centering ring";
        }
-       
+
        @Override
        public boolean allowsChildren() {
                return false;
        }
-       
+
        @Override
        public boolean isCompatible(Class<? extends RocketComponent> type) {
                return false;
        }
-    
-    /**
-     * Accept a visitor to this CenteringRing in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this CenteringRing
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
 
 }
diff --git a/src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java b/src/net/sf/openrocket/rocketcomponent/ComponentVisitor.java
deleted file mode 100644 (file)
index 5a69f75..0000000
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * ComponentVisitor.java
- */
-package net.sf.openrocket.rocketcomponent;
-
-/**
- * This class implements a Visitor pattern to visit any/all components of a Rocket.
- */
-public class ComponentVisitor implements Visitor<ComponentVisitor, RocketComponent> {
-
-    /**
-     * The delegate.
-     */
-    private ComponentVisitorStrategy strategy;
-
-    /**
-     * Constructor.
-     *
-     * @param aStrategy the object to delegate the visiting to
-     */
-    public ComponentVisitor (ComponentVisitorStrategy aStrategy) {
-        strategy = aStrategy;
-        strategy.setParent(this);
-    }
-
-    /**
-     * Visit a Rocket object.
-     *
-     * @param visitable the Rocket to visit
-     */
-    public void visit (final Rocket visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a RocketComponent object.  This is used to catch any RocketComponent subclass not explicity defined by a
-     * visit method in this strategy.
-     *
-     * @param visitable the RocketComponent to visit
-     */
-    public void visit (final RocketComponent visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a Stage object.
-     *
-     * @param visitable the Stage to visit
-     */
-    public void visit (final Stage visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit an ExternalComponent object.
-     *
-     * @param visitable the ExternalComponent to visit
-     */
-    public void visit (final ExternalComponent visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a BodyComponent object.
-     *
-     * @param visitable the BodyComponent to visit
-     */
-    public void visit (final BodyComponent visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a RingComponent object.
-     *
-     * @param visitable the RingComponent to visit
-     */
-    public void visit (final RingComponent visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit an InnerTube object.
-     *
-     * @param visitable the InnerTube to visit
-     */
-    public void visit (final InnerTube visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a LaunchLug object.
-     *
-     * @param visitable the LaunchLug to visit
-     */
-    public void visit (final LaunchLug visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a Transition object.
-     *
-     * @param visitable the Transition to visit
-     */
-    public void visit (final Transition visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a RadiusRingComponent object.
-     *
-     * @param visitable the RadiusRingComponent to visit
-     */
-    public void visit (final RadiusRingComponent visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a MassObject object.
-     *
-     * @param visitable the MassObject to visit
-     */
-    public void visit (final MassObject visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a NoseCone object.
-     *
-     * @param visitable the NoseCone to visit
-     */
-    public void visit (final NoseCone visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a BodyTube object.
-     *
-     * @param visitable the BodyTube to visit
-     */
-    public void visit (final BodyTube visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a Rocket object.
-     *
-     * @param visitable the Rocket to visit
-     */
-    public void visit (final TrapezoidFinSet visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a Rocket object.
-     *
-     * @param visitable the Rocket to visit
-     */
-    public void visit (final EllipticalFinSet visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Visit a FreeformFinSet object.
-     *
-     * @param visitable the FreeformFinSet to visit
-     */
-    public void visit (final FreeformFinSet visitable) {
-        strategy.visit(visitable);
-    }
-
-    /**
-     * Perform any cleanup or finishing operations.
-     */
-    public void close () {
-        strategy.close();
-    }
-
-}
diff --git a/src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java b/src/net/sf/openrocket/rocketcomponent/ComponentVisitorStrategy.java
deleted file mode 100644 (file)
index a4cc334..0000000
+++ /dev/null
@@ -1,136 +0,0 @@
-/*
- * ComponentVisitorStrategy.java
- */
-package net.sf.openrocket.rocketcomponent;
-
-/**
- * This interface defines the methods used in a Rocket component visitor.  By using a strategy, we can reuse one visitor
- * definition and just instrument it with different strategies.
- */
-public interface ComponentVisitorStrategy {
-
-    /**
-     * Visit a Rocket object.
-     *
-     * @param visitable the Rocket to visit
-     */
-    void visit (final Rocket visitable);
-
-    /**
-     * Visit a RocketComponent object.  This is used to catch any RocketComponent subclass not explicity defined by a
-     * visit method in this strategy.
-     *
-     * @param visitable the RocketComponent to visit
-     */
-    void visit (final RocketComponent visitable);
-
-    /**
-     * Visit a Stage object.
-     *
-     * @param visitable the Stage to visit
-     */
-    void visit (final Stage visitable);
-
-    /**
-     * Visit an ExternalComponent object.
-     *
-     * @param visitable the ExternalComponent to visit
-     */
-    void visit (final ExternalComponent visitable);
-
-    /**
-     * Visit a BodyComponent object.
-     *
-     * @param visitable the BodyComponent to visit
-     */
-    void visit (final BodyComponent visitable);
-
-    /**
-     * Visit a RingComponent object.
-     *
-     * @param visitable the RingComponent to visit
-     */
-    void visit (final RingComponent visitable);
-
-    /**
-     * Visit an InnerTube object.
-     *
-     * @param visitable the InnerTube to visit
-     */
-    void visit (final InnerTube visitable);
-
-    /**
-     * Visit a LaunchLug object.
-     *
-     * @param visitable the LaunchLug to visit
-     */
-    void visit (final LaunchLug visitable);
-
-    /**
-     * Visit a Transition object.
-     *
-     * @param visitable the Transition to visit
-     */
-    void visit (final Transition visitable);
-
-    /**
-     * Visit a RadiusRingComponent object.
-     *
-     * @param visitable the RadiusRingComponent to visit
-     */
-    void visit (final RadiusRingComponent visitable);
-
-    /**
-     * Visit a MassComponent object.
-     *
-     * @param visitable the MassComponent to visit
-     */
-    void visit (final MassObject visitable);
-
-    /**
-     * Visit a NoseCone object.
-     *
-     * @param visitable the NoseCone to visit
-     */
-    void visit (final NoseCone visitable);
-
-    /**
-     * Visit a BodyTube object.
-     *
-     * @param visitable the BodyTube to visit
-     */
-    void visit (final BodyTube visitable);
-
-    /**
-     * Visit a TrapezoidFinSet object.
-     *
-     * @param visitable the TrapezoidFinSet to visit
-     */
-    void visit (final TrapezoidFinSet visitable);
-
-    /**
-     * Visit an EllipticalFinSet object.
-     *
-     * @param visitable the EllipticalFinSet to visit
-     */
-    void visit (final EllipticalFinSet visitable);
-
-    /**
-     * Visit a FreeformFinSet object.
-     *
-     * @param visitable the FreeformFinSet to visit
-     */
-    void visit (final FreeformFinSet visitable);
-
-    /**
-     * Set the visitor that is using this strategy.
-     *
-     * @param parent the visitor
-     */
-    void setParent (ComponentVisitor parent);
-
-    /**
-     * Perform any cleanup or finishing operations.
-     */
-    void close ();
-}
index 22d49bea65f2850b38d68d52953c9218ec17afd3..aaaf92845ccb033545159d118f72194cb9e33787 100644 (file)
@@ -5,7 +5,7 @@ import net.sf.openrocket.util.MathUtil;
 
 public class EllipticalFinSet extends FinSet {
        public static final int POINTS = 21;
-       
+
        private static final double[] POINT_X = new double[POINTS];
        private static final double[] POINT_Y = new double[POINTS];
        static {
@@ -19,15 +19,15 @@ public class EllipticalFinSet extends FinSet {
                POINT_X[POINTS-1] = 1;
                POINT_Y[POINTS-1] = 0;
        }
-       
-       
+
+
        private double height = 0.05;
-       
+
        public EllipticalFinSet() {
                this.length = 0.05;
        }
 
-       
+
        @Override
        public Coordinate[] getFinPoints() {
                Coordinate[] points = new Coordinate[POINTS];
@@ -46,20 +46,20 @@ public class EllipticalFinSet extends FinSet {
        public String getComponentName() {
                return "Elliptical fin set";
        }
-       
-       
+
+
        public double getHeight() {
                return height;
        }
-       
+
        public void setHeight(double height) {
                if (MathUtil.equals(this.height, height))
                        return;
                this.height = height;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
        public void setLength(double length) {
                if (MathUtil.equals(this.length, length))
                        return;
@@ -67,15 +67,5 @@ public class EllipticalFinSet extends FinSet {
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
 
-    
-    /**
-     * Accept a visitor to this EllipticalFinSet in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this EllipticalFinSet
-     */
-    @Override
-    public void accept(ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-    
+
 }
index 0f19b43057a2596f1a9cad8ecff84f31a2640e98..ed543c12fe352eab0890e31d04817621afd87b05 100644 (file)
@@ -1,23 +1,23 @@
 package net.sf.openrocket.rocketcomponent;
 
-import java.util.List;
-
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.material.Material;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.unit.UnitGroup;
 import net.sf.openrocket.util.Prefs;
 
+import java.util.List;
+
 /**
  * Class of components with well-defined physical appearance and which have an effect on
- * aerodynamic simulation.  They have material defined for them, and the mass of the component 
+ * aerodynamic simulation.  They have material defined for them, and the mass of the component
  * is calculated using the component's volume.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
 public abstract class ExternalComponent extends RocketComponent {
-       
+
        public enum Finish {
                //// Rough
                ROUGH("ExternalComponent.Rough", 500e-6),
@@ -29,35 +29,35 @@ public abstract class ExternalComponent extends RocketComponent {
                SMOOTH("ExternalComponent.Smoothpaint", 20e-6),
                //// Polished
                POLISHED("ExternalComponent.Polished", 2e-6);
-               
+
                private final String name;
                private final double roughnessSize;
-               
+
                Finish(String name, double roughness) {
                        this.name = name;
                        this.roughnessSize = roughness;
                }
-               
+
                public double getRoughnessSize() {
                        return roughnessSize;
                }
-               
+
                @Override
                public String toString() {
                        final Translator trans = Application.getTranslator();
                        return trans.get(name) + " (" + UnitGroup.UNITS_ROUGHNESS.toStringUnit(roughnessSize) + ")";
                }
        }
-       
-       
+
+
        /**
         * The material of the component.
         */
        protected Material material = null;
-       
+
        protected Finish finish = Finish.NORMAL;
-       
-       
+
+
 
        /**
         * Constructor that sets the relative position of the component.
@@ -66,13 +66,13 @@ public abstract class ExternalComponent extends RocketComponent {
                super(relativePosition);
                this.material = Prefs.getDefaultComponentMaterial(this.getClass(), Material.Type.BULK);
        }
-       
+
        /**
         * Returns the volume of the component.  This value is used in calculating the mass
         * of the object.
         */
        public abstract double getComponentVolume();
-       
+
        /**
         * Calculates the mass of the component as the product of the volume and interior density.
         */
@@ -80,7 +80,7 @@ public abstract class ExternalComponent extends RocketComponent {
        public double getComponentMass() {
                return material.getDensity() * getComponentVolume();
        }
-       
+
        /**
         * ExternalComponent has aerodynamic effect, so return true.
         */
@@ -88,7 +88,7 @@ public abstract class ExternalComponent extends RocketComponent {
        public boolean isAerodynamic() {
                return true;
        }
-       
+
        /**
         * ExternalComponent has effect on the mass, so return true.
         */
@@ -96,36 +96,35 @@ public abstract class ExternalComponent extends RocketComponent {
        public boolean isMassive() {
                return true;
        }
-       
-       
+
+
        public Material getMaterial() {
                return material;
        }
-       
+
        public void setMaterial(Material mat) {
                if (mat.getType() != Material.Type.BULK) {
                        throw new IllegalArgumentException("ExternalComponent requires a bulk material" +
                                        " type=" + mat.getType());
                }
-               
+
                if (material.equals(mat))
                        return;
                material = mat;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
+
        public Finish getFinish() {
                return finish;
        }
-       
+
        public void setFinish(Finish finish) {
                if (this.finish == finish)
                        return;
                this.finish = finish;
                fireComponentChangeEvent(ComponentChangeEvent.AERODYNAMIC_CHANGE);
        }
-       
-       
+
 
        @Override
        protected List<RocketComponent> copyFrom(RocketComponent c) {
@@ -134,15 +133,5 @@ public abstract class ExternalComponent extends RocketComponent {
                this.material = src.material;
                return super.copyFrom(c);
        }
-       
-    /**
-     * Accept a visitor to this ExternalComponent in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this ExternalComponent
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-    
+
 }
index cdbb7c3e607c833bab72f92c1440539314558933..ffa44be37b0bc04e1130afdaec234e064f007aa0 100644 (file)
@@ -15,7 +15,7 @@ import net.sf.openrocket.util.Coordinate;
 public class FreeformFinSet extends FinSet {
        private static final LogHelper log = Application.getLogger();
        private static final Translator trans = Application.getTranslator();
-
+       
        private ArrayList<Coordinate> points = new ArrayList<Coordinate>();
        
        public FreeformFinSet() {
@@ -36,7 +36,7 @@ public class FreeformFinSet extends FinSet {
        public FreeformFinSet(FinSet finset) {
                Coordinate[] finpoints = finset.getFinPoints();
                this.copyFrom(finset);
-               
+
                points.clear();
                for (Coordinate c: finpoints) {
                        points.add(c);
@@ -52,7 +52,7 @@ public class FreeformFinSet extends FinSet {
         * inserted in its stead.
         * <p>
         * The specified fin set should not be used after the call!
-        * 
+        *
         * @param finset        the fin set to convert.
         * @return                      the new freeform fin set.
         */
@@ -122,7 +122,7 @@ public class FreeformFinSet extends FinSet {
        /**
         * Add a fin point between indices <code>index-1</code> and <code>index</code>.
         * The point is placed at the midpoint of the current segment.
-        * 
+        *
         * @param index   the fin point before which to add the new point.
         */
        public void addPoint(int index) {
@@ -143,7 +143,7 @@ public class FreeformFinSet extends FinSet {
         * Remove the fin point with the given index.  The first and last fin points
         * cannot be removed, and will cause an <code>IllegalFinPointException</code>
         * if attempted.
-        * 
+        *
         * @param index   the fin point index to remove
         * @throws IllegalFinPointException if removing would result in invalid fin planform
         */
@@ -182,13 +182,13 @@ public class FreeformFinSet extends FinSet {
         * Set the point at position <code>i</code> to coordinates (x,y).
         * <p>
         * Note that this method enforces basic fin shape restrictions (non-negative y,
-        * first and last point locations) silently, but throws an 
+        * first and last point locations) silently, but throws an
         * <code>IllegalFinPointException</code> if the point causes fin segments to
         * intersect.
         * <p>
         * Moving of the first point in the X-axis is allowed, but this actually moves
         * all of the other points the corresponding distance back.
-        * 
+        *
         * @param index the point index to modify.
         * @param x             the x-coordinate.
         * @param y             the y-coordinate.
@@ -317,30 +317,20 @@ public class FreeformFinSet extends FinSet {
                return c;
        }
        
-       /**
-        * Accept a visitor to this FreeformFinSet in the component hierarchy.
-        * 
-        * @param theVisitor  the visitor that will be called back with a reference to this FreeformFinSet
-        */
-       @Override
-       public void accept(ComponentVisitor theVisitor) {
-               theVisitor.visit(this);
-       }
-       
-       private void validate(ArrayList<Coordinate> points) throws IllegalFinPointException {
-               final int n = points.size();
-               if (points.get(0).x != 0 || points.get(0).y != 0 ||
-                               points.get(n - 1).x < 0 || points.get(n - 1).y != 0) {
+       private void validate(ArrayList<Coordinate> pts) throws IllegalFinPointException {
+               final int n = pts.size();
+               if (pts.get(0).x != 0 || pts.get(0).y != 0 ||
+                               pts.get(n - 1).x < 0 || pts.get(n - 1).y != 0) {
                        throw new IllegalFinPointException("Start or end point illegal.");
                }
                for (int i = 0; i < n - 1; i++) {
                        for (int j = i + 2; j < n - 1; j++) {
-                               if (intersects(points.get(i).x, points.get(i).y, points.get(i + 1).x, points.get(i + 1).y,
-                                                               points.get(j).x, points.get(j).y, points.get(j + 1).x, points.get(j + 1).y)) {
+                               if (intersects(pts.get(i).x, pts.get(i).y, pts.get(i + 1).x, pts.get(i + 1).y,
+                                                               pts.get(j).x, pts.get(j).y, pts.get(j + 1).x, pts.get(j + 1).y)) {
                                        throw new IllegalFinPointException("segments intersect");
                                }
                        }
-                       if (points.get(i).z != 0) {
+                       if (pts.get(i).z != 0) {
                                throw new IllegalFinPointException("z-coordinate not zero");
                        }
                }
index 448da1549f9ab032346030589b3d0b5459dec980..0b8c92d242fbca6150d342fdbdc29400408263cb 100644 (file)
@@ -1,5 +1,9 @@
 package net.sf.openrocket.rocketcomponent;
 
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.motor.Motor;
 import net.sf.openrocket.startup.Application;
@@ -7,21 +11,17 @@ import net.sf.openrocket.util.BugException;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-
 
 /**
  * This class defines an inner tube that can be used as a motor mount.  The component
  * may also be clustered.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public class InnerTube extends ThicknessRingComponent
                implements Clusterable, RadialParent, MotorMount {
        private static final Translator trans = Application.getTranslator();
-
+       
        private ClusterConfiguration cluster = ClusterConfiguration.SINGLE;
        private double clusterScale = 1.0;
        private double clusterRotation = 0.0;
@@ -85,6 +85,7 @@ public class InnerTube extends ThicknessRingComponent
         * Get the current cluster configuration.
         * @return  The current cluster configuration.
         */
+       @Override
        public ClusterConfiguration getClusterConfiguration() {
                return cluster;
        }
@@ -93,6 +94,7 @@ public class InnerTube extends ThicknessRingComponent
         * Set the current cluster configuration.
         * @param cluster  The cluster configuration.
         */
+       @Override
        public void setClusterConfiguration(ClusterConfiguration cluster) {
                this.cluster = cluster;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
@@ -319,22 +321,10 @@ public class InnerTube extends ThicknessRingComponent
                return new Coordinate(this.getLength() - motor.getLength() + this.getMotorOverhang());
        }
        
-    /**
-     * Accept a visitor to an InnerTube object in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this InnerTube
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-       
-
-
        /*
         * (non-Javadoc)
         * Copy the motor and ejection delay HashMaps.
-        * 
+        *
         * @see rocketcomponent.RocketComponent#copy()
         */
        @SuppressWarnings("unchecked")
index f61ce7ed6bb2275ea0961b48b119da1b7f45aad1..8a6b2751da247f3136f99fcf76714b716500884e 100644 (file)
@@ -16,13 +16,13 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
 
        private double radius;
        private double thickness;
-       
+
        private double radialDirection = 0;
-       
+
        /* These are calculated when the component is first attached to any Rocket */
        private double shiftY, shiftZ;
-       
-       
+
+
 
        public LaunchLug() {
                super(Position.MIDDLE);
@@ -30,12 +30,12 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                thickness = 0.001;
                length = 0.03;
        }
-       
-       
+
+
        public double getOuterRadius () {
                return radius;
        }
-       
+
        public void setOuterRadius (double radius) {
                if (MathUtil.equals(this.radius, radius))
                        return;
@@ -43,31 +43,31 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                this.thickness = Math.min(this.thickness, this.radius);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
+
        public double getInnerRadius() {
                return radius - thickness;
        }
-       
+
        public void setInnerRadius(double innerRadius) {
                setOuterRadius(innerRadius + thickness);
        }
-       
+
        public double getThickness() {
                return thickness;
        }
-       
+
        public void setThickness(double thickness) {
                if (MathUtil.equals(this.thickness, thickness))
                        return;
                this.thickness = MathUtil.clamp(thickness, 0, radius);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
        public double getRadialDirection() {
                return radialDirection;
        }
-       
+
        public void setRadialDirection(double direction) {
                direction = MathUtil.reduce180(direction);
                if (MathUtil.equals(this.radialDirection, direction))
@@ -75,8 +75,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                this.radialDirection = direction;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
 
        public void setLength(double length) {
                if (MathUtil.equals(this.length, length))
@@ -84,8 +84,8 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                this.length = length;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
 
 
 
@@ -94,44 +94,44 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                super.setRelativePosition(position);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
        @Override
        public void setPositionValue(double value) {
                super.setPositionValue(value);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
-       
+
+
 
        @Override
        public Coordinate[] shiftCoordinates(Coordinate[] array) {
                array = super.shiftCoordinates(array);
-               
+
                for (int i = 0; i < array.length; i++) {
                        array[i] = array[i].add(0, shiftY, shiftZ);
                }
-               
+
                return array;
        }
-       
-       
+
+
        @Override
        public void componentChanged(ComponentChangeEvent e) {
                super.componentChanged(e);
-               
-               /* 
+
+               /*
                 * shiftY and shiftZ must be computed here since calculating them
                 * in shiftCoordinates() would cause an infinite loop due to .toRelative
                 */
                RocketComponent body;
                double parentRadius;
-               
+
                for (body = this.getParent(); body != null; body = body.getParent()) {
                        if (body instanceof SymmetricComponent)
                                break;
                }
-               
+
                if (body == null) {
                        parentRadius = 0;
                } else {
@@ -143,21 +143,21 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                        x2 = MathUtil.clamp(x2, 0, body.getLength());
                        parentRadius = Math.max(s.getRadius(x1), s.getRadius(x2));
                }
-               
+
                shiftY = Math.cos(radialDirection) * (parentRadius + radius);
                shiftZ = Math.sin(radialDirection) * (parentRadius + radius);
-               
+
                //              System.out.println("Computed shift: y="+shiftY+" z="+shiftZ);
        }
-       
-       
+
+
 
 
        @Override
        public double getComponentVolume() {
                return length * Math.PI * (MathUtil.pow2(radius) - MathUtil.pow2(radius - thickness));
        }
-       
+
        @Override
        public Collection<Coordinate> getComponentBounds() {
                ArrayList<Coordinate> set = new ArrayList<Coordinate>();
@@ -165,50 +165,40 @@ public class LaunchLug extends ExternalComponent implements Coaxial {
                addBound(set, length, radius);
                return set;
        }
-       
+
        @Override
        public Coordinate getComponentCG() {
                return new Coordinate(length / 2, 0, 0, getComponentMass());
        }
-       
+
        @Override
        public String getComponentName() {
                //// Launch lug
                return trans.get("LaunchLug.Launchlug");
        }
-       
+
        @Override
        public double getLongitudinalUnitInertia() {
                // 1/12 * (3 * (r1^2 + r2^2) + h^2)
                return (3 * (MathUtil.pow2(getInnerRadius())) + MathUtil.pow2(getOuterRadius()) +
                                MathUtil.pow2(getLength())) / 12;
        }
-       
+
        @Override
        public double getRotationalUnitInertia() {
                // 1/2 * (r1^2 + r2^2)
                return (MathUtil.pow2(getInnerRadius()) + MathUtil.pow2(getOuterRadius()))/2;
        }
-       
+
        @Override
        public boolean allowsChildren() {
                return false;
        }
-       
+
        @Override
        public boolean isCompatible(Class<? extends RocketComponent> type) {
                // Allow nothing to be attached to a LaunchLug
                return false;
        }
-       
-    /**
-     * Accept a visitor to this LaunchLug in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this LaunchLug
-     */    
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-    
+
 }
index bbd9789db1b98a006985e00144ecc8308d944522..70d22ff3b6d0b84bf3430c7cf9f396f11372a0bd 100644 (file)
@@ -1,12 +1,12 @@
 package net.sf.openrocket.rocketcomponent;
 
-import static net.sf.openrocket.util.MathUtil.pow2;
+import net.sf.openrocket.util.Coordinate;
+import net.sf.openrocket.util.MathUtil;
 
 import java.util.ArrayList;
 import java.util.Collection;
 
-import net.sf.openrocket.util.Coordinate;
-import net.sf.openrocket.util.MathUtil;
+import static net.sf.openrocket.util.MathUtil.pow2;
 
 
 /**
@@ -19,31 +19,31 @@ import net.sf.openrocket.util.MathUtil;
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public abstract class MassObject extends InternalComponent {
-       
+
        private double radius;
-       
+
        private double radialPosition;
        private double radialDirection;
-       
+
        private double shiftY = 0;
        private double shiftZ = 0;
-       
-       
+
+
        public MassObject() {
                this(0.03, 0.015);
        }
-       
+
        public MassObject(double length, double radius) {
                super();
-               
+
                this.length = length;
                this.radius = radius;
-               
+
                this.setRelativePosition(Position.TOP);
                this.setPositionValue(0.0);
        }
-       
-       
+
+
        public void setLength(double length) {
                length = Math.max(length, 0);
                if (MathUtil.equals(this.length, length)) {
@@ -52,13 +52,13 @@ public abstract class MassObject extends InternalComponent {
                this.length = length;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
        public final double getRadius() {
                return radius;
        }
-       
-       
+
+
        public final void setRadius(double radius) {
                radius = Math.max(radius, 0);
                if (MathUtil.equals(this.radius, radius)) {
@@ -67,13 +67,13 @@ public abstract class MassObject extends InternalComponent {
                this.radius = radius;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
 
        public final double getRadialPosition() {
                return radialPosition;
        }
-       
+
        public final void setRadialPosition(double radialPosition) {
                radialPosition = Math.max(radialPosition, 0);
                if (MathUtil.equals(this.radialPosition, radialPosition)) {
@@ -84,11 +84,11 @@ public abstract class MassObject extends InternalComponent {
                shiftZ = radialPosition * Math.sin(radialDirection);
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
+
        public final double getRadialDirection() {
                return radialDirection;
        }
-       
+
        public final void setRadialDirection(double radialDirection) {
                radialDirection = MathUtil.reduce180(radialDirection);
                if (MathUtil.equals(this.radialDirection, radialDirection)) {
@@ -99,8 +99,8 @@ public abstract class MassObject extends InternalComponent {
                shiftZ = radialPosition * Math.sin(radialDirection);
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
        /**
         * Shift the coordinates according to the radial position and direction.
         */
@@ -111,22 +111,22 @@ public abstract class MassObject extends InternalComponent {
                }
                return array;
        }
-       
+
        @Override
        public final Coordinate getComponentCG() {
                return new Coordinate(length / 2, shiftY, shiftZ, getComponentMass());
        }
-       
+
        @Override
        public final double getLongitudinalUnitInertia() {
                return (3 * pow2(radius) + pow2(length)) / 12;
        }
-       
+
        @Override
        public final double getRotationalUnitInertia() {
                return pow2(radius) / 2;
        }
-       
+
        @Override
        public final Collection<Coordinate> getComponentBounds() {
                Collection<Coordinate> c = new ArrayList<Coordinate>();
@@ -134,15 +134,5 @@ public abstract class MassObject extends InternalComponent {
                addBound(c, length, radius);
                return c;
        }
-       
-       /**
-        * Accept a visitor to this MassObject in the component hierarchy.
-        *
-        * @param theVisitor the visitor that will be called back with a reference to this MassObject
-        */
-       @Override
-       public void accept(final ComponentVisitor theVisitor) {
-               theVisitor.visit(this);
-       }
-       
+
 }
index c271e2d442b6cd5044a24b298e35c6b082c97e7e..a4de3a9c8aeabcded7bc652c14c99b24539d0120 100644 (file)
@@ -6,17 +6,17 @@ import net.sf.openrocket.startup.Application;
 /**
  * Rocket nose cones of various types.  Implemented as a transition with the
  * fore radius == 0.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
 public class NoseCone extends Transition {
        private static final Translator trans = Application.getTranslator();
-
+       
        
        /********* Constructors **********/
        public NoseCone() {
-               this(Transition.Shape.OGIVE, 6*DEFAULT_RADIUS, DEFAULT_RADIUS);
+               this(Transition.Shape.OGIVE, 6 * DEFAULT_RADIUS, DEFAULT_RADIUS);
        }
        
        public NoseCone(Transition.Shape type, double length, double radius) {
@@ -25,7 +25,7 @@ public class NoseCone extends Transition {
                super.setForeRadiusAutomatic(false);
                super.setForeRadius(0);
                super.setForeShoulderLength(0);
-               super.setForeShoulderRadius(0.9*radius);
+               super.setForeShoulderRadius(0.9 * radius);
                super.setForeShoulderThickness(0);
                super.setForeShoulderCapped(filled);
                super.setThickness(0.002);
@@ -34,9 +34,9 @@ public class NoseCone extends Transition {
                
        }
        
-
+       
        /********** Get/set methods for component parameters **********/
-
+       
        @Override
        public double getForeRadius() {
                return 0;
@@ -46,7 +46,7 @@ public class NoseCone extends Transition {
        public void setForeRadius(double r) {
                // No-op
        }
-
+       
        @Override
        public boolean isForeRadiusAutomatic() {
                return false;
@@ -56,47 +56,47 @@ public class NoseCone extends Transition {
        public void setForeRadiusAutomatic(boolean b) {
                // No-op
        }
-
+       
        @Override
        public double getForeShoulderLength() {
                return 0;
        }
-
+       
        @Override
        public double getForeShoulderRadius() {
                return 0;
        }
-
+       
        @Override
        public double getForeShoulderThickness() {
                return 0;
        }
-
+       
        @Override
        public boolean isForeShoulderCapped() {
                return false;
        }
-
+       
        @Override
        public void setForeShoulderCapped(boolean capped) {
                // No-op
        }
-
+       
        @Override
        public void setForeShoulderLength(double foreShoulderLength) {
                // No-op
        }
-
+       
        @Override
        public void setForeShoulderRadius(double foreShoulderRadius) {
                // No-op
        }
-
+       
        @Override
        public void setForeShoulderThickness(double foreShoulderThickness) {
                // No-op
        }
-
+       
        @Override
        public boolean isClipped() {
                return false;
@@ -107,10 +107,10 @@ public class NoseCone extends Transition {
                // No-op
        }
        
-
        
-       /********** RocketComponent methods **********/
 
+       /********** RocketComponent methods **********/
+       
        /**
         * Return component name.
         */
@@ -119,14 +119,5 @@ public class NoseCone extends Transition {
                //// Nose cone
                return trans.get("NoseCone.NoseCone");
        }
-    
-    /**
-     * Accept a visitor to this NoseCone in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this NoseCone
-     */
-    @Override
-    public void accept(ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
+       
 }
index b3d862510c99505c3b7b81ca35f349eef8446fa9..46a35143900afddd218c2cda1d29facf7139f4c7 100644 (file)
@@ -6,16 +6,16 @@ import net.sf.openrocket.util.MathUtil;
 /**
  * An inner component that consists of a hollow cylindrical component.  This can be
  * an inner tube, tube coupler, centering ring, bulkhead etc.
- * 
+ *
  * The properties include the inner and outer radii, length and radial position.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public abstract class RadiusRingComponent extends RingComponent implements Coaxial {
 
        protected double outerRadius = 0;
        protected double innerRadius = 0;
-       
+
        @Override
        public double getOuterRadius() {
                if (outerRadiusAutomatic && getParent() instanceof RadialParent) {
@@ -27,7 +27,7 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi
                        outerRadius = Math.min(((RadialParent)parent).getInnerRadius(pos1),
                                        ((RadialParent)parent).getInnerRadius(pos2));
                }
-                               
+
                return outerRadius;
        }
 
@@ -36,7 +36,7 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi
                r = Math.max(r,0);
                if (MathUtil.equals(outerRadius, r) && !isOuterRadiusAutomatic())
                        return;
-               
+
                outerRadius = r;
                outerRadiusAutomatic = false;
                if (getInnerRadius() > r) {
@@ -46,8 +46,8 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi
 
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
        @Override
        public double getInnerRadius() {
                return innerRadius;
@@ -57,18 +57,18 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi
                r = Math.max(r,0);
                if (MathUtil.equals(innerRadius, r))
                        return;
-               
+
                innerRadius = r;
                innerRadiusAutomatic = false;
                if (getOuterRadius() < r) {
                        outerRadius = r;
                        outerRadiusAutomatic = false;
                }
-               
+
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
        @Override
        public double getThickness() {
                return Math.max(getOuterRadius() - getInnerRadius(), 0);
@@ -76,19 +76,9 @@ public abstract class RadiusRingComponent extends RingComponent implements Coaxi
        @Override
        public void setThickness(double thickness) {
                double outer = getOuterRadius();
-               
+
                thickness = MathUtil.clamp(thickness, 0, outer);
                setInnerRadius(outer - thickness);
        }
 
-    /**
-     * Accept a visitor to this RadiusRingComponent in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this RadiusRingComponent
-     */    
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-    
 }
index b6c6cec60f729dac5ad8fca6952f5d390c820fc9..233457c25e8929637e37919e680afff27c07bcee 100644 (file)
@@ -11,90 +11,90 @@ import java.util.List;
 /**
  * An inner component that consists of a hollow cylindrical component.  This can be
  * an inner tube, tube coupler, centering ring, bulkhead etc.
- * 
+ *
  * The properties include the inner and outer radii, length and radial position.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 public abstract class RingComponent extends StructuralComponent implements Coaxial {
 
        protected boolean outerRadiusAutomatic = false;
        protected boolean innerRadiusAutomatic = false;
-       
-       
+
+
        private double radialDirection = 0;
        private double radialPosition = 0;
-       
+
        private double shiftY = 0;
        private double shiftZ = 0;
-       
 
-       
+
+
     @Override
        public abstract double getOuterRadius();
     @Override
        public abstract void setOuterRadius(double r);
-       
+
     @Override
-       public abstract double getInnerRadius();        
+       public abstract double getInnerRadius();
     @Override
        public abstract void setInnerRadius(double r);
-       
+
     @Override
        public abstract double getThickness();
        public abstract void setThickness(double thickness);
-       
-       
+
+
        public final boolean isOuterRadiusAutomatic() {
                return outerRadiusAutomatic;
        }
-       
+
        protected void setOuterRadiusAutomatic(boolean auto) {
                if (auto == outerRadiusAutomatic)
                        return;
                outerRadiusAutomatic = auto;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
+
+
        public final boolean isInnerRadiusAutomatic() {
                return innerRadiusAutomatic;
        }
-       
+
        protected void setInnerRadiusAutomatic(boolean auto) {
                if (auto == innerRadiusAutomatic)
                        return;
                innerRadiusAutomatic = auto;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
-       
-       
-       
-       
+
+
+
+
        public final void setLength(double length) {
                double l = Math.max(length,0);
                if (this.length == l)
                        return;
-               
+
                this.length = l;
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
 
-       
+
        /**
         * Return the radial direction of displacement of the component.  Direction 0
         * is equivalent to the Y-direction.
-        * 
+        *
         * @return  the radial direction.
         */
        public double getRadialDirection() {
                return radialDirection;
        }
-       
+
        /**
         * Set the radial direction of displacement of the component.  Direction 0
         * is equivalent to the Y-direction.
-        * 
+        *
         * @param dir  the radial direction.
         */
        public void setRadialDirection(double dir) {
@@ -107,23 +107,23 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
 
-       
-       
-       
+
+
+
        /**
         * Return the radial position of the component.  The position is the distance
         * of the center of the component from the center of the parent component.
-        * 
+        *
         * @return  the radial position.
         */
        public double getRadialPosition() {
                return radialPosition;
        }
-       
+
        /**
         * Set the radial position of the component.  The position is the distance
         * of the center of the component from the center of the parent component.
-        * 
+        *
         * @param pos  the radial position.
         */
        public void setRadialPosition(double pos) {
@@ -136,25 +136,25 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
 
-       
+
        public double getRadialShiftY() {
                return shiftY;
        }
-       
+
        public double getRadialShiftZ() {
                return shiftZ;
        }
-       
+
        public void setRadialShift(double y, double z) {
                radialPosition = Math.hypot(y, z);
                radialDirection = Math.atan2(z, y);
-               
-               // Re-calculate to ensure consistency 
+
+               // Re-calculate to ensure consistency
                shiftY = radialPosition * Math.cos(radialDirection);
                shiftZ = radialPosition * Math.sin(radialDirection);
                assert(MathUtil.equals(y, shiftY));
                assert(MathUtil.equals(z, shiftZ));
-               
+
                fireComponentChangeEvent(ComponentChangeEvent.MASS_CHANGE);
        }
 
@@ -167,8 +167,8 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                        return ((Clusterable)this).getClusterConfiguration().getClusterCount();
                return 1;
        }
-       
-       
+
+
        /**
         * Shift the coordinates according to the radial position and direction.
         */
@@ -179,8 +179,8 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                }
                return array;
        }
-       
-       
+
+
        @Override
        public Collection<Coordinate> getComponentBounds() {
                List<Coordinate> bounds = new ArrayList<Coordinate>();
@@ -188,9 +188,9 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                addBound(bounds,length,getOuterRadius());
                return bounds;
        }
-       
 
-       
+
+
        @Override
        public Coordinate getComponentCG() {
                return new Coordinate(length/2, 0, 0, getComponentMass());
@@ -201,7 +201,7 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                return ringMass(getOuterRadius(), getInnerRadius(), getLength(),
                                getMaterial().getDensity()) * getClusterCount();
        }
-       
+
 
        @Override
        public double getLongitudinalUnitInertia() {
@@ -213,14 +213,4 @@ public abstract class RingComponent extends StructuralComponent implements Coaxi
                return ringRotationalUnitInertia(getOuterRadius(), getInnerRadius());
        }
 
-    /**
-     * Accept a visitor to this RingComponent in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this RingComponent
-     */    
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-
 }
index 0be57cd16b6dcb88b3274d5f3ddd50a0937f010e..4115c87b6f43551e728d1877ff12544eedd604b1 100644 (file)
@@ -29,7 +29,7 @@ import net.sf.openrocket.util.UniqueID;
  * (eg. the rocket listener lists) and the methods defined in RocketComponent call these.
  * It also defines some other methods that concern the whole rocket, and helper methods
  * that keep information about the program state.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
@@ -128,7 +128,7 @@ public class Rocket extends RocketComponent {
 
        /**
         * Return the number of stages in this rocket.
-        * 
+        *
         * @return   the number of stages in this rocket.
         */
        public int getStageCount() {
@@ -139,20 +139,20 @@ public class Rocket extends RocketComponent {
        
        /**
         * Return the non-negative modification ID of this rocket.  The ID is changed
-        * every time any change occurs in the rocket.  This can be used to check 
+        * every time any change occurs in the rocket.  This can be used to check
         * whether it is necessary to void cached data in cases where listeners can not
         * or should not be used.
         * <p>
         * Three other modification IDs are also available, {@link #getMassModID()},
-        * {@link #getAerodynamicModID()} {@link #getTreeModID()}, which change every time 
-        * a mass change, aerodynamic change, or tree change occur.  Even though the values 
-        * of the different modification ID's may be equal, they should be treated totally 
+        * {@link #getAerodynamicModID()} {@link #getTreeModID()}, which change every time
+        * a mass change, aerodynamic change, or tree change occur.  Even though the values
+        * of the different modification ID's may be equal, they should be treated totally
         * separate.
         * <p>
         * Note that undo events restore the modification IDs that were in use at the
         * corresponding undo level.  Subsequent modifications, however, produce modIDs
         * distinct from those already used.
-        * 
+        *
         * @return   a unique ID number for this modification state.
         */
        public int getModID() {
@@ -162,7 +162,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return the non-negative mass modification ID of this rocket.  See
         * {@link #getModID()} for details.
-        * 
+        *
         * @return   a unique ID number for this mass-modification state.
         */
        public int getMassModID() {
@@ -172,7 +172,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return the non-negative aerodynamic modification ID of this rocket.  See
         * {@link #getModID()} for details.
-        * 
+        *
         * @return   a unique ID number for this aerodynamic-modification state.
         */
        public int getAerodynamicModID() {
@@ -182,7 +182,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return the non-negative tree modification ID of this rocket.  See
         * {@link #getModID()} for details.
-        * 
+        *
         * @return   a unique ID number for this tree-modification state.
         */
        public int getTreeModID() {
@@ -192,7 +192,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return the non-negative functional modificationID of this rocket.
         * This changes every time a functional change occurs.
-        * 
+        *
         * @return      a unique ID number for this functional modification state.
         */
        public int getFunctionalModID() {
@@ -238,7 +238,7 @@ public class Rocket extends RocketComponent {
        /**
         * Set whether the rocket has a perfect finish.  This will affect whether the
         * boundary layer is assumed to be fully turbulent or not.
-        * 
+        *
         * @param perfectFinish         whether the finish is perfect.
         */
        public void setPerfectFinish(boolean perfectFinish) {
@@ -252,7 +252,7 @@ public class Rocket extends RocketComponent {
 
        /**
         * Get whether the rocket has a perfect finish.
-        * 
+        *
         * @return the perfectFinish
         */
        public boolean isPerfectFinish() {
@@ -437,7 +437,7 @@ public class Rocket extends RocketComponent {
         * Freezes the rocket structure from firing any events.  This may be performed to
         * combine several actions on the structure into a single large action.
         * <code>thaw()</code> must always be called afterwards.
-        * 
+        *
         * NOTE:  Always use a try/finally to ensure <code>thaw()</code> is called:
         * <pre>
         *     Rocket r = c.getRocket();
@@ -448,7 +448,7 @@ public class Rocket extends RocketComponent {
         *         r.thaw();
         *     }
         * </pre>
-        * 
+        *
         * @see #thaw()
         */
        public void freeze() {
@@ -504,7 +504,7 @@ public class Rocket extends RocketComponent {
         * Return the default configuration.  This should be used in the user interface
         * to ensure a consistent rocket configuration between dialogs.  It should NOT
         * be used in simulations not relating to the UI.
-        * 
+        *
         * @return   the default {@link Configuration}.
         */
        public Configuration getDefaultConfiguration() {
@@ -516,7 +516,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return an array of the motor configuration IDs.  This array is guaranteed
         * to contain the <code>null</code> ID as the first element.
-        * 
+        *
         * @return  an array of the motor configuration IDs.
         */
        public String[] getMotorConfigurationIDs() {
@@ -527,7 +527,7 @@ public class Rocket extends RocketComponent {
        /**
         * Add a new motor configuration ID to the motor configurations.  The new ID
         * is returned.
-        * 
+        *
         * @return  the new motor configuration ID.
         */
        public String newMotorConfigurationID() {
@@ -540,7 +540,7 @@ public class Rocket extends RocketComponent {
        
        /**
         * Add a specified motor configuration ID to the motor configurations.
-        * 
+        *
         * @param id    the motor configuration ID.
         * @return              true if successful, false if the ID was already used.
         */
@@ -556,7 +556,7 @@ public class Rocket extends RocketComponent {
        /**
         * Remove a motor configuration ID from the configuration IDs.  The <code>null</code>
         * ID cannot be removed, and an attempt to remove it will be silently ignored.
-        * 
+        *
         * @param id   the motor configuration ID to remove
         */
        public void removeMotorConfigurationID(String id) {
@@ -570,7 +570,7 @@ public class Rocket extends RocketComponent {
        
        /**
         * Check whether <code>id</code> is a valid motor configuration ID.
-        * 
+        *
         * @param id    the configuration ID.
         * @return              whether a motor configuration with that ID exists.
         */
@@ -583,7 +583,7 @@ public class Rocket extends RocketComponent {
 
        /**
         * Check whether the given motor configuration ID has motors defined for it.
-        * 
+        *
         * @param id    the motor configuration ID (may be invalid).
         * @return              whether any motors are defined for it.
         */
@@ -612,7 +612,7 @@ public class Rocket extends RocketComponent {
        /**
         * Return the user-set name of the motor configuration.  If no name has been set,
         * returns an empty string (not null).
-        *  
+        *
         * @param id   the motor configuration id
         * @return         the configuration name
         */
@@ -630,7 +630,7 @@ public class Rocket extends RocketComponent {
        /**
         * Set the name of the motor configuration.  A name can be unset by passing
         * <code>null</code> or an empty string.
-        * 
+        *
         * @param id    the motor configuration id
         * @param name  the name for the motor configuration
         */
@@ -642,8 +642,8 @@ public class Rocket extends RocketComponent {
        
        
        /**
-        * Return either the motor configuration name (if set) or its description. 
-        * 
+        * Return either the motor configuration name (if set) or its description.
+        *
         * @param id  the motor configuration ID.
         * @return    a textual representation of the configuration
         */
@@ -659,9 +659,9 @@ public class Rocket extends RocketComponent {
        }
        
        /**
-        * Return a description for the motor configuration, generated from the motor 
+        * Return a description for the motor configuration, generated from the motor
         * designations of the components.
-        * 
+        *
         * @param id  the motor configuration ID.
         * @return    a textual representation of the configuration
         */
@@ -708,7 +708,7 @@ public class Rocket extends RocketComponent {
                        return trans.get("Rocket.motorCount.Nomotor");
                }
                
-               // Change multiple occurrences of a motor to n x motor 
+               // Change multiple occurrences of a motor to n x motor
                List<String> stages = new ArrayList<String>();
                
                for (List<String> stage : list) {
@@ -833,13 +833,4 @@ public class Rocket extends RocketComponent {
                return (Stage.class.isAssignableFrom(type));
        }
        
-       /**
-        * Accept a visitor to this Rocket in the component hierarchy.
-        * 
-        * @param theVisitor  the visitor that will be called back with a reference to this Rocket
-        */
-       @Override
-       public void accept(final ComponentVisitor theVisitor) {
-               theVisitor.visit(this);
-       }
 }
index b525a47d35e18dc16ebf77605fe23eb6697449e8..158c1b7d48c6910b8434f5bdd84167a99cab442e 100644 (file)
@@ -24,11 +24,10 @@ import net.sf.openrocket.util.SafetyMutex;
 import net.sf.openrocket.util.UniqueID;
 
 
-public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent>,
-               Visitable<ComponentVisitor, RocketComponent> {
+public abstract class RocketComponent implements ChangeSource, Cloneable, Iterable<RocketComponent> {
        private static final LogHelper log = Application.getLogger();
        private static final Translator trans = Application.getTranslator();
-
+       
        /*
         * Text is suitable to the form
         *    Position relative to:  <title>
@@ -165,22 +164,22 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
 
        /**
-        * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.  
+        * Return the longitudinal (around the y- or z-axis) unitary moment of inertia.
         * The unitary moment of inertia is the moment of inertia with the assumption that
         * the mass of the component is one kilogram.  The inertia is measured in
         * respect to the non-overridden CG.
-        * 
+        *
         * @return   the longitudinal unitary moment of inertia of this component.
         */
        public abstract double getLongitudinalUnitInertia();
        
        
        /**
-        * Return the rotational (around the x-axis) unitary moment of inertia.  
+        * Return the rotational (around the x-axis) unitary moment of inertia.
         * The unitary moment of inertia is the moment of inertia with the assumption that
         * the mass of the component is one kilogram.  The inertia is measured in
         * respect to the non-overridden CG.
-        * 
+        *
         * @return   the rotational unitary moment of inertia of this component.
         */
        public abstract double getRotationalUnitInertia();
@@ -190,7 +189,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Test whether this component allows any children components.  This method must
         * return true if and only if {@link #isCompatible(Class)} returns true for any
         * rocket component class.
-        * 
+        *
         * @return      <code>true</code> if children can be attached to this component, <code>false</code> otherwise.
         */
        public abstract boolean allowsChildren();
@@ -200,7 +199,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * is enforced by the <code>addChild()</code> methods.  The return value of this method
         * may change to reflect the current state of this component (e.g. two components of some
         * type cannot be placed as children).
-        * 
+        *
         * @param type  The RocketComponent class type to add.
         * @return      Whether such a component can be added.
         */
@@ -211,7 +210,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Test whether the given component can be added to this component.  This is equivalent
         * to calling <code>isCompatible(c.getClass())</code>.
-        * 
+        *
         * @param c  Component to test.
         * @return   Whether the component can be added.
         * @see #isCompatible(Class)
@@ -226,7 +225,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return a collection of bounding coordinates.  The coordinates must be such that
         * the component is fully enclosed in their convex hull.
-        * 
+        *
         * @return      a collection of coordinates that bound the component.
         */
        public abstract Collection<Coordinate> getComponentBounds();
@@ -255,7 +254,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * coordinate for each cluster.
         * <p>
         * The default implementation simply returns the array, and thus produces no shift.
-        * 
+        *
         * @param c   an array of coordinates to shift.
         * @return    an array of shifted coordinates.  The method may modify the contents
         *                        of the passed array and return the array itself.
@@ -267,11 +266,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Called when any component in the tree fires a ComponentChangeEvent.  This is by 
-        * default a no-op, but subclasses may override this method to e.g. invalidate 
-        * cached data.  The overriding method *must* call 
+        * Called when any component in the tree fires a ComponentChangeEvent.  This is by
+        * default a no-op, but subclasses may override this method to e.g. invalidate
+        * cached data.  The overriding method *must* call
         * <code>super.componentChanged(e)</code> at some point.
-        * 
+        *
         * @param e  The event fired
         */
        protected void componentChanged(ComponentChangeEvent e) {
@@ -285,7 +284,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return the user-provided name of the component, or the component base
         * name if the user-provided name is empty.  This can be used in the UI.
-        * 
+        *
         * @return A string describing the component.
         */
        @Override
@@ -328,7 +327,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Make a deep copy of the rocket component tree structure from this component
         * downwards for copying purposes.  Each component in the copy will be assigned
         * a new component ID, making it a safe copy.  This method does not fire any events.
-        * 
+        *
         * @return A deep copy of the structure.
         */
        public final RocketComponent copy() {
@@ -350,13 +349,13 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * undo/redo mechanism.  This method should not be used for other purposes,
         * such as copy/paste.  This method does not fire any events.
         * <p>
-        * This method must be overridden by any component that refers to mutable objects, 
+        * This method must be overridden by any component that refers to mutable objects,
         * or if some fields should not be copied.  This should be performed by
         * <code>RocketComponent c = super.copyWithOriginalID();</code> and then cloning/modifying
         * the appropriate fields.
         * <p>
         * This is not performed as serializing/deserializing for performance reasons.
-        * 
+        *
         * @return A deep copy of the structure.
         */
        protected RocketComponent copyWithOriginalID() {
@@ -395,16 +394,6 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        }
        
        
-       /**
-        * Accept a visitor to this RocketComponent in the component hierarchy.
-        * 
-        * @param theVisitor  the visitor that will be called back with a reference to this RocketComponent
-        */
-       @Override
-       public void accept(final ComponentVisitor theVisitor) {
-               theVisitor.visit(this);
-       }
-       
        //////////////  Methods that may not be overridden  ////////////
        
 
@@ -421,7 +410,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        }
        
        /**
-        * Set the color of the object to use in 2D figures.  
+        * Set the color of the object to use in 2D figures.
         */
        public final void setColor(Color c) {
                if ((color == null && c == null) ||
@@ -453,7 +442,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Get the current override mass.  The mass is not necessarily in use
         * at the moment.
-        * 
+        *
         * @return  the override mass
         */
        public final double getOverrideMass() {
@@ -464,7 +453,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Set the current override mass.  The mass is not set to use by this
         * method.
-        * 
+        *
         * @param m  the override mass
         */
        public final void setOverrideMass(double m) {
@@ -479,7 +468,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return whether mass override is active for this component.  This does NOT
         * take into account whether a parent component is overriding the mass.
-        * 
+        *
         * @return  whether the mass is overridden
         */
        public final boolean isMassOverridden() {
@@ -489,7 +478,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Set whether the mass is currently overridden.
-        * 
+        *
         * @param o  whether the mass is overridden
         */
        public final void setMassOverridden(boolean o) {
@@ -507,7 +496,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
 
        /**
         * Return the current override CG.  The CG is not necessarily overridden.
-        * 
+        *
         * @return  the override CG
         */
        public final Coordinate getOverrideCG() {
@@ -517,7 +506,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Return the x-coordinate of the current override CG.
-        * 
+        *
         * @return      the x-coordinate of the override CG.
         */
        public final double getOverrideCGX() {
@@ -527,7 +516,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Set the current override CG to (x,0,0).
-        * 
+        *
         * @param x  the x-coordinate of the override CG to set.
         */
        public final void setOverrideCGX(double x) {
@@ -543,7 +532,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Return whether the CG is currently overridden.
-        * 
+        *
         * @return  whether the CG is overridden
         */
        public final boolean isCGOverridden() {
@@ -553,7 +542,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Set whether the CG is currently overridden.
-        * 
+        *
         * @param o  whether the CG is overridden
         */
        public final void setCGOverridden(boolean o) {
@@ -574,7 +563,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * always or never overrides subcomponents.  In this case the subclass should
         * also override {@link #isOverrideSubcomponentsEnabled()} to return
         * <code>false</code>.
-        * 
+        *
         * @return      whether the current mass and/or CG override overrides subcomponents as well.
         */
        public boolean getOverrideSubcomponents() {
@@ -586,7 +575,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Set whether the mass and/or CG override overrides all subcomponent values
         * as well.  See {@link #getOverrideSubcomponents()} for details.
-        * 
+        *
         * @param override      whether the mass and/or CG override overrides all subcomponent.
         */
        public void setOverrideSubcomponents(boolean override) {
@@ -605,7 +594,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * <p>
         * This method may be overridden if the setting of overriding subcomponents
         * cannot be set.
-        * 
+        *
         * @return      whether the option to override subcomponents is currently enabled.
         */
        public boolean isOverrideSubcomponentsEnabled() {
@@ -644,7 +633,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return the comment of the component.  The component may contain multiple lines
         * using \n as a newline separator.
-        * 
+        *
         * @return  the comment of the component.
         */
        public final String getComment() {
@@ -654,7 +643,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Set the comment of the component.
-        * 
+        *
         * @param comment  the comment of the component.
         */
        public final void setComment(String comment) {
@@ -672,7 +661,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
 
        /**
         * Returns the unique ID of the component.
-        * 
+        *
         * @return      the ID of the component.
         */
        public final String getID() {
@@ -694,7 +683,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Get the characteristic length of the component, for example the length of a body tube
         * of the length of the root chord of a fin.  This is used in positioning the component
         * relative to its parent.
-        * 
+        *
         * If the length of a component is settable, the class must define the setter method
         * itself.
         */
@@ -722,7 +711,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * do not support setting the relative position.  A component that does support
         * it should override this with a public method that simply calls this
         * supermethod AND fire a suitable ComponentChangeEvent.
-        * 
+        *
         * @param position      the relative positioning.
         */
        protected void setRelativePosition(RocketComponent.Position position) {
@@ -766,7 +755,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Get the position value of the component.  The exact meaning of the value is
         * dependent on the current relative positioning.
-        * 
+        *
         * @return  the positional value.
         */
        public final double getPositionValue() {
@@ -783,7 +772,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * do not support setting the relative position.  A component that does support
         * it should override this with a public method that simply calls this
         * supermethod AND fire a suitable ComponentChangeEvent.
-        * 
+        *
         * @param value         the position value of the component.
         */
        public void setPositionValue(double value) {
@@ -807,16 +796,16 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Return coordinate <code>c</code> described in the coordinate system of 
+        * Return coordinate <code>c</code> described in the coordinate system of
         * <code>dest</code>.  If <code>dest</code> is <code>null</code> returns
         * absolute coordinates.
         * <p>
         * This method returns an array of coordinates, each of which represents a
         * position of the coordinate in clustered cases.  The array is guaranteed
-        * to contain at least one element.  
+        * to contain at least one element.
         * <p>
         * The current implementation does not support rotating components.
-        * 
+        *
         * @param c    Coordinate in the component's coordinate system.
         * @param dest Destination component coordinate system.
         * @return     an array of coordinates describing <code>c</code> in coordinates
@@ -895,7 +884,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
                        }
                        
                        // Check whether destination has been found or whether to backtrack
-                       // TODO: LOW: Backtracking into clustered components uses only one component 
+                       // TODO: LOW: Backtracking into clustered components uses only one component
                        if ((dest != null) && (component != dest)) {
                                Coordinate[] origin = dest.toAbsolute(Coordinate.NUL);
                                for (int i = 0; i < array.length; i++) {
@@ -911,9 +900,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Recursively sum the lengths of all subcomponents that have position 
+        * Recursively sum the lengths of all subcomponents that have position
         * Position.AFTER.
-        * 
+        *
         * @return  Sum of the lengths.
         */
        private final double getTotalLength() {
@@ -938,7 +927,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Return the (possibly overridden) mass of component.
-        * 
+        *
         * @return The mass of the component or the given override mass.
         */
        public final double getMass() {
@@ -950,10 +939,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Return the (possibly overridden) center of gravity and mass.
-        * 
+        *
         * Returns the CG with the weight of the coordinate set to the weight of the component.
         * Both CG and mass may be separately overridden.
-        * 
+        *
         * @return The CG of the component or the given override CG.
         */
        public final Coordinate getCG() {
@@ -972,7 +961,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Return the longitudinal (around the y- or z-axis) moment of inertia of this component.
         * The moment of inertia is scaled in reference to the (possibly overridden) mass
         * and is relative to the non-overridden CG.
-        * 
+        *
         * @return    the longitudinal moment of inertia of this component.
         */
        public final double getLongitudinalInertia() {
@@ -984,7 +973,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Return the rotational (around the y- or z-axis) moment of inertia of this component.
         * The moment of inertia is scaled in reference to the (possibly overridden) mass
         * and is relative to the non-overridden CG.
-        * 
+        *
         * @return    the rotational moment of inertia of this component.
         */
        public final double getRotationalInertia() {
@@ -999,11 +988,11 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
 
        /**
         * Adds a child to the rocket component tree.  The component is added to the end
-        * of the component's child list.  This is a helper method that calls 
+        * of the component's child list.  This is a helper method that calls
         * {@link #addChild(RocketComponent,int)}.
-        * 
+        *
         * @param component  The component to add.
-        * @throws IllegalArgumentException  if the component is already part of some 
+        * @throws IllegalArgumentException  if the component is already part of some
         *                                                                       component tree.
         * @see #addChild(RocketComponent,int)
         */
@@ -1014,15 +1003,15 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Adds a child to the rocket component tree.  The component is added to 
+        * Adds a child to the rocket component tree.  The component is added to
         * the given position of the component's child list.
         * <p>
-        * This method may be overridden to enforce more strict component addition rules.  
+        * This method may be overridden to enforce more strict component addition rules.
         * The tests should be performed first and then this method called.
-        * 
+        *
         * @param component     The component to add.
         * @param index         Position to add component to.
-        * @throws IllegalArgumentException  If the component is already part of 
+        * @throws IllegalArgumentException  If the component is already part of
         *                                                                       some component tree.
         */
        public void addChild(RocketComponent component, int index) {
@@ -1048,7 +1037,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Removes a child from the rocket component tree.
-        * 
+        *
         * @param n  remove the n'th child.
         * @throws IndexOutOfBoundsException  if n is out of bounds
         */
@@ -1066,7 +1055,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Removes a child from the rocket component tree.  Does nothing if the component
         * is not present as a child.
-        * 
+        *
         * @param component             the component to remove
         * @return                              whether the component was a child
         */
@@ -1092,7 +1081,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
 
        /**
         * Move a child to another position.
-        * 
+        *
         * @param component     the component to move
         * @param index the component's new position
         * @throws IllegalArgumentException If an illegal placement was attempted.
@@ -1151,7 +1140,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Returns the position of the child in this components child list, or -1 if the
         * component is not a child of this component.
-        * 
+        *
         * @param child  The child to search for.
         * @return  Position in the list or -1 if not found.
         */
@@ -1164,7 +1153,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Get the parent component of this component.  Returns <code>null</code> if the component
         * has no parent.
-        * 
+        *
         * @return  The parent of this component or <code>null</code>.
         */
        public final RocketComponent getParent() {
@@ -1174,7 +1163,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Get the root component of the component tree.
-        * 
+        *
         * @return  The root component of the component tree.
         */
        public final RocketComponent getRoot() {
@@ -1186,9 +1175,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        }
        
        /**
-        * Returns the root Rocket component of this component tree.  Throws an 
+        * Returns the root Rocket component of this component tree.  Throws an
         * IllegalStateException if the root component is not a Rocket.
-        * 
+        *
         * @return  The root Rocket component of the component tree.
         * @throws  IllegalStateException  If the root component is not a Rocket.
         */
@@ -1205,7 +1194,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return the Stage component that this component belongs to.  Throws an
         * IllegalStateException if a Stage is not in the parentage of this component.
-        * 
+        *
         * @return      The Stage component this component belongs to.
         * @throws      IllegalStateException   if a Stage component is not in the parentage.
         */
@@ -1223,7 +1212,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Return the stage number of the stage this component belongs to.  The stages
         * are numbered from zero upwards.
-        * 
+        *
         * @return   the stage number this component belongs to.
         */
        public final int getStageNumber() {
@@ -1248,7 +1237,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Find a component with the given ID.  The component tree is searched from this component
         * down (including this component) for the ID and the corresponding component is returned,
         * or null if not found.
-        * 
+        *
         * @param idToFind  ID to search for.
         * @return    The component with the ID, or null if not found.
         */
@@ -1326,7 +1315,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Adds a ComponentChangeListener to the rocket tree.  The listener is added to the root
         * component, which must be of type Rocket (which overrides this method).  Events of all
         * subcomponents are sent to all listeners.
-        * 
+        *
         * @throws IllegalStateException - if the root component is not a Rocket
         */
        public void addComponentChangeListener(ComponentChangeListener l) {
@@ -1339,7 +1328,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * the root component, which must be of type Rocket (which overrides this method).
         * Does nothing if the root component is not a Rocket.  (The asymmetry is so
         * that listeners can always be removed just in case.)
-        * 
+        *
         * @param l  Listener to remove
         */
        public void removeComponentChangeListener(ComponentChangeListener l) {
@@ -1350,12 +1339,12 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Adds a <code>ChangeListener</code> to the rocket tree.  This is identical to 
-        * <code>addComponentChangeListener()</code> except that it uses a 
+        * Adds a <code>ChangeListener</code> to the rocket tree.  This is identical to
+        * <code>addComponentChangeListener()</code> except that it uses a
         * <code>ChangeListener</code>.  The same events are dispatched to the
-        * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass 
+        * <code>ChangeListener</code>, as <code>ComponentChangeEvent</code> is a subclass
         * of <code>ChangeEvent</code>.
-        * 
+        *
         * @throws IllegalStateException - if the root component is not a <code>Rocket</code>
         */
        @Override
@@ -1369,7 +1358,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * removeComponentChangeListener() except it uses a ChangeListener.
         * Does nothing if the root component is not a Rocket.  (The asymmetry is so
         * that listeners can always be removed just in case.)
-        * 
+        *
         * @param l  Listener to remove
         */
        @Override
@@ -1381,14 +1370,14 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        
        /**
-        * Fires a ComponentChangeEvent on the rocket structure.  The call is passed to the 
+        * Fires a ComponentChangeEvent on the rocket structure.  The call is passed to the
         * root component, which must be of type Rocket (which overrides this method).
         * Events of all subcomponents are sent to all listeners.
-        * 
-        * If the component tree root is not a Rocket, the event is ignored.  This is the 
-        * case when constructing components not in any Rocket tree.  In this case it 
+        *
+        * If the component tree root is not a Rocket, the event is ignored.  This is the
+        * case when constructing components not in any Rocket tree.  In this case it
         * would be impossible for the component to have listeners in any case.
-        *  
+        *
         * @param e  Event to send
         */
        protected void fireComponentChangeEvent(ComponentChangeEvent e) {
@@ -1405,7 +1394,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Fires a ComponentChangeEvent of the given type.  The source of the event is set to
         * this component.
-        * 
+        *
         * @param type  Type of event
         * @see #fireComponentChangeEvent(ComponentChangeEvent)
         */
@@ -1419,7 +1408,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * This is a safety check that in-place replaced components are no longer used.
         * All non-trivial methods (with the exception of methods simply getting a property)
         * should call this method before changing or computing anything.
-        * 
+        *
         * @throws      BugException    if this component has been invalidated by {@link #copyFrom(RocketComponent)}.
         */
        protected void checkState() {
@@ -1473,10 +1462,10 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * <p>
         * If an iterator iterating over only the direct children of the component is required,
         * use <code>component.getChildren().iterator()</code>.
-        * 
+        *
         * TODO: HIGH: Remove this after merges have been done
-        * 
-        * @param returnSelf boolean value specifying whether the component itself should be 
+        *
+        * @param returnSelf boolean value specifying whether the component itself should be
         *                                       returned
         * @return An iterator for the children and sub-children.
         * @deprecated Use {@link #iterator(boolean)} instead
@@ -1491,9 +1480,9 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Returns an iterator that iterates over all children and sub-children, including itself.
         * <p>
         * This method is equivalent to <code>deepIterator(true)</code>.
-        * 
+        *
         * TODO: HIGH: Remove this after merges have been done
-        * 
+        *
         * @return An iterator for this component, its children and sub-children.
         * @deprecated Use {@link #iterator()} instead
         */
@@ -1513,8 +1502,8 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * <p>
         * If an iterator iterating over only the direct children of the component is required,
         * use <code>component.getChildren().iterator()</code>.
-        * 
-        * @param returnSelf boolean value specifying whether the component itself should be 
+        *
+        * @param returnSelf boolean value specifying whether the component itself should be
         *                                       returned
         * @return An iterator for the children and sub-children.
         */
@@ -1528,7 +1517,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * Returns an iterator that iterates over this components, its children and sub-children.
         * <p>
         * This method is equivalent to <code>iterator(true)</code>.
-        * 
+        *
         * @return An iterator for this component, its children and sub-children.
         */
        @Override
@@ -1573,7 +1562,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        /**
         * Helper method to add rotationally symmetric bounds at the specified coordinates.
         * The X-axis value is <code>x</code> and the radius at the specified position is
-        * <code>r</code>. 
+        * <code>r</code>.
         */
        protected static final void addBound(Collection<Coordinate> bounds, double x, double r) {
                bounds.add(new Coordinate(x, -r, -r));
@@ -1626,7 +1615,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
         * to them have been removed (for example by firing appropriate events).  The list contains
         * all children and sub-children of the current component and the entire component
         * tree of <code>src</code>.
-        * 
+        *
         * @return      a list of components that should not be used after this call.
         */
        protected List<RocketComponent> copyFrom(RocketComponent src) {
@@ -1692,7 +1681,7 @@ public abstract class RocketComponent implements ChangeSource, Cloneable, Iterab
        
        /**
         * Private inner class to implement the Iterator.
-        * 
+        *
         * This iterator is fail-fast if the root of the structure is a Rocket.
         */
        private static class RocketComponentIterator implements Iterator<RocketComponent> {
index b984a2a5861f8de9d4c9394d29615641b200d77f..f5079bfe649d8935eadad95137e4c8ea5a045885 100644 (file)
@@ -4,7 +4,7 @@ import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.startup.Application;
 
 public class Stage extends ComponentAssembly {
-       
+
        private static final Translator trans = Application.getTranslator();
 
     @Override
@@ -12,15 +12,15 @@ public class Stage extends ComponentAssembly {
        //// Stage
         return trans.get("Stage.Stage");
     }
-       
-       
+
+
        @Override
        public boolean allowsChildren() {
                return true;
        }
-       
+
     /**
-        * Check whether the given type can be added to this component.  A Stage allows 
+        * Check whether the given type can be added to this component.  A Stage allows
         * only BodyComponents to be added.
      *
      * @param type The RocketComponent class type to add.
@@ -31,14 +31,4 @@ public class Stage extends ComponentAssembly {
     public boolean isCompatible (Class<? extends RocketComponent> type) {
         return BodyComponent.class.isAssignableFrom(type);
     }
-
-    /**
-     * Accept a visitor to this Stage in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this Stage
-     */    
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
 }
index 098183596bd976a0c0eb0552fc4ca5fd06c17a0c..1c2adb36cbdb1ccb8e9a7aa38d8ce123dd3bf294 100644 (file)
@@ -1,21 +1,18 @@
 package net.sf.openrocket.rocketcomponent;
 
+import static java.lang.Math.*;
+import static net.sf.openrocket.util.MathUtil.*;
+
+import java.util.Collection;
+
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.startup.Application;
 import net.sf.openrocket.util.Coordinate;
 import net.sf.openrocket.util.MathUtil;
 
-import java.util.Collection;
-
-import static java.lang.Math.sin;
-import static java.lang.Math.sqrt;
-import static net.sf.openrocket.util.Chars.FRAC12;
-import static net.sf.openrocket.util.Chars.FRAC34;
-import static net.sf.openrocket.util.MathUtil.pow2;
-import static net.sf.openrocket.util.MathUtil.pow3;
-
 
 public class Transition extends SymmetricComponent {
+       private static final Translator trans = Application.getTranslator();
        private static final double CLIP_PRECISION = 0.0001;
        
 
@@ -35,7 +32,7 @@ public class Transition extends SymmetricComponent {
        private double aftShoulderThickness;
        private double aftShoulderLength;
        private boolean aftShoulderCapped;
-       private static final Translator trans = Application.getTranslator();
+       
 
        // Used to cache the clip length
        private double clipLength = -1;
@@ -503,26 +500,16 @@ public class Transition extends SymmetricComponent {
                clipLength = -1;
        }
        
-    /**
-     * Accept a visitor to this Transition in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this Transition
-     */
-    @Override 
-    public void accept (final ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
-       
        /**
         * Check whether the given type can be added to this component.  Transitions allow any
         * InternalComponents to be added.
-        * 
-        * @param type  The RocketComponent class type to add.
+        *
+        * @param ctype  The RocketComponent class type to add.
         * @return      Whether such a component can be added.
         */
        @Override
-       public boolean isCompatible(Class<? extends RocketComponent> type) {
-               if (InternalComponent.class.isAssignableFrom(type))
+       public boolean isCompatible(Class<? extends RocketComponent> ctype) {
+               if (InternalComponent.class.isAssignableFrom(ctype))
                        return true;
                return false;
        }
@@ -531,7 +518,7 @@ public class Transition extends SymmetricComponent {
 
        /**
         * An enumeration listing the possible shapes of transitions.
-        * 
+        *
         * @author Sampo Niskanen <sampo.niskanen@iki.fi>
         */
        public static enum Shape {
@@ -621,20 +608,11 @@ public class Transition extends SymmetricComponent {
                                return sqrt(2 * radius * x - x * x); // radius/length * sphere
                        }
                },
+               
                //// Power series
                POWER(trans.get("Shape.Powerseries"),
-                               //// A power series nose cone has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 this is a <b>
-                               trans.get("Shape.Powerseries.descA1") + FRAC12 + 
-                               //// -power</b> or <b>parabolic</b> nose cone, for <i>k</i>=0.75 a <b>
-                               trans.get("Shape.Powerseries.descA2") + FRAC34 + 
-                               //// -power</b>, and for <i>k</i>=1 a <b>conical</b> nose cone.
-                               trans.get("Shape.Powerseries.descA3"),
-                               //// A power series transition has a profile of <i>Radius</i>&nbsp;&times;&nbsp;(<i>x</i>&nbsp;/&nbsp;<i>Length</i>)<sup><i>k</i></sup> where <i>k</i> is the shape parameter.  For <i>k</i>=0.5 the transition is <b>
-                               trans.get("Shape.Powerseries.descB1") + FRAC12 + 
-                                               //// -power</b> or <b>parabolic</b>, for <i>k</i>=0.75 a <b>
-                               trans.get("Shape.Powerseries.descB2") + FRAC34 + 
-                                               //// -power</b>, and for <i>k</i>=1 <b>conical</b>.
-                               trans.get("Shape.Powerseries.descB3"), true) {
+                               trans.get("Shape.Powerseries.desc1"),
+                               trans.get("Shape.Powerseries.desc2"), true) {
                        @Override
                        public boolean usesParameter() { // Range 0...1
                                return true;
@@ -662,6 +640,7 @@ public class Transition extends SymmetricComponent {
                        }
                        
                },
+               
                //// Parabolic series
                PARABOLIC(trans.get("Shape.Parabolicseries"),
                                ////A parabolic series nose cone has a profile of a parabola.  The shape parameter defines the segment of the parabola to utilize.  The shape parameter 1.0 produces a <b>full parabola</b> which is tangent to the body tube, 0.75 produces a <b>3/4 parabola</b>, 0.5 procudes a <b>1/2 parabola</b> and 0 produces a <b>conical</b> nose cone.
@@ -671,7 +650,7 @@ public class Transition extends SymmetricComponent {
                        
                        // In principle a parabolic transition is clippable, but the difference is
                        // negligible.
-               
+                       
                        @Override
                        public boolean usesParameter() { // Range 0...1
                                return true;
@@ -694,13 +673,13 @@ public class Transition extends SymmetricComponent {
                        }
                },
                
-
                //// Haack series
                HAACK(trans.get("Shape.Haackseries"),
                                //// The Haack series nose cones are designed to minimize drag.  The shape parameter 0 produces an <b>LD-Haack</b> or <b>Von Karman</b> nose cone, which minimizes drag for fixed length and diameter, while a value of 0.333 produces an <b>LV-Haack</b> nose cone, which minimizes drag for fixed length and volume.
                                trans.get("Shape.Haackseries.desc1"),
                                //// The Haack series <i>nose cones</i> are designed to minimize drag.  These transition shapes are their equivalents, but do not necessarily produce optimal drag for transitions.  The shape parameter 0 produces an <b>LD-Haack</b> or <b>Von Karman</b> shape, while a value of 0.333 produces an <b>LV-Haack</b> shape.            
                                trans.get("Shape.Haackseries.desc2"), true) {
+                       
                        @Override
                        public boolean usesParameter() {
                                return true;
@@ -841,7 +820,7 @@ public class Transition extends SymmetricComponent {
                 * shape parameter at the point x from the tip of the component.  It is assumed
                 * that the fore radius if zero and the aft radius is <code>radius >= 0</code>.
                 * Boattails are achieved by reversing the component.
-                * 
+                *
                 * @param x      Position from the tip of the component.
                 * @param radius Aft end radius >= 0.
                 * @param length Length of the transition >= 0.
index fb276a7c03349248e532b3a71357e1b94b6676d4..288726b1fd38c344405f0d09fe4a274074d260b2 100644 (file)
@@ -7,7 +7,7 @@ import net.sf.openrocket.util.Coordinate;
 /**
  * A set of trapezoidal fins.  The root and tip chords are perpendicular to the rocket
  * base line, while the leading and aft edges may be slanted.
- * 
+ *
  * @author Sampo Niskanen <sampo.niskanen@iki.fi>
  */
 
@@ -20,37 +20,37 @@ public class TrapezoidFinSet extends FinSet {
         *           sweep   tipChord
         *           |    |___________
         *           |   /            |
-        *           |  /             | 
+        *           |  /             |
         *           | /              |  height
         *            /               |
         * __________/________________|_____________
         *                length
         *              == rootChord
         */
-       
+
        // rootChord == length
        private double tipChord = 0;
        private double height = 0;
        private double sweep = 0;
-       
-       
+
+
        public TrapezoidFinSet() {
                this (3, 0.05, 0.05, 0.025, 0.05);
        }
-       
+
        // TODO: HIGH:  height=0 -> CP = NaN
-       public TrapezoidFinSet(int fins, double rootChord, double tipChord, double sweep, 
+       public TrapezoidFinSet(int fins, double rootChord, double tipChord, double sweep,
                        double height) {
                super();
-               
+
                this.setFinCount(fins);
                this.length = rootChord;
                this.tipChord = tipChord;
                this.sweep = sweep;
                this.height = height;
        }
-       
-       
+
+
        public void setFinShape(double rootChord, double tipChord, double sweep, double height,
                        double thickness) {
                if (this.length==rootChord && this.tipChord==tipChord && this.sweep==sweep &&
@@ -73,7 +73,7 @@ public class TrapezoidFinSet extends FinSet {
                length = Math.max(r,0);
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
+
        public double getTipChord() {
                return tipChord;
        }
@@ -99,7 +99,7 @@ public class TrapezoidFinSet extends FinSet {
                sweep = r;
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
-       
+
        /**
         * Get the sweep angle.  This is calculated from the true sweep and height, and is not
         * stored separetely.
@@ -139,7 +139,7 @@ public class TrapezoidFinSet extends FinSet {
                fireComponentChangeEvent(ComponentChangeEvent.BOTH_CHANGE);
        }
 
-       
+
 
        /**
         * Returns the geometry of a trapezoidal fin.
@@ -147,15 +147,15 @@ public class TrapezoidFinSet extends FinSet {
        @Override
        public Coordinate[] getFinPoints() {
                Coordinate[] c = new Coordinate[4];
-               
+
                c[0] = Coordinate.NUL;
                c[1] = new Coordinate(sweep,height);
                c[2] = new Coordinate(sweep+tipChord,height);
                c[3] = new Coordinate(length,0);
-               
+
                return c;
        }
-       
+
        /**
         * Returns the span of a trapezoidal fin.
         */
@@ -163,7 +163,7 @@ public class TrapezoidFinSet extends FinSet {
        public double getSpan() {
                return height;
        }
-       
+
 
        @Override
        public String getComponentName() {
@@ -171,13 +171,4 @@ public class TrapezoidFinSet extends FinSet {
                return trans.get("TrapezoidFinSet.TrapezoidFinSet");
        }
 
-    /**
-     * Accept a visitor to this TrapezoidFinSet in the component hierarchy.
-     * 
-     * @param theVisitor  the visitor that will be called back with a reference to this TrapezoidFinSet
-     */    
-    @Override
-    public void accept(ComponentVisitor theVisitor) {
-        theVisitor.visit(this);
-    }
 }
index 020d714e57abb13c7f48c5554d320520e72b637f..bb8b20f771c452493804f23888207acc372cf4b6 100644 (file)
@@ -1,7 +1,9 @@
 package net.sf.openrocket.startup;
 
 import net.sf.openrocket.database.ThrustCurveMotorSetDatabase;
+import net.sf.openrocket.l10n.ClassBasedTranslator;
 import net.sf.openrocket.l10n.DebugTranslator;
+import net.sf.openrocket.l10n.ExceptionSuppressingTranslator;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.logging.LogLevel;
@@ -18,7 +20,7 @@ public final class Application {
        private static LogHelper logger;
        private static LogLevelBufferLogger logBuffer;
        
-       private static Translator translator = new DebugTranslator();
+       private static Translator baseTranslator = new DebugTranslator(null);
        
        private static ThrustCurveMotorSetDatabase motorSetDatabase;
        
@@ -87,15 +89,18 @@ public final class Application {
         * @return      a translator.
         */
        public static Translator getTranslator() {
-               return translator;
+               Translator t = baseTranslator;
+               t = new ClassBasedTranslator(t, 1);
+               t = new ExceptionSuppressingTranslator(t);
+               return t;
        }
        
        /**
         * Set the translator used in obtaining translated strings.
         * @param translator    the translator to set.
         */
-       public static void setTranslator(Translator translator) {
-               Application.translator = translator;
+       public static void setBaseTranslator(Translator translator) {
+               Application.baseTranslator = translator;
        }
        
        
index 802bdcc737673171c6ee9188715f05cffbf299b3..f4141f9b057746b22d928ce85da1bb0376cbd05b 100644 (file)
@@ -134,20 +134,20 @@ public class Startup {
                        } else {
                                l = new Locale(split[0], split[1], split[2]);
                        }
+                       log.info("Setting custom locale " + l);
                        Locale.setDefault(l);
                }
                
                Translator t;
+               t = new ResourceBundleTranslator("l10n.messages");
                if (Locale.getDefault().getLanguage().equals("xx")) {
-                       t = new DebugTranslator();
-               } else {
-                       t = new ResourceBundleTranslator("l10n.messages");
+                       t = new DebugTranslator(t);
                }
                
                log.info("Set up translation for locale " + Locale.getDefault() +
                                ", debug.currentFile=" + t.get("debug.currentFile"));
                
-               Application.setTranslator(t);
+               Application.setBaseTranslator(t);
        }
        
        
diff --git a/src/net/sf/openrocket/util/AbstractChangeSource.java b/src/net/sf/openrocket/util/AbstractChangeSource.java
new file mode 100644 (file)
index 0000000..14109da
--- /dev/null
@@ -0,0 +1,47 @@
+package net.sf.openrocket.util;
+
+import java.util.List;
+
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.ChangeListener;
+
+import net.sf.openrocket.logging.LogHelper;
+import net.sf.openrocket.startup.Application;
+
+/**
+ * Abstract implementation of a ChangeSource.
+ * 
+ * @author Sampo Niskanen <sampo.niskanen@iki.fi>
+ */
+public abstract class AbstractChangeSource implements ChangeSource {
+       private static final LogHelper log = Application.getLogger();
+       
+       private final List<ChangeListener> listeners = new ArrayList<ChangeListener>();
+       
+       private final ChangeEvent event = new ChangeEvent(this);
+       
+       
+       @Override
+       public final void addChangeListener(ChangeListener listener) {
+               listeners.add(listener);
+               log.verbose(1, "Adding change listeners, listener count is now " + listeners.size());
+       }
+       
+       @Override
+       public final void removeChangeListener(ChangeListener listener) {
+               listeners.remove(listener);
+               log.verbose(1, "Removing change listeners, listener count is now " + listeners.size());
+       }
+       
+       
+       /**
+        * Fire a change event to all listeners.
+        */
+       protected void fireChangeEvent() {
+               ChangeListener[] array = listeners.toArray(new ChangeListener[0]);
+               
+               for (ChangeListener l : array) {
+                       l.stateChanged(event);
+               }
+       }
+}
index 215389ab4d1d37109098a133f7a4f83be41582eb..047db50c7ae3c1a65599be088552aacb9a87168b 100644 (file)
@@ -22,6 +22,7 @@ import net.sf.openrocket.arch.SystemInfo;
 import net.sf.openrocket.database.Databases;
 import net.sf.openrocket.document.Simulation;
 import net.sf.openrocket.gui.main.ExceptionHandler;
+import net.sf.openrocket.gui.print.PrintSettings;
 import net.sf.openrocket.l10n.Translator;
 import net.sf.openrocket.logging.LogHelper;
 import net.sf.openrocket.material.Material;
@@ -200,7 +201,7 @@ public class Prefs {
         */
        private static class DefaultMaterialHolder {
                private static final Translator trans = Application.getTranslator();
-
+               
                //// Elastic cord (round 2mm, 1/16 in)
                private static final Material DEFAULT_LINE_MATERIAL =
                                Databases.findMaterial(Material.Type.LINE, trans.get("Databases.materials.Elasticcordround2mm"),
@@ -312,6 +313,47 @@ public class Prefs {
                storeVersion();
        }
        
+       
+       /**
+        * Retrieve an enum value from the user preferences.
+        * 
+        * @param <T>   the enum type
+        * @param key   the key
+        * @param def   the default value, cannot be null
+        * @return              the value in the preferences, or the default value
+        */
+       public static <T extends Enum<T>> T getEnum(String key, T def) {
+               if (def == null) {
+                       throw new BugException("Default value cannot be null");
+               }
+               
+               String value = getString(key, null);
+               if (value == null) {
+                       return def;
+               }
+               
+               try {
+                       return Enum.valueOf(def.getDeclaringClass(), value);
+               } catch (IllegalArgumentException e) {
+                       return def;
+               }
+       }
+       
+       /**
+        * Store an enum value to the user preferences.
+        * 
+        * @param key           the key
+        * @param value         the value to store, or null to remove the value
+        */
+       public static void putEnum(String key, Enum<?> value) {
+               if (value == null) {
+                       putString(key, null);
+               } else {
+                       putString(key, value.name());
+               }
+       }
+       
+       
        /**
         * Return a boolean preference.
         * 
@@ -449,6 +491,26 @@ public class Prefs {
                if (color == null)
                        return Color.BLACK;
                
+               Color clr = parseColor(color);
+               if (clr != null) {
+                       return clr;
+               } else {
+                       return Color.BLACK;
+               }
+       }
+       
+       public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
+               if (color == null)
+                       return;
+               set("componentColors", c, stringifyColor(color));
+       }
+       
+       
+       private static Color parseColor(String color) {
+               if (color == null) {
+                       return null;
+               }
+               
                String[] rgb = color.split(",");
                if (rgb.length == 3) {
                        try {
@@ -459,17 +521,17 @@ public class Prefs {
                        } catch (NumberFormatException ignore) {
                        }
                }
-               
-               return Color.BLACK;
+               return null;
        }
        
-       public static void setDefaultColor(Class<? extends RocketComponent> c, Color color) {
-               if (color == null)
-                       return;
+       
+       private static String stringifyColor(Color color) {
                String string = color.getRed() + "," + color.getGreen() + "," + color.getBlue();
-               set("componentColors", c, string);
+               return string;
        }
        
+       
+
        public static Color getMotorBorderColor() {
                // TODO: MEDIUM:  Motor color (settable?)
                return new Color(0, 0, 0, 200);
@@ -629,6 +691,35 @@ public class Prefs {
        }
        
        
+       ////  Printing
+       
+       public static PrintSettings getPrintSettings() {
+               PrintSettings settings = new PrintSettings();
+               Color c;
+               
+               c = parseColor(getString("print.template.fillColor", null));
+               if (c != null) {
+                       settings.setTemplateFillColor(c);
+               }
+               
+               c = parseColor(getString("print.template.borderColor", null));
+               if (c != null) {
+                       settings.setTemplateBorderColor(c);
+               }
+               
+               settings.setPaperSize(getEnum("print.paper.size", settings.getPaperSize()));
+               settings.setPaperOrientation(getEnum("print.paper.orientation", settings.getPaperOrientation()));
+               
+               return settings;
+       }
+       
+       public static void setPrintSettings(PrintSettings settings) {
+               putString("print.template.fillColor", stringifyColor(settings.getTemplateFillColor()));
+               putString("print.template.borderColor", stringifyColor(settings.getTemplateBorderColor()));
+               putEnum("print.paper.size", settings.getPaperSize());
+               putEnum("print.paper.orientation", settings.getPaperOrientation());
+       }
+       
        ////  Background flight data computation
        
        public static boolean computeFlightInBackground() {
diff --git a/test/net/sf/openrocket/gui/print/TestPaperSize.java b/test/net/sf/openrocket/gui/print/TestPaperSize.java
new file mode 100644 (file)
index 0000000..82677cd
--- /dev/null
@@ -0,0 +1,36 @@
+package net.sf.openrocket.gui.print;
+
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+public class TestPaperSize {
+       
+       @Test
+       public void testGetDefaultForCountry() {
+               assertEquals(PaperSize.LETTER, PaperSize.getDefaultForCountry("US"));
+               assertEquals(PaperSize.LETTER, PaperSize.getDefaultForCountry("cA"));
+               assertEquals(PaperSize.LETTER, PaperSize.getDefaultForCountry("mx"));
+               
+               assertEquals(PaperSize.A4, PaperSize.getDefaultForCountry("FI"));
+               assertEquals(PaperSize.A4, PaperSize.getDefaultForCountry("xy"));
+               
+               assertNull(PaperSize.getDefaultForCountry("FIN"));
+               assertNull(PaperSize.getDefaultForCountry("a"));
+               assertNull(PaperSize.getDefaultForCountry("A4"));
+               assertNull(PaperSize.getDefaultForCountry(null));
+       }
+       
+       @Test
+       public void testGetSizeFromString() {
+               assertEquals(PaperSize.LETTER, PaperSize.getSizeFromString("Letter"));
+               assertEquals(PaperSize.LEGAL, PaperSize.getSizeFromString("  legal\t"));
+               assertEquals(PaperSize.A4, PaperSize.getSizeFromString("  A4\n"));
+               assertEquals(PaperSize.A3, PaperSize.getSizeFromString("A3"));
+               
+               assertNull(PaperSize.getSizeFromString("#A4"));
+               assertNull(PaperSize.getSizeFromString(""));
+               assertNull(PaperSize.getSizeFromString(null));
+       }
+       
+}
diff --git a/test/net/sf/openrocket/l10n/TestClassBasedTranslator.java b/test/net/sf/openrocket/l10n/TestClassBasedTranslator.java
new file mode 100644 (file)
index 0000000..94909b0
--- /dev/null
@@ -0,0 +1,79 @@
+package net.sf.openrocket.l10n;
+
+import static org.junit.Assert.*;
+
+import java.util.MissingResourceException;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.auto.Mock;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+
+@RunWith(JMock.class)
+public class TestClassBasedTranslator {
+       Mockery context = new JUnit4Mockery();
+       
+       @Mock
+       Translator translator;
+       
+       @Test
+       public void testClassName() {
+               ClassBasedTranslator cbt = new ClassBasedTranslator(null, 0);
+               assertEquals("TestClassBasedTranslator", cbt.getClassName());
+               
+               cbt = new ClassBasedTranslator(null, "foobar");
+               assertEquals("foobar", cbt.getClassName());
+       }
+       
+       @Test
+       public void testGetWithClassName() {
+               ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
+               
+               // @formatter:off
+               context.checking(new Expectations() {{
+                               oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(returnValue("foobar")); 
+               }});
+               // @formatter:on
+               
+               assertEquals("foobar", cbt.get("fake.key"));
+       }
+       
+       
+       @Test
+       public void testGetWithoutClassName() {
+               ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
+               
+               // @formatter:off
+               context.checking(new Expectations() {{
+                       oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+                       oneOf(translator).get("fake.key"); will(returnValue("barbaz")); 
+               }});
+               // @formatter:on
+               
+               assertEquals("barbaz", cbt.get("fake.key"));
+       }
+       
+       
+       @Test
+       public void testMissing() {
+               ClassBasedTranslator cbt = new ClassBasedTranslator(translator, 0);
+               
+               // @formatter:off
+               context.checking(new Expectations() {{
+                       oneOf(translator).get("TestClassBasedTranslator.fake.key"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+                       oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+               }});
+               // @formatter:on
+               
+               try {
+                       fail("Returned: " + cbt.get("fake.key"));
+               } catch (MissingResourceException e) {
+                       assertEquals("Neither key 'TestClassBasedTranslator.fake.key' nor 'fake.key' could be found", e.getMessage());
+               }
+               
+       }
+}
diff --git a/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java b/test/net/sf/openrocket/l10n/TestExceptionSuppressingTranslator.java
new file mode 100644 (file)
index 0000000..719ee39
--- /dev/null
@@ -0,0 +1,64 @@
+package net.sf.openrocket.l10n;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.MissingResourceException;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.jmock.auto.Mock;
+import org.jmock.integration.junit4.JMock;
+import org.jmock.integration.junit4.JUnit4Mockery;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@RunWith(JMock.class)
+public class TestExceptionSuppressingTranslator {
+       Mockery context = new JUnit4Mockery();
+       
+       @Mock
+       Translator translator;
+       
+       @Test
+       public void testSuccessful() {
+               ExceptionSuppressingTranslator est = new ExceptionSuppressingTranslator(translator);
+               
+               // @formatter:off
+               context.checking(new Expectations() {{
+                               oneOf(translator).get("fake.key"); will(returnValue("foobar")); 
+               }});
+               // @formatter:on
+               
+               assertEquals("foobar", est.get("fake.key"));
+       }
+       
+       
+       @Test
+       public void testFailure() {
+               ExceptionSuppressingTranslator est = new ExceptionSuppressingTranslator(translator);
+               
+               assertEquals("Prerequisite failed", 0, ExceptionSuppressingTranslator.failures);
+               
+               // @formatter:off
+               context.checking(new Expectations() {{
+                       oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+                       oneOf(translator).get("fake.key"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+                       oneOf(translator).get("fake.key2"); will(throwException(new MissingResourceException("a", "b", "c"))); 
+               }});
+               // @formatter:on
+               
+               // Test first failure
+               assertEquals("fake.key", est.get("fake.key"));
+               assertEquals(1, ExceptionSuppressingTranslator.failures);
+               
+               // Test second failure
+               assertEquals("fake.key", est.get("fake.key"));
+               assertEquals(1, ExceptionSuppressingTranslator.failures);
+               
+               // Test failure with other key
+               assertEquals("fake.key2", est.get("fake.key2"));
+               assertEquals(2, ExceptionSuppressingTranslator.failures);
+       }
+       
+
+}
index ea686483385befd13350f799d0d41bee72fccb64..6bede2f422ef7c58428442a1fe91731b75020f85 100644 (file)
@@ -80,15 +80,24 @@ header("Content-type: text/plain");
 $version = $_GET["version"];
 $updates = "";
 
-$unstable = "1.1.3";
+$unstable = "1.1.5";
 $stable = "1.0.0";
 
-if (preg_match("/^1\.1\.[12]/", $version)) {
+if (preg_match("/^1\.1\.4/", $version)) {
   $updates = "Version: " . $unstable . "\n" .
+    "5: Fixes to printing system";
+} else if (preg_match("/^1\.1\.3/", $version)) {
+  $updates = "Version: " . $unstable . "\n" .
+    "5: Initial printing support\n" .
+    "4: Bug fixes\n";
+} else if (preg_match("/^1\.1\.[12]/", $version)) {
+  $updates = "Version: " . $unstable . "\n" .
+    "6: Initial printing support\n" .
     "5: Initial drag-and-drop support\n" .
     "4: Bug fixes\n";
 } else if (preg_match("/^1\.1\.0/", $version)) {
   $updates = "Version: " . $unstable . "\n" .
+    "6: Initial printing support\n" .
     "6: Enhanced motor selection\n" .
     "5: Rewritten simulation code\n" .
     "5: Drag-and-drop support\n" .
index ddb2b5be3bead929bc9e683dde699f41782978c6..e116453c7e5d77ca270ad28412246e4af44baa42 100644 (file)
   <div class="content">
 <div class="news">
       <h2>Recent news:</h2>
+  <p><span class="date">10.6.2011:</span> Version 1.1.5 is
+    <a href="download.html">released</a>!</p>
+  <p>This release includes updates to the printing system which fixes
+  printing on Windows.</p>
+  <p><span class="date">5.3.2011:</span> Version 1.1.4 is
+    <a href="download.html">released</a>!</p>
+  <p>This release includes initial printing support, thanks to Doug
+    Pedrick.  Printing still has a few issues on some platforms.
+    Various bugs have also been fixed.</p>
   <p><span class="date">6.10.2010:</span> Version 1.1.3 is
     <a href="download.html">released</a>!</p>
   <p>This release includes support for moving and copying components
     startup times than previous releases.</p>
   <p>Simulation listeners written for older versions of OpenRocket
     are not compatible with this release.</p>
-  <p><span class="date">21.3.2010:</span> Version 1.1.0 is
-    <a href="download.html">released</a>!</p>
-  <p>This release includes the first support for loading RockSim
-    rocket design files (.RKT), thanks to contributions by Doug
-    Pedrick.  It's also the kick-off of the 1.1 development branch of
-    OpenRocket.</p>
 </div>
     <div class="contentholder">
       <h2>Ready packages</h2>
         <a href="http://sourceforge.net/donate/index.php?group_id=260357"><img src="project-support.jpg" width="88" height="32" alt="Support This Project" /></a>
       </div>
         <div class="downloadbox">
-    <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.3/OpenRocket-1.1.3.jar/download">
+    <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.5/OpenRocket-1.1.5.jar/download">
       <strong>Download now!</strong>
-      <span>OpenRocket-1.1.3.jar</span>
+      <span>OpenRocket-1.1.5.jar</span>
     </a>
     <span class="alternative">
-      <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.3/ReleaseNotes/view">Release notes</a> |
-            <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.3/OpenRocket-1.1.3-src.zip/download">Source code</a>
+      <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.5/ReleaseNotes/view">Release notes</a> |
+            <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.5/OpenRocket-1.1.5-src.zip/download">Source code</a>
     </span>
   </div>
       <h3>Stable release</h3>
        Windows) by double-clicking the package icon.  No installation is
        required.</p>
       <p>From the command line OpenRocket can be started by
-      <span class="command">java -jar OpenRocket-1.1.3.jar</span></p>
+      <span class="command">java -jar OpenRocket-1.1.5.jar</span></p>
        
     </div>
     <div class="clear"></div>
index aa10d745a8342bac831c6d0a04ade368a986e602..5041a2cdc4d5311591903bef96f94663704dbef9 100644 (file)
     <h2>Introduction</h2>
     <div class="rightpane">
         <div class="downloadbox">
-    <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.3/OpenRocket-1.1.3.jar/download">
+    <a class="main" href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.5/OpenRocket-1.1.5.jar/download">
       <strong>Download now!</strong>
-      <span>OpenRocket-1.1.3.jar</span>
+      <span>OpenRocket-1.1.5.jar</span>
     </a>
     <span class="alternative">
-      <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.3/ReleaseNotes/view">Release notes</a> |
+      <a href="https://sourceforge.net/projects/openrocket/files/openrocket/1.1.5/ReleaseNotes/view">Release notes</a> |
             <a href="download.html">Other versions</a>
     </span>
   </div>
     <div class="clear"></div>
 <div class="news">
       <h2>News</h2>
+  <p><span class="date">10.6.2011:</span> Version 1.1.5 is
+    <a href="download.html">released</a>!</p>
+  <p>This release includes updates to the printing system which fixes
+  printing on Windows.</p>
+  <p><span class="date">5.3.2011:</span> Version 1.1.4 is
+    <a href="download.html">released</a>!</p>
+  <p>This release includes initial printing support, thanks to Doug
+    Pedrick.  Printing still has a few issues on some platforms.
+    Various bugs have also been fixed.</p>
   <p><span class="date">6.10.2010:</span> Version 1.1.3 is
     <a href="download.html">released</a>!</p>
   <p>This release includes support for moving and copying components
index c527097f46d88b72833574fa909e384734b95e72..535ef97d360474040966c00c25c82112e2c90cdc 100644 (file)
Binary files a/web/html/menubg.png and b/web/html/menubg.png differ
index 8fd48dea324333f44d93f8f76881ac133e41ff6f..b9bf0f6bc12b0bd48fe3db876806424807133c6d 100644 (file)
@@ -1,5 +1,5 @@
 <set stableversion="1.0.0">
-<set developmentversion="1.1.3">
+<set developmentversion="1.1.5">
 <set version="${developmentversion}">
 
 <def name="downloadbox">
index 03938a0d666045fe7742c4fa4aaa4988e32ca0cf..093d6bd10d85e8e092b009a86f282ea7604a9f24 100644 (file)
@@ -9,6 +9,19 @@
 
   <!--- Remember to move the position of "onlyrecent" below! --->
 
+  <p><span class="date">10.6.2011:</span> Version 1.1.5 is 
+    <a href="download.html">released</a>!</p>
+
+  <p>This release includes updates to the printing system which fixes
+  printing on Windows.</p>
+
+  <p><span class="date">5.3.2011:</span> Version 1.1.4 is 
+    <a href="download.html">released</a>!</p>
+
+  <p>This release includes initial printing support, thanks to Doug
+    Pedrick.  Printing still has a few issues on some platforms.
+    Various bugs have also been fixed.</p>
+
   <p><span class="date">6.10.2010:</span> Version 1.1.3 is 
     <a href="download.html">released</a>!</p>
 
   <p>Simulation listeners written for older versions of OpenRocket
     are not compatible with this release.</p>
 
+
+  <if not onlyrecent><!--- Older items not shown on download page:  --->
+
+
   <p><span class="date">21.3.2010:</span> Version 1.1.0 is 
     <a href="download.html">released</a>!</p>
 
     Pedrick.  It's also the kick-off of the 1.1 development branch of
     OpenRocket.</p>
 
-
-  <if not onlyrecent><!--- Older items not shown on download page:  --->
-
-
   <p><span class="date">10.3.2010:</span> Version 1.0.0 is 
     <a href="download.html">released</a>!</p>