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.Collections;
12 import java.util.List;
13 import java.util.Locale;
15 import javax.swing.AbstractListModel;
16 import javax.swing.ComboBoxModel;
17 import javax.swing.JButton;
18 import javax.swing.JCheckBox;
19 import javax.swing.JComboBox;
20 import javax.swing.JDialog;
21 import javax.swing.JFileChooser;
22 import javax.swing.JLabel;
23 import javax.swing.JOptionPane;
24 import javax.swing.JPanel;
25 import javax.swing.JProgressBar;
26 import javax.swing.JTabbedPane;
27 import javax.swing.JTextField;
28 import javax.swing.Timer;
29 import javax.swing.event.DocumentEvent;
30 import javax.swing.event.DocumentListener;
32 import net.miginfocom.swing.MigLayout;
33 import net.sf.openrocket.communication.UpdateInfo;
34 import net.sf.openrocket.communication.UpdateInfoRetriever;
35 import net.sf.openrocket.gui.components.DescriptionArea;
36 import net.sf.openrocket.gui.components.StyledLabel;
37 import net.sf.openrocket.gui.components.StyledLabel.Style;
38 import net.sf.openrocket.gui.dialogs.UpdateInfoDialog;
39 import net.sf.openrocket.l10n.Translator;
40 import net.sf.openrocket.logging.LogHelper;
41 import net.sf.openrocket.startup.Application;
42 import net.sf.openrocket.unit.Unit;
43 import net.sf.openrocket.unit.UnitGroup;
44 import net.sf.openrocket.util.GUIUtil;
45 import net.sf.openrocket.util.Named;
46 import net.sf.openrocket.util.Prefs;
47 import net.sf.openrocket.util.SimpleFileFilter;
48 import net.sf.openrocket.util.Utils;
51 public class PreferencesDialog extends JDialog {
52 private static final LogHelper log = Application.getLogger();
54 private final List<DefaultUnitSelector> unitSelectors = new ArrayList<DefaultUnitSelector>();
56 private File defaultDirectory = null;
57 private static final Translator trans = Application.getTranslator();
59 private PreferencesDialog(Window parent) {
61 super(parent, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL);
63 JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]", "[grow][]"));
65 JTabbedPane tabbedPane = new JTabbedPane();
66 panel.add(tabbedPane, "grow, wrap");
68 //// Units and Default units
69 tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(),
70 trans.get("pref.dlg.tab.Defaultunits"));
71 //// Materials and Custom materials
72 tabbedPane.addTab(trans.get("pref.dlg.tab.Materials"), null, new MaterialEditPanel(),
73 trans.get("pref.dlg.tab.Custommaterials"));
74 //// Options and Miscellaneous options
75 tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(),
76 trans.get("pref.dlg.tab.Miscellaneousoptions"));
79 JButton close = new JButton(trans.get("dlg.but.close"));
80 close.addActionListener(new ActionListener() {
82 public void actionPerformed(ActionEvent arg0) {
83 PreferencesDialog.this.setVisible(false);
84 PreferencesDialog.this.dispose();
87 panel.add(close, "span, right, tag close");
89 this.setContentPane(panel);
91 this.setLocationRelativeTo(null);
93 this.addWindowListener(new WindowAdapter() {
95 public void windowClosed(WindowEvent e) {
96 Prefs.storeDefaultUnits();
100 GUIUtil.setDisposableDialogOptions(this, close);
104 private JPanel optionsPane() {
105 JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n"));
108 //// Language selector
109 Locale userLocale = Prefs.getUserLocale();
110 List<Named<Locale>> locales = new ArrayList<Named<Locale>>();
111 for (Locale l : Prefs.getSupportedLocales()) {
112 locales.add(new Named<Locale>(l, l.getDisplayLanguage()));
114 Collections.sort(locales);
115 locales.add(0, new Named<Locale>(null, trans.get("languages.default")));
117 final JComboBox languageCombo = new JComboBox(locales.toArray());
118 for (int i = 0; i < locales.size(); i++) {
119 if (Utils.equals(userLocale, locales.get(i).get())) {
120 languageCombo.setSelectedIndex(i);
123 languageCombo.addActionListener(new ActionListener() {
125 @SuppressWarnings("unchecked")
126 public void actionPerformed(ActionEvent e) {
127 Named<Locale> selection = (Named<Locale>) languageCombo.getSelectedItem();
128 Prefs.setUserLocale(selection.get());
131 panel.add(new JLabel(trans.get("lbl.language")), "gapright para");
132 panel.add(languageCombo, "wrap rel, growx, sg combos");
134 panel.add(new StyledLabel(trans.get("PreferencesDialog.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2");
137 //// Position to insert new body components:
138 panel.add(new JLabel(trans.get("pref.dlg.lbl.Positiontoinsert")), "gapright para");
139 panel.add(new JComboBox(new PrefChoiseSelector(Prefs.BODY_COMPONENT_INSERT_POSITION_KEY,
141 //// Insert in middle
143 trans.get("pref.dlg.PrefChoiseSelector1"),
144 trans.get("pref.dlg.PrefChoiseSelector2"),
145 trans.get("pref.dlg.PrefChoiseSelector3"))), "wrap para, growx, sg combos");
147 //// Confirm deletion of simulations:
148 panel.add(new JLabel(trans.get("pref.dlg.lbl.Confirmdeletion")));
149 panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
152 trans.get("pref.dlg.PrefBooleanSelector1"),
153 trans.get("pref.dlg.PrefBooleanSelector2"), true)), "wrap 40lp, growx, sg combos");
155 //// User-defined thrust curves:
156 panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap");
157 final JTextField field = new JTextField();
158 List<File> files = Prefs.getUserThrustCurveFiles();
160 for (File file : files) {
161 if (str.length() > 0) {
164 str += file.getAbsolutePath();
167 field.getDocument().addDocumentListener(new DocumentListener() {
169 public void removeUpdate(DocumentEvent e) {
174 public void insertUpdate(DocumentEvent e) {
179 public void changedUpdate(DocumentEvent e) {
183 private void changed() {
184 String text = field.getText();
185 List<File> list = new ArrayList<File>();
186 for (String s : text.split(";")) {
188 if (s.length() > 0) {
189 list.add(new File(s));
192 Prefs.setUserThrustCurveFiles(list);
195 panel.add(field, "w 100px, gapright unrel, spanx, growx, split");
198 JButton button = new JButton(trans.get("pref.dlg.but.add"));
199 button.addActionListener(new ActionListener() {
201 public void actionPerformed(ActionEvent e) {
202 JFileChooser chooser = new JFileChooser();
203 SimpleFileFilter filter =
204 new SimpleFileFilter(
205 //// All thrust curve files (*.eng; *.rse; *.zip; directories)
206 trans.get("pref.dlg.Allthrustcurvefiles"),
207 true, "eng", "rse", "zip");
208 chooser.addChoosableFileFilter(filter);
209 //// RASP motor files (*.eng)
210 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RASPfiles"),
212 //// RockSim engine files (*.rse)
213 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RockSimfiles"),
215 //// ZIP archives (*.zip)
216 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.ZIParchives"),
218 chooser.setFileFilter(filter);
219 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
220 if (defaultDirectory != null) {
221 chooser.setCurrentDirectory(defaultDirectory);
225 int returnVal = chooser.showDialog(PreferencesDialog.this, trans.get("pref.dlg.Add"));
226 if (returnVal == JFileChooser.APPROVE_OPTION) {
227 log.user("Adding user thrust curve: " + chooser.getSelectedFile());
228 defaultDirectory = chooser.getCurrentDirectory();
229 String text = field.getText().trim();
230 if (text.length() > 0) {
233 text += chooser.getSelectedFile().getAbsolutePath();
238 panel.add(button, "gapright unrel");
241 button = new JButton(trans.get("pref.dlg.but.reset"));
243 button.addActionListener(new ActionListener() {
245 public void actionPerformed(ActionEvent e) {
246 // First one sets to the default, but does not un-set the pref
247 field.setText(Prefs.getDefaultUserThrustCurveFile().getAbsolutePath());
248 Prefs.setUserThrustCurveFiles(null);
251 panel.add(button, "wrap");
253 //// 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.
254 DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false);
255 desc.setBackground(getBackground());
256 panel.add(desc, "spanx, growx, wrap 40lp");
260 //// Check for software updates at startup
261 final JCheckBox softwareUpdateBox =
262 new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
263 softwareUpdateBox.setSelected(Prefs.getCheckUpdates());
264 softwareUpdateBox.addActionListener(new ActionListener() {
266 public void actionPerformed(ActionEvent e) {
267 Prefs.setCheckUpdates(softwareUpdateBox.isSelected());
270 panel.add(softwareUpdateBox);
272 //// Check now button
273 button = new JButton(trans.get("pref.dlg.but.checknow"));
274 //// Check for software updates now
275 button.setToolTipText(trans.get("pref.dlg.ttip.Checkupdatesnow"));
276 button.addActionListener(new ActionListener() {
278 public void actionPerformed(ActionEvent e) {
282 panel.add(button, "right, wrap");
288 private JPanel unitsPane() {
289 JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
292 //// Select your preferred units:
293 panel.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph");
296 //// Rocket dimensions:
297 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
298 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
299 panel.add(combo, "sizegroup boxes");
302 panel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
303 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
304 panel.add(combo, "sizegroup boxes, wrap");
307 //// Motor dimensions:
308 panel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
309 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
310 panel.add(combo, "sizegroup boxes");
312 //// Surface density:
313 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
314 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
315 panel.add(combo, "sizegroup boxes, wrap");
319 panel.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
320 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
321 panel.add(combo, "sizegroup boxes");
324 panel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
325 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
326 panel.add(combo, "sizegroup boxes, wrap");
330 panel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
331 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
332 panel.add(combo, "sizegroup boxes");
334 //// Surface roughness:
335 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
336 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
337 panel.add(combo, "sizegroup boxes, wrap");
341 panel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
342 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
343 panel.add(combo, "sizegroup boxes");
346 panel.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
347 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
348 panel.add(combo, "sizegroup boxes, wrap");
352 panel.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
353 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
354 panel.add(combo, "sizegroup boxes");
357 panel.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
358 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
359 panel.add(combo, "sizegroup boxes, wrap");
363 panel.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
364 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
365 panel.add(combo, "sizegroup boxes");
368 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
369 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
370 panel.add(combo, "sizegroup boxes, wrap");
374 panel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
375 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
376 panel.add(combo, "sizegroup boxes");
379 panel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
380 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
381 panel.add(combo, "sizegroup boxes, wrap");
384 //// Moment of inertia:
385 panel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
386 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA));
387 panel.add(combo, "sizegroup boxes");
390 panel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
391 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
392 panel.add(combo, "sizegroup boxes, wrap");
396 panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
397 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
398 panel.add(combo, "sizegroup boxes, wrap para");
402 //// Default metric button
403 JButton button = new JButton(trans.get("pref.dlg.but.defaultmetric"));
404 button.addActionListener(new ActionListener() {
406 public void actionPerformed(ActionEvent e) {
407 UnitGroup.setDefaultMetricUnits();
408 for (DefaultUnitSelector s : unitSelectors)
412 panel.add(button, "spanx, split 2, grow");
414 //// Default imperial button
415 button = new JButton(trans.get("pref.dlg.but.defaultimperial"));
416 button.addActionListener(new ActionListener() {
418 public void actionPerformed(ActionEvent e) {
419 UnitGroup.setDefaultImperialUnits();
420 for (DefaultUnitSelector s : unitSelectors)
424 panel.add(button, "grow, wrap para");
426 //// The effects will take place the next time you open a window.
427 panel.add(new StyledLabel(
428 trans.get("pref.dlg.lbl.effect1"), -2, Style.ITALIC),
439 private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
441 private final UnitGroup group;
443 public DefaultUnitSelector(UnitGroup group) {
445 unitSelectors.add(this);
449 public Object getSelectedItem() {
450 return group.getDefaultUnit();
454 public void setSelectedItem(Object item) {
456 // Clear selection - huh?
459 if (!(item instanceof Unit)) {
460 throw new IllegalArgumentException("Illegal argument " + item);
462 group.setDefaultUnit(group.getUnitIndex((Unit) item));
466 public Object getElementAt(int index) {
467 return group.getUnit(index);
471 public int getSize() {
472 return group.getUnitCount();
476 public void fireChange() {
477 this.fireContentsChanged(this, 0, this.getSize());
483 private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
484 private final String preference;
485 private final String[] descriptions;
487 public PrefChoiseSelector(String preference, String... descriptions) {
488 this.preference = preference;
489 this.descriptions = descriptions;
493 public Object getSelectedItem() {
494 return descriptions[Prefs.getChoise(preference, descriptions.length, 0)];
498 public void setSelectedItem(Object item) {
500 // Clear selection - huh?
503 if (!(item instanceof String)) {
504 throw new IllegalArgumentException("Illegal argument " + item);
507 for (index = 0; index < descriptions.length; index++) {
508 if (((String) item).equalsIgnoreCase(descriptions[index]))
511 if (index >= descriptions.length) {
512 throw new IllegalArgumentException("Illegal argument " + item);
515 Prefs.putChoise(preference, index);
519 public Object getElementAt(int index) {
520 return descriptions[index];
524 public int getSize() {
525 return descriptions.length;
530 private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
531 private final String preference;
532 private final String trueDesc, falseDesc;
533 private final boolean def;
535 public PrefBooleanSelector(String preference, String falseDescription,
536 String trueDescription, boolean defaultState) {
537 this.preference = preference;
538 this.trueDesc = trueDescription;
539 this.falseDesc = falseDescription;
540 this.def = defaultState;
544 public Object getSelectedItem() {
545 if (Prefs.NODE.getBoolean(preference, def)) {
553 public void setSelectedItem(Object item) {
555 // Clear selection - huh?
558 if (!(item instanceof String)) {
559 throw new IllegalArgumentException("Illegal argument " + item);
562 if (trueDesc.equals(item)) {
563 Prefs.NODE.putBoolean(preference, true);
564 } else if (falseDesc.equals(item)) {
565 Prefs.NODE.putBoolean(preference, false);
567 throw new IllegalArgumentException("Illegal argument " + item);
572 public Object getElementAt(int index) {
575 return def ? trueDesc : falseDesc;
578 return def ? falseDesc : trueDesc;
581 throw new IndexOutOfBoundsException("Boolean asked for index=" + index);
586 public int getSize() {
592 private void checkForUpdates() {
593 final UpdateInfoRetriever retriever = new UpdateInfoRetriever();
598 final JDialog dialog = new JDialog(this, ModalityType.APPLICATION_MODAL);
599 JPanel panel = new JPanel(new MigLayout());
601 //// Checking for updates...
602 panel.add(new JLabel(trans.get("pref.dlg.lbl.Checkingupdates")), "wrap");
604 JProgressBar bar = new JProgressBar();
605 bar.setIndeterminate(true);
606 panel.add(bar, "growx, wrap para");
609 JButton cancel = new JButton(trans.get("dlg.but.cancel"));
610 cancel.addActionListener(new ActionListener() {
612 public void actionPerformed(ActionEvent e) {
616 panel.add(cancel, "right");
619 GUIUtil.setDisposableDialogOptions(dialog, cancel);
622 // Timer to monitor progress
623 final Timer timer = new Timer(100, null);
624 final long startTime = System.currentTimeMillis();
626 ActionListener listener = new ActionListener() {
628 public void actionPerformed(ActionEvent e) {
629 if (!retriever.isRunning() || startTime + 10000 < System.currentTimeMillis()) {
635 timer.addActionListener(listener);
640 dialog.setVisible(true);
644 UpdateInfo info = retriever.getUpdateInfo();
646 JOptionPane.showMessageDialog(this,
647 //// An error occurred while communicating with the server.
648 trans.get("pref.dlg.lbl.msg1"),
649 //// Unable to retrieve update information
650 trans.get("pref.dlg.lbl.msg2"), JOptionPane.WARNING_MESSAGE, null);
651 } else if (info.getLatestVersion() == null ||
652 info.getLatestVersion().equals("") ||
653 Prefs.getVersion().equalsIgnoreCase(info.getLatestVersion())) {
654 JOptionPane.showMessageDialog(this,
655 //// You are running the latest version of OpenRocket.
656 trans.get("pref.dlg.lbl.msg3"),
657 //// No updates available
658 trans.get("pref.dlg.lbl.msg4"), JOptionPane.INFORMATION_MESSAGE, null);
660 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
661 infoDialog.setVisible(true);
662 if (infoDialog.isReminderSelected()) {
663 Prefs.putString(Prefs.LAST_UPDATE, "");
665 Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
672 //////// Singleton implementation ////////
674 private static PreferencesDialog dialog = null;
676 public static void showPreferences(Window parent) {
677 if (dialog != null) {
680 dialog = new PreferencesDialog(parent);
681 dialog.setVisible(true);