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.gui.util.GUIUtil;
40 import net.sf.openrocket.gui.util.SwingPreferences;
41 import net.sf.openrocket.gui.util.SimpleFileFilter;
42 import net.sf.openrocket.l10n.L10N;
43 import net.sf.openrocket.l10n.Translator;
44 import net.sf.openrocket.logging.LogHelper;
45 import net.sf.openrocket.startup.Application;
46 import net.sf.openrocket.startup.Preferences;
47 import net.sf.openrocket.unit.Unit;
48 import net.sf.openrocket.unit.UnitGroup;
49 import net.sf.openrocket.util.BuildProperties;
50 import net.sf.openrocket.util.Named;
51 import net.sf.openrocket.util.Utils;
54 public class PreferencesDialog extends JDialog {
55 private static final LogHelper log = Application.getLogger();
57 private final List<DefaultUnitSelector> unitSelectors = new ArrayList<DefaultUnitSelector>();
59 private File defaultDirectory = null;
60 private static final Translator trans = Application.getTranslator();
62 private PreferencesDialog(Window parent) {
64 super(parent, trans.get("pref.dlg.title.Preferences"), Dialog.ModalityType.APPLICATION_MODAL);
66 JPanel panel = new JPanel(new MigLayout("fill, gap unrel", "[grow]", "[grow][]"));
68 JTabbedPane tabbedPane = new JTabbedPane();
69 panel.add(tabbedPane, "grow, wrap");
71 //// Units and Default units
72 tabbedPane.addTab(trans.get("pref.dlg.tab.Units"), null, unitsPane(),
73 trans.get("pref.dlg.tab.Defaultunits"));
74 //// Materials and Custom materials
75 tabbedPane.addTab(trans.get("pref.dlg.tab.Materials"), null, new MaterialEditPanel(),
76 trans.get("pref.dlg.tab.Custommaterials"));
77 //// Options and Miscellaneous options
78 tabbedPane.addTab(trans.get("pref.dlg.tab.Options"), null, optionsPane(),
79 trans.get("pref.dlg.tab.Miscellaneousoptions"));
82 JButton close = new JButton(trans.get("dlg.but.close"));
83 close.addActionListener(new ActionListener() {
85 public void actionPerformed(ActionEvent arg0) {
86 PreferencesDialog.this.setVisible(false);
87 PreferencesDialog.this.dispose();
90 panel.add(close, "span, right, tag close");
92 this.setContentPane(panel);
94 this.setLocationRelativeTo(null);
96 this.addWindowListener(new WindowAdapter() {
98 public void windowClosed(WindowEvent e) {
99 ((SwingPreferences) Application.getPreferences()).storeDefaultUnits();
103 GUIUtil.setDisposableDialogOptions(this, close);
107 private JPanel optionsPane() {
108 JPanel panel = new JPanel(new MigLayout("fillx, ins 30lp n n n"));
111 //// Language selector
112 Locale userLocale = null;
114 String locale = Application.getPreferences().getString("locale", null);
115 userLocale = L10N.toLocale(locale);
117 List<Named<Locale>> locales = new ArrayList<Named<Locale>>();
118 for (Locale l : SwingPreferences.getSupportedLocales()) {
119 locales.add(new Named<Locale>(l, l.getDisplayLanguage()));
121 Collections.sort(locales);
122 locales.add(0, new Named<Locale>(null, trans.get("languages.default")));
124 final JComboBox languageCombo = new JComboBox(locales.toArray());
125 for (int i = 0; i < locales.size(); i++) {
126 if (Utils.equals(userLocale, locales.get(i).get())) {
127 languageCombo.setSelectedIndex(i);
130 languageCombo.addActionListener(new ActionListener() {
132 @SuppressWarnings("unchecked")
133 public void actionPerformed(ActionEvent e) {
134 Named<Locale> selection = (Named<Locale>) languageCombo.getSelectedItem();
135 Locale l = selection.get();
136 Application.getPreferences().putString(Preferences.USER_LOCAL, l == null ? null : l.toString());
139 panel.add(new JLabel(trans.get("lbl.language")), "gapright para");
140 panel.add(languageCombo, "wrap rel, growx, sg combos");
142 panel.add(new StyledLabel(trans.get("PreferencesDialog.lbl.languageEffect"), -3, Style.ITALIC), "span, wrap para*2");
145 //// Position to insert new body components:
146 panel.add(new JLabel(trans.get("pref.dlg.lbl.Positiontoinsert")), "gapright para");
147 panel.add(new JComboBox(new PrefChoiseSelector(Preferences.BODY_COMPONENT_INSERT_POSITION_KEY,
149 //// Insert in middle
151 trans.get("pref.dlg.PrefChoiseSelector1"),
152 trans.get("pref.dlg.PrefChoiseSelector2"),
153 trans.get("pref.dlg.PrefChoiseSelector3"))), "wrap para, growx, sg combos");
155 //// Confirm deletion of simulations:
156 panel.add(new JLabel(trans.get("pref.dlg.lbl.Confirmdeletion")));
157 panel.add(new JComboBox(new PrefBooleanSelector(Preferences.CONFIRM_DELETE_SIMULATION,
160 trans.get("pref.dlg.PrefBooleanSelector1"),
161 trans.get("pref.dlg.PrefBooleanSelector2"), true)), "wrap 40lp, growx, sg combos");
163 //// User-defined thrust curves:
164 panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap");
165 final JTextField field = new JTextField();
166 List<File> files = ((SwingPreferences) Application.getPreferences()).getUserThrustCurveFiles();
168 for (File file : files) {
169 if (str.length() > 0) {
172 str += file.getAbsolutePath();
175 field.getDocument().addDocumentListener(new DocumentListener() {
177 public void removeUpdate(DocumentEvent e) {
182 public void insertUpdate(DocumentEvent e) {
187 public void changedUpdate(DocumentEvent e) {
191 private void changed() {
192 String text = field.getText();
193 List<File> list = new ArrayList<File>();
194 for (String s : text.split(";")) {
196 if (s.length() > 0) {
197 list.add(new File(s));
200 ((SwingPreferences) Application.getPreferences()).setUserThrustCurveFiles(list);
203 panel.add(field, "w 100px, gapright unrel, spanx, growx, split");
206 JButton button = new JButton(trans.get("pref.dlg.but.add"));
207 button.addActionListener(new ActionListener() {
209 public void actionPerformed(ActionEvent e) {
210 JFileChooser chooser = new JFileChooser();
211 SimpleFileFilter filter =
212 new SimpleFileFilter(
213 //// All thrust curve files (*.eng; *.rse; *.zip; directories)
214 trans.get("pref.dlg.Allthrustcurvefiles"),
215 true, "eng", "rse", "zip");
216 chooser.addChoosableFileFilter(filter);
217 //// RASP motor files (*.eng)
218 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RASPfiles"),
220 //// RockSim engine files (*.rse)
221 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RockSimfiles"),
223 //// ZIP archives (*.zip)
224 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.ZIParchives"),
226 chooser.setFileFilter(filter);
227 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
228 if (defaultDirectory != null) {
229 chooser.setCurrentDirectory(defaultDirectory);
233 int returnVal = chooser.showDialog(PreferencesDialog.this, trans.get("pref.dlg.Add"));
234 if (returnVal == JFileChooser.APPROVE_OPTION) {
235 log.user("Adding user thrust curve: " + chooser.getSelectedFile());
236 defaultDirectory = chooser.getCurrentDirectory();
237 String text = field.getText().trim();
238 if (text.length() > 0) {
241 text += chooser.getSelectedFile().getAbsolutePath();
246 panel.add(button, "gapright unrel");
249 button = new JButton(trans.get("pref.dlg.but.reset"));
251 button.addActionListener(new ActionListener() {
253 public void actionPerformed(ActionEvent e) {
254 // First one sets to the default, but does not un-set the pref
255 field.setText(((SwingPreferences)Application.getPreferences()).getDefaultUserThrustCurveFile().getAbsolutePath());
256 ((SwingPreferences) Application.getPreferences()).setUserThrustCurveFiles(null);
259 panel.add(button, "wrap");
261 //// 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.
262 DescriptionArea desc = new DescriptionArea(trans.get("pref.dlg.DescriptionArea.Adddirectories"), 3, -3, false);
263 desc.setBackground(getBackground());
264 panel.add(desc, "spanx, growx, wrap 40lp");
268 //// Check for software updates at startup
269 final JCheckBox softwareUpdateBox =
270 new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
271 softwareUpdateBox.setSelected( Application.getPreferences().getCheckUpdates());
272 softwareUpdateBox.addActionListener(new ActionListener() {
274 public void actionPerformed(ActionEvent e) {
275 Application.getPreferences().setCheckUpdates(softwareUpdateBox.isSelected());
278 panel.add(softwareUpdateBox);
280 //// Check now button
281 button = new JButton(trans.get("pref.dlg.but.checknow"));
282 //// Check for software updates now
283 button.setToolTipText(trans.get("pref.dlg.ttip.Checkupdatesnow"));
284 button.addActionListener(new ActionListener() {
286 public void actionPerformed(ActionEvent e) {
290 panel.add(button, "right, wrap");
296 private JPanel unitsPane() {
297 JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
300 //// Select your preferred units:
301 panel.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph");
304 //// Rocket dimensions:
305 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
306 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
307 panel.add(combo, "sizegroup boxes");
310 panel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
311 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
312 panel.add(combo, "sizegroup boxes, wrap");
315 //// Motor dimensions:
316 panel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
317 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
318 panel.add(combo, "sizegroup boxes");
320 //// Surface density:
321 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
322 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
323 panel.add(combo, "sizegroup boxes, wrap");
327 panel.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
328 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
329 panel.add(combo, "sizegroup boxes");
332 panel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
333 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
334 panel.add(combo, "sizegroup boxes, wrap");
338 panel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
339 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
340 panel.add(combo, "sizegroup boxes");
342 //// Surface roughness:
343 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
344 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
345 panel.add(combo, "sizegroup boxes, wrap");
349 panel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
350 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
351 panel.add(combo, "sizegroup boxes");
354 panel.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
355 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
356 panel.add(combo, "sizegroup boxes, wrap");
360 panel.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
361 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
362 panel.add(combo, "sizegroup boxes");
365 panel.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
366 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
367 panel.add(combo, "sizegroup boxes, wrap");
371 panel.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
372 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
373 panel.add(combo, "sizegroup boxes");
376 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
377 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
378 panel.add(combo, "sizegroup boxes, wrap");
382 panel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
383 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
384 panel.add(combo, "sizegroup boxes");
387 panel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
388 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
389 panel.add(combo, "sizegroup boxes, wrap");
392 //// Moment of inertia:
393 panel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
394 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA));
395 panel.add(combo, "sizegroup boxes");
398 panel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
399 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
400 panel.add(combo, "sizegroup boxes, wrap");
404 panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
405 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
406 panel.add(combo, "sizegroup boxes, wrap para");
410 //// Default metric button
411 JButton button = new JButton(trans.get("pref.dlg.but.defaultmetric"));
412 button.addActionListener(new ActionListener() {
414 public void actionPerformed(ActionEvent e) {
415 UnitGroup.setDefaultMetricUnits();
416 for (DefaultUnitSelector s : unitSelectors)
420 panel.add(button, "spanx, split 2, grow");
422 //// Default imperial button
423 button = new JButton(trans.get("pref.dlg.but.defaultimperial"));
424 button.addActionListener(new ActionListener() {
426 public void actionPerformed(ActionEvent e) {
427 UnitGroup.setDefaultImperialUnits();
428 for (DefaultUnitSelector s : unitSelectors)
432 panel.add(button, "grow, wrap para");
434 //// The effects will take place the next time you open a window.
435 panel.add(new StyledLabel(
436 trans.get("pref.dlg.lbl.effect1"), -2, Style.ITALIC),
447 private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
449 private final UnitGroup group;
451 public DefaultUnitSelector(UnitGroup group) {
453 unitSelectors.add(this);
457 public Object getSelectedItem() {
458 return group.getDefaultUnit();
462 public void setSelectedItem(Object item) {
464 // Clear selection - huh?
467 if (!(item instanceof Unit)) {
468 throw new IllegalArgumentException("Illegal argument " + item);
470 group.setDefaultUnit(group.getUnitIndex((Unit) item));
474 public Object getElementAt(int index) {
475 return group.getUnit(index);
479 public int getSize() {
480 return group.getUnitCount();
484 public void fireChange() {
485 this.fireContentsChanged(this, 0, this.getSize());
491 private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
492 private final String preference;
493 private final String[] descriptions;
495 public PrefChoiseSelector(String preference, String... descriptions) {
496 this.preference = preference;
497 this.descriptions = descriptions;
501 public Object getSelectedItem() {
502 return descriptions[Application.getPreferences().getChoice(preference, descriptions.length, 0)];
506 public void setSelectedItem(Object item) {
508 // Clear selection - huh?
511 if (!(item instanceof String)) {
512 throw new IllegalArgumentException("Illegal argument " + item);
515 for (index = 0; index < descriptions.length; index++) {
516 if (((String) item).equalsIgnoreCase(descriptions[index]))
519 if (index >= descriptions.length) {
520 throw new IllegalArgumentException("Illegal argument " + item);
523 Application.getPreferences().putChoice(preference, index);
527 public Object getElementAt(int index) {
528 return descriptions[index];
532 public int getSize() {
533 return descriptions.length;
538 private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
539 private final String preference;
540 private final String trueDesc, falseDesc;
541 private final boolean def;
543 public PrefBooleanSelector(String preference, String falseDescription,
544 String trueDescription, boolean defaultState) {
545 this.preference = preference;
546 this.trueDesc = trueDescription;
547 this.falseDesc = falseDescription;
548 this.def = defaultState;
552 public Object getSelectedItem() {
553 if (Application.getPreferences().getBoolean(preference, def)) {
561 public void setSelectedItem(Object item) {
563 // Clear selection - huh?
566 if (!(item instanceof String)) {
567 throw new IllegalArgumentException("Illegal argument " + item);
570 if (trueDesc.equals(item)) {
571 Application.getPreferences().putBoolean(preference, true);
572 } else if (falseDesc.equals(item)) {
573 Application.getPreferences().putBoolean(preference, false);
575 throw new IllegalArgumentException("Illegal argument " + item);
580 public Object getElementAt(int index) {
583 return def ? trueDesc : falseDesc;
586 return def ? falseDesc : trueDesc;
589 throw new IndexOutOfBoundsException("Boolean asked for index=" + index);
594 public int getSize() {
600 private void checkForUpdates() {
601 final UpdateInfoRetriever retriever = new UpdateInfoRetriever();
606 final JDialog dialog = new JDialog(this, ModalityType.APPLICATION_MODAL);
607 JPanel panel = new JPanel(new MigLayout());
609 //// Checking for updates...
610 panel.add(new JLabel(trans.get("pref.dlg.lbl.Checkingupdates")), "wrap");
612 JProgressBar bar = new JProgressBar();
613 bar.setIndeterminate(true);
614 panel.add(bar, "growx, wrap para");
617 JButton cancel = new JButton(trans.get("dlg.but.cancel"));
618 cancel.addActionListener(new ActionListener() {
620 public void actionPerformed(ActionEvent e) {
624 panel.add(cancel, "right");
627 GUIUtil.setDisposableDialogOptions(dialog, cancel);
630 // Timer to monitor progress
631 final Timer timer = new Timer(100, null);
632 final long startTime = System.currentTimeMillis();
634 ActionListener listener = new ActionListener() {
636 public void actionPerformed(ActionEvent e) {
637 if (!retriever.isRunning() || startTime + 10000 < System.currentTimeMillis()) {
643 timer.addActionListener(listener);
648 dialog.setVisible(true);
652 UpdateInfo info = retriever.getUpdateInfo();
654 JOptionPane.showMessageDialog(this,
655 //// An error occurred while communicating with the server.
656 trans.get("pref.dlg.lbl.msg1"),
657 //// Unable to retrieve update information
658 trans.get("pref.dlg.lbl.msg2"), JOptionPane.WARNING_MESSAGE, null);
659 } else if (info.getLatestVersion() == null ||
660 info.getLatestVersion().equals("") ||
661 BuildProperties.getVersion().equalsIgnoreCase(info.getLatestVersion())) {
662 JOptionPane.showMessageDialog(this,
663 //// You are running the latest version of OpenRocket.
664 trans.get("pref.dlg.lbl.msg3"),
665 //// No updates available
666 trans.get("pref.dlg.lbl.msg4"), JOptionPane.INFORMATION_MESSAGE, null);
668 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
669 infoDialog.setVisible(true);
670 if (infoDialog.isReminderSelected()) {
671 Application.getPreferences().putString(SwingPreferences.LAST_UPDATE, "");
673 Application.getPreferences().putString(SwingPreferences.LAST_UPDATE, info.getLatestVersion());
680 //////// Singleton implementation ////////
682 private static PreferencesDialog dialog = null;
684 public static void showPreferences(Window parent) {
685 if (dialog != null) {
688 dialog = new PreferencesDialog(parent);
689 dialog.setVisible(true);