1 package net.sf.openrocket.gui.dialogs.preferences;
3 import java.awt.Dialog;
4 import java.awt.Window;
5 import java.awt.event.ActionEvent;
6 import java.awt.event.ActionListener;
7 import java.awt.event.WindowAdapter;
8 import java.awt.event.WindowEvent;
10 import java.util.ArrayList;
11 import java.util.List;
13 import javax.swing.AbstractListModel;
14 import javax.swing.ComboBoxModel;
15 import javax.swing.JButton;
16 import javax.swing.JCheckBox;
17 import javax.swing.JComboBox;
18 import javax.swing.JDialog;
19 import javax.swing.JFileChooser;
20 import javax.swing.JLabel;
21 import javax.swing.JOptionPane;
22 import javax.swing.JPanel;
23 import javax.swing.JProgressBar;
24 import javax.swing.JTabbedPane;
25 import javax.swing.JTextField;
26 import javax.swing.Timer;
27 import javax.swing.event.DocumentEvent;
28 import javax.swing.event.DocumentListener;
30 import net.miginfocom.swing.MigLayout;
31 import net.sf.openrocket.communication.UpdateInfo;
32 import net.sf.openrocket.communication.UpdateInfoRetriever;
33 import net.sf.openrocket.gui.components.DescriptionArea;
34 import net.sf.openrocket.gui.components.StyledLabel;
35 import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
36 import net.sf.openrocket.gui.main.SimpleFileFilter;
37 import net.sf.openrocket.l10n.Translator;
38 import net.sf.openrocket.logging.LogHelper;
39 import net.sf.openrocket.startup.Application;
40 import net.sf.openrocket.unit.Unit;
41 import net.sf.openrocket.unit.UnitGroup;
42 import net.sf.openrocket.util.GUIUtil;
43 import net.sf.openrocket.util.Prefs;
45 public class PreferencesDialog extends JDialog {
46 private static final LogHelper log = Application.getLogger();
48 private final List<DefaultUnitSelector> unitSelectors = new ArrayList<DefaultUnitSelector>();
50 private File defaultDirectory = null;
51 private static final Translator trans = Application.getTranslator();
53 private PreferencesDialog() {
55 super((Window) null, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL);
57 JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]", "[grow][]"));
59 JTabbedPane tabbedPane = new JTabbedPane();
60 panel.add(tabbedPane, "grow, wrap");
62 //// Units and Default units
63 tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(),
64 trans.get("pref.dlg.tab.Defaultunits"));
65 //// Materials and Custom materials
66 tabbedPane.addTab(trans.get("pref.dlg.tab.Materials"), null, new MaterialEditPanel(),
67 trans.get("pref.dlg.tab.Custommaterials"));
68 //// Options and Miscellaneous options
69 tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(),
70 trans.get("pref.dlg.tab.Miscellaneousoptions"));
73 JButton close = new JButton(trans.get("dlg.but.close"));
74 close.addActionListener(new ActionListener() {
76 public void actionPerformed(ActionEvent arg0) {
77 PreferencesDialog.this.setVisible(false);
78 PreferencesDialog.this.dispose();
81 panel.add(close, "span, right, tag close");
83 this.setContentPane(panel);
85 this.setLocationRelativeTo(null);
87 this.addWindowListener(new WindowAdapter() {
89 public void windowClosed(WindowEvent e) {
90 Prefs.storeDefaultUnits();
94 GUIUtil.setDisposableDialogOptions(this, close);
98 private JPanel optionsPane() {
99 JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n"));
101 //// Position to insert new body components:
102 panel.add(new JLabel(trans.get("pref.dlg.lbl.Positiontoinsert")), "gapright para");
103 panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
105 //// Insert in middle
107 trans.get("pref.dlg.PrefChoiseSelector1"),
108 trans.get("pref.dlg.PrefChoiseSelector2"),
109 trans.get("pref.dlg.PrefChoiseSelector3"))), "wrap para, growx, sg combos");
111 //// Confirm deletion of simulations:
112 panel.add(new JLabel(trans.get("pref.dlg.lbl.Confirmdeletion")));
113 panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
116 trans.get("pref.dlg.PrefBooleanSelector1"),
117 trans.get("pref.dlg.PrefBooleanSelector2"), true)), "wrap 40lp, growx, sg combos");
119 //// User-defined thrust curves:
120 panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap");
121 final JTextField field = new JTextField();
122 List<File> files = Prefs.getUserThrustCurveFiles();
124 for (File file : files) {
125 if (str.length() > 0) {
128 str += file.getAbsolutePath();
131 field.getDocument().addDocumentListener(new DocumentListener() {
133 public void removeUpdate(DocumentEvent e) {
138 public void insertUpdate(DocumentEvent e) {
143 public void changedUpdate(DocumentEvent e) {
147 private void changed() {
148 String text = field.getText();
149 List<File> list = new ArrayList<File>();
150 for (String s : text.split(";")) {
152 if (s.length() > 0) {
153 list.add(new File(s));
156 Prefs.setUserThrustCurveFiles(list);
159 panel.add(field, "w 100px, gapright unrel, spanx, growx, split");
162 JButton button = new JButton(trans.get("pref.dlg.but.add"));
163 button.addActionListener(new ActionListener() {
165 public void actionPerformed(ActionEvent e) {
166 JFileChooser chooser = new JFileChooser();
167 SimpleFileFilter filter =
168 new SimpleFileFilter(
169 //// All thrust curve files (*.eng; *.rse; *.zip; directories)
170 trans.get("pref.dlg.Allthrustcurvefiles"),
171 true, "eng", "rse", "zip");
172 chooser.addChoosableFileFilter(filter);
173 //// RASP motor files (*.eng)
174 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RASPfiles"),
176 //// RockSim engine files (*.rse)
177 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RockSimfiles"),
179 //// ZIP archives (*.zip)
180 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.ZIParchives"),
182 chooser.setFileFilter(filter);
183 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
184 if (defaultDirectory != null) {
185 chooser.setCurrentDirectory(defaultDirectory);
189 int returnVal = chooser.showDialog(PreferencesDialog.this, trans.get("pref.dlg.Add"));
190 if (returnVal == JFileChooser.APPROVE_OPTION) {
191 log.user("Adding user thrust curve: " + chooser.getSelectedFile());
192 defaultDirectory = chooser.getCurrentDirectory();
193 String text = field.getText().trim();
194 if (text.length() > 0) {
197 text += chooser.getSelectedFile().getAbsolutePath();
202 panel.add(button, "gapright unrel");
205 button = new JButton(trans.get("pref.dlg.but.reset"));
207 button.addActionListener(new ActionListener() {
209 public void actionPerformed(ActionEvent e) {
210 // First one sets to the default, but does not un-set the pref
211 field.setText(Prefs.getDefaultUserThrustCurveFile().getAbsolutePath());
212 Prefs.setUserThrustCurveFiles(null);
215 panel.add(button, "wrap");
217 //// Add directories, RASP motor files (*.eng), RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external thrust curves. Changes will take effect the next time you start OpenRocket.
218 DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false);
219 desc.setBackground(getBackground());
220 panel.add(desc, "spanx, growx, wrap 40lp");
224 //// Check for software updates at startup
225 final JCheckBox softwareUpdateBox =
226 new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
227 softwareUpdateBox.setSelected(Prefs.getCheckUpdates());
228 softwareUpdateBox.addActionListener(new ActionListener() {
230 public void actionPerformed(ActionEvent e) {
231 Prefs.setCheckUpdates(softwareUpdateBox.isSelected());
234 panel.add(softwareUpdateBox);
236 //// Check now button
237 button = new JButton(trans.get("pref.dlg.but.checknow"));
238 //// Check for software updates now
239 button.setToolTipText(trans.get("pref.dlg.ttip.Checkupdatesnow"));
240 button.addActionListener(new ActionListener() {
242 public void actionPerformed(ActionEvent e) {
246 panel.add(button, "right, wrap");
252 private JPanel unitsPane() {
253 JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
256 //// Select your preferred units:
257 panel.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph");
260 //// Rocket dimensions:
261 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
262 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
263 panel.add(combo, "sizegroup boxes");
266 panel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
267 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
268 panel.add(combo, "sizegroup boxes, wrap");
271 //// Motor dimensions:
272 panel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
273 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
274 panel.add(combo, "sizegroup boxes");
276 //// Surface density:
277 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
278 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
279 panel.add(combo, "sizegroup boxes, wrap");
283 panel.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
284 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
285 panel.add(combo, "sizegroup boxes");
288 panel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
289 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
290 panel.add(combo, "sizegroup boxes, wrap");
294 panel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
295 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
296 panel.add(combo, "sizegroup boxes");
298 //// Surface roughness:
299 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
300 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
301 panel.add(combo, "sizegroup boxes, wrap");
305 panel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
306 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
307 panel.add(combo, "sizegroup boxes");
310 panel.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
311 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
312 panel.add(combo, "sizegroup boxes, wrap");
316 panel.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
317 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
318 panel.add(combo, "sizegroup boxes");
321 panel.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
322 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
323 panel.add(combo, "sizegroup boxes, wrap");
327 panel.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
328 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
329 panel.add(combo, "sizegroup boxes");
332 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
333 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
334 panel.add(combo, "sizegroup boxes, wrap");
338 panel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
339 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
340 panel.add(combo, "sizegroup boxes");
343 panel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
344 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
345 panel.add(combo, "sizegroup boxes, wrap");
348 //// Moment of inertia:
349 panel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
350 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA));
351 panel.add(combo, "sizegroup boxes");
354 panel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
355 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
356 panel.add(combo, "sizegroup boxes, wrap");
359 panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
360 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
361 panel.add(combo, "sizegroup boxes, wrap para");
365 //// Default metric button
366 JButton button = new JButton(trans.get("pref.dlg.but.defaultmetric"));
367 button.addActionListener(new ActionListener() {
369 public void actionPerformed(ActionEvent e) {
370 UnitGroup.setDefaultMetricUnits();
371 for (DefaultUnitSelector s : unitSelectors)
375 panel.add(button, "spanx, split 2, grow");
377 //// Default imperial button
378 button = new JButton(trans.get("pref.dlg.but.defaultimperial"));
379 button.addActionListener(new ActionListener() {
381 public void actionPerformed(ActionEvent e) {
382 UnitGroup.setDefaultImperialUnits();
383 for (DefaultUnitSelector s : unitSelectors)
387 panel.add(button, "grow, wrap para");
389 //// The effects will take place the next time you open a window.
390 panel.add(new StyledLabel(
391 trans.get("pref.dlg.lbl.effect1"), -2),
402 private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
404 private final UnitGroup group;
406 public DefaultUnitSelector(UnitGroup group) {
408 unitSelectors.add(this);
412 public Object getSelectedItem() {
413 return group.getDefaultUnit();
417 public void setSelectedItem(Object item) {
419 // Clear selection - huh?
422 if (!(item instanceof Unit)) {
423 throw new IllegalArgumentException("Illegal argument " + item);
425 group.setDefaultUnit(group.getUnitIndex((Unit) item));
429 public Object getElementAt(int index) {
430 return group.getUnit(index);
434 public int getSize() {
435 return group.getUnitCount();
439 public void fireChange() {
440 this.fireContentsChanged(this, 0, this.getSize());
446 private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
447 private final String preference;
448 private final String[] descriptions;
450 public PrefChoiseSelector(String preference, String... descriptions) {
451 this.preference = preference;
452 this.descriptions = descriptions;
456 public Object getSelectedItem() {
457 return descriptions[Prefs.getChoise(preference, descriptions.length, 0)];
461 public void setSelectedItem(Object item) {
463 // Clear selection - huh?
466 if (!(item instanceof String)) {
467 throw new IllegalArgumentException("Illegal argument " + item);
470 for (index = 0; index < descriptions.length; index++) {
471 if (((String) item).equalsIgnoreCase(descriptions[index]))
474 if (index >= descriptions.length) {
475 throw new IllegalArgumentException("Illegal argument " + item);
478 Prefs.putChoise(preference, index);
482 public Object getElementAt(int index) {
483 return descriptions[index];
487 public int getSize() {
488 return descriptions.length;
493 private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
494 private final String preference;
495 private final String trueDesc, falseDesc;
496 private final boolean def;
498 public PrefBooleanSelector(String preference, String falseDescription,
499 String trueDescription, boolean defaultState) {
500 this.preference = preference;
501 this.trueDesc = trueDescription;
502 this.falseDesc = falseDescription;
503 this.def = defaultState;
507 public Object getSelectedItem() {
508 if (Prefs.NODE.getBoolean(preference, def)) {
516 public void setSelectedItem(Object item) {
518 // Clear selection - huh?
521 if (!(item instanceof String)) {
522 throw new IllegalArgumentException("Illegal argument " + item);
525 if (trueDesc.equals(item)) {
526 Prefs.NODE.putBoolean(preference, true);
527 } else if (falseDesc.equals(item)) {
528 Prefs.NODE.putBoolean(preference, false);
530 throw new IllegalArgumentException("Illegal argument " + item);
535 public Object getElementAt(int index) {
538 return def ? trueDesc : falseDesc;
541 return def ? falseDesc : trueDesc;
544 throw new IndexOutOfBoundsException("Boolean asked for index=" + index);
549 public int getSize() {
555 private void checkForUpdates() {
556 final UpdateInfoRetriever retriever = new UpdateInfoRetriever();
561 final JDialog dialog = new JDialog(this, ModalityType.APPLICATION_MODAL);
562 JPanel panel = new JPanel(new MigLayout());
564 //// Checking for updates...
565 panel.add(new JLabel(trans.get("pref.dlg.lbl.Checkingupdates")), "wrap");
567 JProgressBar bar = new JProgressBar();
568 bar.setIndeterminate(true);
569 panel.add(bar, "growx, wrap para");
572 JButton cancel = new JButton(trans.get("dlg.but.cancel"));
573 cancel.addActionListener(new ActionListener() {
575 public void actionPerformed(ActionEvent e) {
579 panel.add(cancel, "right");
582 GUIUtil.setDisposableDialogOptions(dialog, cancel);
585 // Timer to monitor progress
586 final Timer timer = new Timer(100, null);
587 final long startTime = System.currentTimeMillis();
589 ActionListener listener = new ActionListener() {
591 public void actionPerformed(ActionEvent e) {
592 if (!retriever.isRunning() || startTime + 10000 < System.currentTimeMillis()) {
598 timer.addActionListener(listener);
603 dialog.setVisible(true);
607 UpdateInfo info = retriever.getUpdateInfo();
609 JOptionPane.showMessageDialog(this,
610 //// An error occurred while communicating with the server.
611 trans.get("pref.dlg.lbl.msg1"),
612 //// Unable to retrieve update information
613 trans.get("pref.dlg.lbl.msg2"), JOptionPane.WARNING_MESSAGE, null);
614 } else if (info.getLatestVersion() == null ||
615 info.getLatestVersion().equals("") ||
616 Prefs.getVersion().equalsIgnoreCase(info.getLatestVersion())) {
617 JOptionPane.showMessageDialog(this,
618 //// You are running the latest version of OpenRocket.
619 trans.get("pref.dlg.lbl.msg3"),
620 //// No updates available
621 trans.get("pref.dlg.lbl.msg4"), JOptionPane.INFORMATION_MESSAGE, null);
623 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
624 infoDialog.setVisible(true);
625 if (infoDialog.isReminderSelected()) {
626 Prefs.putString(Prefs.LAST_UPDATE, "");
628 Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
635 //////// Singleton implementation ////////
637 private static PreferencesDialog dialog = null;
639 public static void showPreferences() {
640 if (dialog != null) {
643 dialog = new PreferencesDialog();
644 dialog.setVisible(true);