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,
104 "Always ask", "Insert in middle", "Add to end")), "wrap para, growx, sg combos");
106 //// Confirm deletion of simulations:
107 panel.add(new JLabel(trans.get("pref.dlg.lbl.Confirmdeletion")));
108 panel.add(new JComboBox(new PrefBooleanSelector(Prefs.CONFIRM_DELETE_SIMULATION,
109 "Delete", "Confirm", true)), "wrap 40lp, growx, sg combos");
111 //// User-defined thrust curves:
112 panel.add(new JLabel(trans.get("pref.dlg.lbl.User-definedthrust")), "spanx, wrap");
113 final JTextField field = new JTextField();
114 List<File> files = Prefs.getUserThrustCurveFiles();
116 for (File file : files) {
117 if (str.length() > 0) {
120 str += file.getAbsolutePath();
123 field.getDocument().addDocumentListener(new DocumentListener() {
125 public void removeUpdate(DocumentEvent e) {
130 public void insertUpdate(DocumentEvent e) {
135 public void changedUpdate(DocumentEvent e) {
139 private void changed() {
140 String text = field.getText();
141 List<File> list = new ArrayList<File>();
142 for (String s : text.split(";")) {
144 if (s.length() > 0) {
145 list.add(new File(s));
148 Prefs.setUserThrustCurveFiles(list);
151 panel.add(field, "w 100px, gapright unrel, spanx, growx, split");
154 JButton button = new JButton(trans.get("pref.dlg.but.add"));
155 button.addActionListener(new ActionListener() {
157 public void actionPerformed(ActionEvent e) {
158 JFileChooser chooser = new JFileChooser();
159 SimpleFileFilter filter =
160 new SimpleFileFilter(
161 //// All thrust curve files (*.eng; *.rse; *.zip; directories)
162 trans.get("pref.dlg.Allthrustcurvefiles"),
163 true, "eng", "rse", "zip");
164 chooser.addChoosableFileFilter(filter);
165 //// RASP motor files (*.eng)
166 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RASPfiles"),
168 //// RockSim engine files (*.rse)
169 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.RockSimfiles"),
171 //// ZIP archives (*.zip)
172 chooser.addChoosableFileFilter(new SimpleFileFilter(trans.get("pref.dlg.ZIParchives"),
174 chooser.setFileFilter(filter);
175 chooser.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
176 if (defaultDirectory != null) {
177 chooser.setCurrentDirectory(defaultDirectory);
180 int returnVal = chooser.showDialog(PreferencesDialog.this, "Add");
181 if (returnVal == JFileChooser.APPROVE_OPTION) {
182 log.user("Adding user thrust curve: " + chooser.getSelectedFile());
183 defaultDirectory = chooser.getCurrentDirectory();
184 String text = field.getText().trim();
185 if (text.length() > 0) {
188 text += chooser.getSelectedFile().getAbsolutePath();
193 panel.add(button, "gapright unrel");
196 button = new JButton(trans.get("pref.dlg.but.reset"));
198 button.addActionListener(new ActionListener() {
200 public void actionPerformed(ActionEvent e) {
201 // First one sets to the default, but does not un-set the pref
202 field.setText(Prefs.getDefaultUserThrustCurveFile().getAbsolutePath());
203 Prefs.setUserThrustCurveFiles(null);
206 panel.add(button, "wrap");
208 DescriptionArea desc = new DescriptionArea("Add directories, RASP motor files (*.eng), " +
209 "RockSim engine files (*.rse) or ZIP archives separated by a semicolon (;) to load external " +
210 "thrust curves. Changes will take effect the next time you start OpenRocket.", 3, -3, false);
211 desc.setBackground(getBackground());
212 panel.add(desc, "spanx, growx, wrap 40lp");
216 //// Check for software updates at startup
217 final JCheckBox softwareUpdateBox =
218 new JCheckBox(trans.get("pref.dlg.checkbox.Checkupdates"));
219 softwareUpdateBox.setSelected(Prefs.getCheckUpdates());
220 softwareUpdateBox.addActionListener(new ActionListener() {
222 public void actionPerformed(ActionEvent e) {
223 Prefs.setCheckUpdates(softwareUpdateBox.isSelected());
226 panel.add(softwareUpdateBox);
228 //// Check now button
229 button = new JButton(trans.get("pref.dlg.but.checknow"));
230 //// Check for software updates now
231 button.setToolTipText(trans.get("pref.dlg.ttip.Checkupdatesnow"));
232 button.addActionListener(new ActionListener() {
234 public void actionPerformed(ActionEvent e) {
238 panel.add(button, "right, wrap");
244 private JPanel unitsPane() {
245 JPanel panel = new JPanel(new MigLayout("", "[][]40lp[][]"));
248 //// Select your preferred units:
249 panel.add(new JLabel(trans.get("pref.dlg.lbl.Selectprefunits")), "span, wrap paragraph");
252 //// Rocket dimensions:
253 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rocketdimensions")));
254 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_LENGTH));
255 panel.add(combo, "sizegroup boxes");
258 panel.add(new JLabel(trans.get("pref.dlg.lbl.Linedensity")));
259 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_LINE));
260 panel.add(combo, "sizegroup boxes, wrap");
263 //// Motor dimensions:
264 panel.add(new JLabel(trans.get("pref.dlg.lbl.Motordimensions")));
265 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MOTOR_DIMENSIONS));
266 panel.add(combo, "sizegroup boxes");
268 //// Surface density:
269 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfacedensity")));
270 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_SURFACE));
271 panel.add(combo, "sizegroup boxes, wrap");
275 panel.add(new JLabel(trans.get("pref.dlg.lbl.Distance")));
276 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DISTANCE));
277 panel.add(combo, "sizegroup boxes");
280 panel.add(new JLabel(trans.get("pref.dlg.lbl.Bulkdensity")));
281 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_DENSITY_BULK));
282 panel.add(combo, "sizegroup boxes, wrap");
286 panel.add(new JLabel(trans.get("pref.dlg.lbl.Velocity")));
287 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_VELOCITY));
288 panel.add(combo, "sizegroup boxes");
290 //// Surface roughness:
291 panel.add(new JLabel(trans.get("pref.dlg.lbl.Surfaceroughness")));
292 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROUGHNESS));
293 panel.add(combo, "sizegroup boxes, wrap");
297 panel.add(new JLabel(trans.get("pref.dlg.lbl.Acceleration")));
298 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ACCELERATION));
299 panel.add(combo, "sizegroup boxes");
302 panel.add(new JLabel(trans.get("pref.dlg.lbl.Area")));
303 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_AREA));
304 panel.add(combo, "sizegroup boxes, wrap");
308 panel.add(new JLabel(trans.get("pref.dlg.lbl.Mass")));
309 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_MASS));
310 panel.add(combo, "sizegroup boxes");
313 panel.add(new JLabel(trans.get("pref.dlg.lbl.Angle")));
314 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ANGLE));
315 panel.add(combo, "sizegroup boxes, wrap");
319 panel.add(new JLabel(trans.get("pref.dlg.lbl.Force")));
320 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_FORCE));
321 panel.add(combo, "sizegroup boxes");
324 panel.add(new JLabel(trans.get("pref.dlg.lbl.Rollrate")));
325 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_ROLL));
326 panel.add(combo, "sizegroup boxes, wrap");
330 panel.add(new JLabel(trans.get("pref.dlg.lbl.Totalimpulse")));
331 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_IMPULSE));
332 panel.add(combo, "sizegroup boxes");
335 panel.add(new JLabel(trans.get("pref.dlg.lbl.Temperature")));
336 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_TEMPERATURE));
337 panel.add(combo, "sizegroup boxes, wrap");
340 //// Moment of inertia:
341 panel.add(new JLabel(trans.get("pref.dlg.lbl.Momentofinertia")));
342 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_INERTIA));
343 panel.add(combo, "sizegroup boxes");
346 panel.add(new JLabel(trans.get("pref.dlg.lbl.Pressure")));
347 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_PRESSURE));
348 panel.add(combo, "sizegroup boxes, wrap");
351 panel.add(new JLabel(trans.get("pref.dlg.lbl.Stability")));
352 combo = new JComboBox(new DefaultUnitSelector(UnitGroup.UNITS_STABILITY));
353 panel.add(combo, "sizegroup boxes, wrap para");
357 //// Default metric button
358 JButton button = new JButton(trans.get("pref.dlg.but.defaultmetric"));
359 button.addActionListener(new ActionListener() {
361 public void actionPerformed(ActionEvent e) {
362 UnitGroup.setDefaultMetricUnits();
363 for (DefaultUnitSelector s : unitSelectors)
367 panel.add(button, "spanx, split 2, grow");
369 //// Default imperial button
370 button = new JButton(trans.get("pref.dlg.but.defaultimperial"));
371 button.addActionListener(new ActionListener() {
373 public void actionPerformed(ActionEvent e) {
374 UnitGroup.setDefaultImperialUnits();
375 for (DefaultUnitSelector s : unitSelectors)
379 panel.add(button, "grow, wrap para");
381 //// The effects will take place the next time you open a window.
382 panel.add(new StyledLabel(
383 trans.get("pref.dlg.lbl.effect1"), -2),
394 private class DefaultUnitSelector extends AbstractListModel implements ComboBoxModel {
396 private final UnitGroup group;
398 public DefaultUnitSelector(UnitGroup group) {
400 unitSelectors.add(this);
404 public Object getSelectedItem() {
405 return group.getDefaultUnit();
409 public void setSelectedItem(Object item) {
411 // Clear selection - huh?
414 if (!(item instanceof Unit)) {
415 throw new IllegalArgumentException("Illegal argument " + item);
417 group.setDefaultUnit(group.getUnitIndex((Unit) item));
421 public Object getElementAt(int index) {
422 return group.getUnit(index);
426 public int getSize() {
427 return group.getUnitCount();
431 public void fireChange() {
432 this.fireContentsChanged(this, 0, this.getSize());
438 private class PrefChoiseSelector extends AbstractListModel implements ComboBoxModel {
439 private final String preference;
440 private final String[] descriptions;
442 public PrefChoiseSelector(String preference, String... descriptions) {
443 this.preference = preference;
444 this.descriptions = descriptions;
448 public Object getSelectedItem() {
449 return descriptions[Prefs.getChoise(preference, descriptions.length, 0)];
453 public void setSelectedItem(Object item) {
455 // Clear selection - huh?
458 if (!(item instanceof String)) {
459 throw new IllegalArgumentException("Illegal argument " + item);
462 for (index = 0; index < descriptions.length; index++) {
463 if (((String) item).equalsIgnoreCase(descriptions[index]))
466 if (index >= descriptions.length) {
467 throw new IllegalArgumentException("Illegal argument " + item);
470 Prefs.putChoise(preference, index);
474 public Object getElementAt(int index) {
475 return descriptions[index];
479 public int getSize() {
480 return descriptions.length;
485 private class PrefBooleanSelector extends AbstractListModel implements ComboBoxModel {
486 private final String preference;
487 private final String trueDesc, falseDesc;
488 private final boolean def;
490 public PrefBooleanSelector(String preference, String falseDescription,
491 String trueDescription, boolean defaultState) {
492 this.preference = preference;
493 this.trueDesc = trueDescription;
494 this.falseDesc = falseDescription;
495 this.def = defaultState;
499 public Object getSelectedItem() {
500 if (Prefs.NODE.getBoolean(preference, def)) {
508 public void setSelectedItem(Object item) {
510 // Clear selection - huh?
513 if (!(item instanceof String)) {
514 throw new IllegalArgumentException("Illegal argument " + item);
517 if (trueDesc.equals(item)) {
518 Prefs.NODE.putBoolean(preference, true);
519 } else if (falseDesc.equals(item)) {
520 Prefs.NODE.putBoolean(preference, false);
522 throw new IllegalArgumentException("Illegal argument " + item);
527 public Object getElementAt(int index) {
530 return def ? trueDesc : falseDesc;
533 return def ? falseDesc : trueDesc;
536 throw new IndexOutOfBoundsException("Boolean asked for index=" + index);
541 public int getSize() {
547 private void checkForUpdates() {
548 final UpdateInfoRetriever retriever = new UpdateInfoRetriever();
553 final JDialog dialog = new JDialog(this, ModalityType.APPLICATION_MODAL);
554 JPanel panel = new JPanel(new MigLayout());
556 //// Checking for updates...
557 panel.add(new JLabel(trans.get("pref.dlg.lbl.Checkingupdates")), "wrap");
559 JProgressBar bar = new JProgressBar();
560 bar.setIndeterminate(true);
561 panel.add(bar, "growx, wrap para");
564 JButton cancel = new JButton(trans.get("dlg.but.cancel"));
565 cancel.addActionListener(new ActionListener() {
567 public void actionPerformed(ActionEvent e) {
571 panel.add(cancel, "right");
574 GUIUtil.setDisposableDialogOptions(dialog, cancel);
577 // Timer to monitor progress
578 final Timer timer = new Timer(100, null);
579 final long startTime = System.currentTimeMillis();
581 ActionListener listener = new ActionListener() {
583 public void actionPerformed(ActionEvent e) {
584 if (!retriever.isRunning() || startTime + 10000 < System.currentTimeMillis()) {
590 timer.addActionListener(listener);
595 dialog.setVisible(true);
599 UpdateInfo info = retriever.getUpdateInfo();
601 JOptionPane.showMessageDialog(this,
602 //// An error occurred while communicating with the server.
603 trans.get("pref.dlg.lbl.msg1"),
604 //// Unable to retrieve update information
605 trans.get("pref.dlg.lbl.msg2"), JOptionPane.WARNING_MESSAGE, null);
606 } else if (info.getLatestVersion() == null ||
607 info.getLatestVersion().equals("") ||
608 Prefs.getVersion().equalsIgnoreCase(info.getLatestVersion())) {
609 JOptionPane.showMessageDialog(this,
610 //// You are running the latest version of OpenRocket.
611 trans.get("pref.dlg.lbl.msg3"),
612 //// No updates available
613 trans.get("pref.dlg.lbl.msg4"), JOptionPane.INFORMATION_MESSAGE, null);
615 UpdateInfoDialog infoDialog = new UpdateInfoDialog(info);
616 infoDialog.setVisible(true);
617 if (infoDialog.isReminderSelected()) {
618 Prefs.putString(Prefs.LAST_UPDATE, "");
620 Prefs.putString(Prefs.LAST_UPDATE, info.getLatestVersion());
627 //////// Singleton implementation ////////
629 private static PreferencesDialog dialog = null;
631 public static void showPreferences() {
632 if (dialog != null) {
635 dialog = new PreferencesDialog();
636 dialog.setVisible(true);