From 0aa6cc434d4394da4a92bbef9e8fa0b9fce16697 Mon Sep 17 00:00:00 2001 From: Vhati Date: Mon, 16 Sep 2013 06:57:58 -0400 Subject: [PATCH] Added Preferences dialog --- skel_common/readme_changelog.txt | 1 + .../modmanager/core/SlipstreamConfig.java | 2 +- .../vhati/modmanager/ui/FieldEditorPanel.java | 432 ++++++++++++++++++ .../net/vhati/modmanager/ui/ManagerFrame.java | 24 +- .../vhati/modmanager/ui/ModXMLSandbox.java | 1 + .../vhati/modmanager/ui/RegexDocument.java | 62 +++ .../modmanager/ui/SlipstreamConfigDialog.java | 171 +++++++ 7 files changed, 691 insertions(+), 2 deletions(-) create mode 100644 src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java create mode 100644 src/main/java/net/vhati/modmanager/ui/RegexDocument.java create mode 100644 src/main/java/net/vhati/modmanager/ui/SlipstreamConfigDialog.java diff --git a/skel_common/readme_changelog.txt b/skel_common/readme_changelog.txt index 5e6de47..c2de281 100644 --- a/skel_common/readme_changelog.txt +++ b/skel_common/readme_changelog.txt @@ -8,6 +8,7 @@ Changelog - Added --global-panic commandline arg to show mod devs typoed find tags - Added commandline tips in readme_modders.txt - Fixed sloppy parser Validate error about things not allowed at root +- Added a Preferences dialog as an alternative to editing modman.cfg 1.2: - Added a commandline interface diff --git a/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java index d93cce0..df49d6a 100644 --- a/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java +++ b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java @@ -57,7 +57,7 @@ public class SlipstreamConfig { configComments += " ftl_dats_path - The path to FTL's resources folder. If invalid, you'll be prompted.\n"; configComments += " never_run_ftl - If true, there will be no offer to run FTL after patching. Default: false.\n"; configComments += " update_catalog - If a number greater than 0, check for new mod descriptions every N days.\n"; - configComments += " update_app - If a number greater than 0, check for new app version every N days.\n"; + configComments += " update_app - If a number greater than 0, check for newer app versions every N days.\n"; configComments += " use_default_ui - If true, no attempt will be made to resemble a native GUI. Default: false.\n"; configComments += " remember_geometry - If true, window geometry will be saved on exit and restored on startup.\n"; configComments += "\n"; diff --git a/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java b/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java new file mode 100644 index 0000000..47e9a88 --- /dev/null +++ b/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java @@ -0,0 +1,432 @@ +package net.vhati.modmanager.ui; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.util.HashMap; +import java.util.Map; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSeparator; +import javax.swing.JSlider; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.SwingConstants; +import javax.swing.UIManager; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import net.vhati.modmanager.ui.RegexDocument; + + +public class FieldEditorPanel extends JPanel { + public enum ContentType { WRAPPED_LABEL, LABEL, STRING, INTEGER, BOOLEAN, SLIDER, COMBO, CHOOSER }; + + private Map wrappedLabelMap = new HashMap(); + private Map labelMap = new HashMap(); + private Map stringMap = new HashMap(); + private Map intMap = new HashMap(); + private Map boolMap = new HashMap(); + private Map sliderMap = new HashMap(); + private Map comboMap = new HashMap(); + private Map chooserMap = new HashMap(); + private Map reminderMap = new HashMap(); + + private GridBagConstraints gridC = new GridBagConstraints(); + + private Component nameStrut = Box.createHorizontalStrut(1); + private Component valueStrut = Box.createHorizontalStrut(120); + private Component reminderStrut = Box.createHorizontalStrut(90); + + private boolean remindersVisible; + + + public FieldEditorPanel( boolean remindersVisible ) { + super( new GridBagLayout() ); + this.remindersVisible = remindersVisible; + + gridC.anchor = GridBagConstraints.WEST; + gridC.fill = GridBagConstraints.HORIZONTAL; + gridC.weightx = 0.0; + gridC.weighty = 0.0; + gridC.gridwidth = 1; + gridC.gridx = 0; + gridC.gridy = 0; + + // No default width for col 0. + gridC.gridx = 0; + this.add( nameStrut, gridC ); + gridC.gridx++; + this.add( valueStrut, gridC ); + gridC.gridx++; + if ( remindersVisible ) { + this.add( reminderStrut, gridC ); + gridC.gridy++; + } + + gridC.insets = new Insets(2, 4, 2, 4); + } + + + public void setNameWidth( int width ) { + nameStrut.setMinimumSize( new Dimension(width, 0) ); + nameStrut.setPreferredSize( new Dimension(width, 0) ); + } + + public void setValueWidth( int width ) { + valueStrut.setMinimumSize( new Dimension(width, 0) ); + valueStrut.setPreferredSize( new Dimension(width, 0) ); + } + + public void setReminderWidth( int width ) { + reminderStrut.setMinimumSize( new Dimension(width, 0) ); + reminderStrut.setPreferredSize( new Dimension(width, 0) ); + } + + + /** + * Constructs JComponents for a given type of value. + * A row consists of a static label, some JComponent, + * and a reminder label. + * + * The component and reminder will be accessable later + * via getter methods. + */ + public void addRow( String valueName, ContentType contentType ) { + gridC.fill = GridBagConstraints.HORIZONTAL; + gridC.gridwidth = 1; + gridC.weighty = 0.0; + gridC.gridx = 0; + this.add( new JLabel( valueName +":" ), gridC ); + + gridC.gridx++; + if ( contentType == ContentType.WRAPPED_LABEL ) { + gridC.anchor = GridBagConstraints.WEST; + JTextArea valueArea = new JTextArea(); + valueArea.setBackground(null); + valueArea.setEditable( false ); + valueArea.setBorder(null); + valueArea.setLineWrap( true ); + valueArea.setWrapStyleWord( true ); + valueArea.setFocusable( false ); + valueArea.setFont( UIManager.getFont("Label.font") ); + + wrappedLabelMap.put( valueName, valueArea ); + this.add( valueArea, gridC ); + } + else if ( contentType == ContentType.LABEL ) { + gridC.anchor = GridBagConstraints.WEST; + JLabel valueLbl = new JLabel(); + valueLbl.setHorizontalAlignment( SwingConstants.CENTER ); + labelMap.put( valueName, valueLbl ); + this.add( valueLbl, gridC ); + } + else if ( contentType == ContentType.STRING ) { + gridC.anchor = GridBagConstraints.WEST; + JTextField valueField = new JTextField(); + stringMap.put( valueName, valueField ); + this.add( valueField, gridC ); + } + else if ( contentType == ContentType.INTEGER ) { + gridC.anchor = GridBagConstraints.WEST; + JTextField valueField = new JTextField(); + valueField.setHorizontalAlignment( JTextField.RIGHT ); + valueField.setDocument( new RegexDocument("[0-9]*") ); + intMap.put( valueName, valueField ); + this.add( valueField, gridC ); + } + else if ( contentType == ContentType.BOOLEAN ) { + gridC.anchor = GridBagConstraints.CENTER; + JCheckBox valueCheck = new JCheckBox(); + valueCheck.setHorizontalAlignment( SwingConstants.CENTER ); + boolMap.put( valueName, valueCheck ); + this.add( valueCheck, gridC ); + } + else if ( contentType == ContentType.SLIDER ) { + gridC.anchor = GridBagConstraints.CENTER; + JPanel panel = new JPanel(); + panel.setLayout( new BoxLayout(panel, BoxLayout.X_AXIS) ); + final JSlider valueSlider = new JSlider( JSlider.HORIZONTAL ); + valueSlider.setPreferredSize( new Dimension(50, valueSlider.getPreferredSize().height) ); + sliderMap.put( valueName, valueSlider ); + panel.add(valueSlider); + final JTextField valueField = new JTextField(3); + valueField.setMaximumSize( valueField.getPreferredSize() ); + valueField.setHorizontalAlignment( JTextField.RIGHT ); + valueField.setEditable( false ); + panel.add(valueField); + this.add( panel, gridC ); + + valueSlider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + valueField.setText( ""+valueSlider.getValue() ); + } + }); + } + else if ( contentType == ContentType.COMBO ) { + gridC.anchor = GridBagConstraints.CENTER; + JComboBox valueCombo = new JComboBox(); + valueCombo.setEditable(false); + comboMap.put( valueName, valueCombo ); + this.add( valueCombo, gridC ); + } + else if ( contentType == ContentType.CHOOSER ) { + gridC.anchor = GridBagConstraints.WEST; + JPanel panel = new JPanel(); + panel.setLayout( new BoxLayout(panel, BoxLayout.X_AXIS) ); + + JTextField chooserField = new JTextField(); + panel.add( chooserField ); + panel.add( Box.createHorizontalStrut( 5 ) ); + JButton chooserBtn = new JButton( "..." ); + chooserBtn.setMargin( new Insets(1,2,1,2) ); + panel.add( chooserBtn ); + Chooser valueChooser = new Chooser( chooserField, chooserBtn ); + chooserMap.put( valueName, valueChooser ); + + this.add( panel, gridC ); + } + gridC.gridx++; + + if ( remindersVisible ) { + gridC.anchor = GridBagConstraints.WEST; + JLabel valueReminder = new JLabel(); + reminderMap.put( valueName, valueReminder ); + this.add( valueReminder, gridC ); + } + + gridC.gridy++; + } + + public void addTextRow( String text ) { + gridC.fill = GridBagConstraints.HORIZONTAL; + gridC.weighty = 0.0; + gridC.gridwidth = GridBagConstraints.REMAINDER; + gridC.gridx = 0; + + gridC.anchor = GridBagConstraints.WEST; + JTextArea textArea = new JTextArea( text ); + textArea.setBackground(null); + textArea.setEditable( false ); + textArea.setBorder(null); + textArea.setLineWrap( true ); + textArea.setWrapStyleWord( true ); + textArea.setFocusable( false ); + textArea.setFont( UIManager.getFont("Label.font") ); + + this.add( textArea, gridC ); + gridC.gridy++; + } + + public void addSeparatorRow() { + gridC.fill = GridBagConstraints.HORIZONTAL; + gridC.weighty = 0.0; + gridC.gridwidth = GridBagConstraints.REMAINDER; + gridC.gridx = 0; + + JPanel panel = new JPanel(); + panel.setLayout( new BoxLayout( panel, BoxLayout.Y_AXIS ) ); + panel.add( Box.createVerticalStrut(10) ); + JSeparator sep = new JSeparator(); + sep.setPreferredSize( new Dimension(1, sep.getPreferredSize().height) ); + panel.add( sep ); + panel.add( Box.createVerticalStrut(10) ); + + this.add( panel, gridC ); + gridC.gridy++; + } + + public void addBlankRow() { + gridC.fill = GridBagConstraints.NONE; + gridC.weighty = 0.0; + gridC.gridwidth = GridBagConstraints.REMAINDER; + gridC.gridx = 0; + + this.add( Box.createVerticalStrut(12), gridC ); + gridC.gridy++; + } + + public void addFillRow() { + gridC.fill = GridBagConstraints.VERTICAL; + gridC.weighty = 1.0; + gridC.gridwidth = GridBagConstraints.REMAINDER; + gridC.gridx = 0; + + this.add( Box.createVerticalGlue(), gridC ); + gridC.gridy++; + } + + + public void setStringAndReminder( String valueName, String s ) { + JTextField valueField = stringMap.get( valueName ); + if ( valueField != null ) valueField.setText(s); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setIntAndReminder( String valueName, int n ) { + setIntAndReminder( valueName, n, ""+n ); + } + public void setIntAndReminder( String valueName, int n, String s ) { + JTextField valueField = intMap.get( valueName ); + if ( valueField != null ) valueField.setText( ""+n ); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setBoolAndReminder( String valueName, boolean b ) { + setBoolAndReminder( valueName, b, ""+b ); + } + public void setBoolAndReminder( String valueName, boolean b, String s ) { + JCheckBox valueCheck = boolMap.get( valueName ); + if ( valueCheck != null ) valueCheck.setSelected(b); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setSliderAndReminder( String valueName, int n ) { + setSliderAndReminder( valueName, n, ""+n ); + } + public void setSliderAndReminder( String valueName, int n, String s ) { + JSlider valueSlider = sliderMap.get( valueName ); + if ( valueSlider != null ) valueSlider.setValue(n); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setComboAndReminder( String valueName, Object o ) { + setComboAndReminder( valueName, o, o.toString() ); + } + public void setComboAndReminder( String valueName, Object o, String s ) { + JComboBox valueCombo = comboMap.get( valueName ); + if ( valueCombo != null ) valueCombo.setSelectedItem(o); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setChooserAndReminder( String valueName, String s ) { + Chooser valueChooser = chooserMap.get( valueName ); + if ( valueChooser != null ) valueChooser.getTextField().setText(s); + if ( remindersVisible ) setReminder( valueName, s ); + } + + public void setReminder( String valueName, String s ) { + JLabel valueReminder = reminderMap.get( valueName ); + if ( valueReminder != null ) valueReminder.setText( "( "+ s +" )" ); + } + + public JTextArea getWrappedLabel( String valueName ) { + return wrappedLabelMap.get( valueName ); + } + + public JLabel getLabel( String valueName ) { + return labelMap.get( valueName ); + } + + public JTextField getString( String valueName ) { + return stringMap.get( valueName ); + } + + public JTextField getInt( String valueName ) { + return intMap.get( valueName ); + } + + public JCheckBox getBoolean( String valueName ) { + return boolMap.get( valueName ); + } + + public JSlider getSlider( String valueName ) { + return sliderMap.get( valueName ); + } + + public JComboBox getCombo( String valueName ) { + return comboMap.get( valueName ); + } + + public Chooser getChooser( String valueName ) { + return chooserMap.get( valueName ); + } + + + public void reset() { + for ( JTextArea valueArea : wrappedLabelMap.values() ) + valueArea.setText(""); + + for ( JLabel valueLbl : labelMap.values() ) + valueLbl.setText(""); + + for ( JTextField valueField : stringMap.values() ) + valueField.setText(""); + + for ( JTextField valueField : intMap.values() ) + valueField.setText(""); + + for ( JCheckBox valueCheck : boolMap.values() ) + valueCheck.setSelected(false); + + for ( JSlider valueSlider : sliderMap.values() ) + valueSlider.setValue(0); + + for ( JComboBox valueCombo : comboMap.values() ) + valueCombo.removeAllItems(); + + for ( Chooser valueChooser : chooserMap.values() ) + valueChooser.getTextField().setText(""); + + for ( JLabel valueReminder : reminderMap.values() ) + valueReminder.setText(""); + } + + @Override + public void removeAll() { + labelMap.clear(); + stringMap.clear(); + intMap.clear(); + boolMap.clear(); + sliderMap.clear(); + comboMap.clear(); + reminderMap.clear(); + super.removeAll(); + gridC = new GridBagConstraints(); + + gridC.anchor = GridBagConstraints.WEST; + gridC.fill = GridBagConstraints.HORIZONTAL; + gridC.weightx = 0.0; + gridC.weighty = 0.0; + gridC.gridwidth = 1; + gridC.gridx = 0; + gridC.gridy = 0; + + // No default width for col 0. + gridC.gridx = 0; + this.add( Box.createVerticalStrut(1), gridC ); + gridC.gridx++; + this.add( valueStrut, gridC ); + gridC.gridx++; + if ( remindersVisible ) { + this.add( reminderStrut, gridC ); + gridC.gridy++; + } + + gridC.insets = new Insets(2, 4, 2, 4); + } + + + + public static class Chooser { + private JTextField textField; + private JButton button; + + public Chooser( JTextField textField, JButton button ) { + this.textField = textField; + this.button = button; + } + + public JTextField getTextField() { return textField; } + public JButton getButton() { return button; } + } +} diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java index 298c169..1fb10a0 100644 --- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java +++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java @@ -84,6 +84,7 @@ import net.vhati.modmanager.ui.InertPanel; import net.vhati.modmanager.ui.ModInfoArea; import net.vhati.modmanager.ui.ModPatchDialog; import net.vhati.modmanager.ui.ModXMLSandbox; +import net.vhati.modmanager.ui.SlipstreamConfigDialog; import net.vhati.modmanager.ui.Statusbar; import net.vhati.modmanager.ui.StatusbarMouseListener; import net.vhati.modmanager.ui.TableRowTransferHandler; @@ -131,6 +132,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver private JMenuItem rescanMenuItem; private JMenuItem extractDatsMenuItem; private JMenuItem sandboxMenuItem; + private JMenuItem configMenuItem; private JMenuItem exitMenuItem; private JMenu helpMenu; private JMenuItem aboutMenuItem; @@ -296,6 +298,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver log.error( String.format( "Error writing config to \"%s\".", appConfig.getConfigFile() ), f ); } + System.gc(); // Ward off an intermittent InterruptedException from exit()? System.exit( 0 ); } }); @@ -367,6 +370,10 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver sandboxMenuItem.addMouseListener( new StatusbarMouseListener( this, "Experiment with advanced mod syntax." ) ); sandboxMenuItem.addActionListener(this); fileMenu.add( sandboxMenuItem ); + configMenuItem = new JMenuItem( "Preferences..." ); + configMenuItem.addMouseListener( new StatusbarMouseListener( this, "Edit preferences." ) ); + configMenuItem.addActionListener(this); + fileMenu.add( configMenuItem ); fileMenu.add( new JSeparator() ); exitMenuItem = new JMenuItem( "Exit" ); exitMenuItem.addMouseListener( new StatusbarMouseListener( this, "Exit this application." ) ); @@ -915,6 +922,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver extractDlg.setVisible( true ); } else if ( source == sandboxMenuItem ) { + setStatusText( "" ); File datsDir = new File( appConfig.getProperty( "ftl_dats_path" ) ); File dataDatFile = new File( datsDir, "data.dat" ); @@ -924,6 +932,15 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver sandboxFrame.setLocationRelativeTo( null ); sandboxFrame.setVisible( true ); } + else if ( source == configMenuItem ) { + setStatusText( "" ); + + SlipstreamConfigDialog configFrame = new SlipstreamConfigDialog( appConfig ); + configFrame.addWindowListener( nerfListener ); + //configFrame.setSize( 300, 400 ); + configFrame.setLocationRelativeTo( null ); + configFrame.setVisible( true ); + } else if ( source == exitMenuItem ) { setStatusText( "" ); exitApp(); @@ -995,6 +1012,11 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver + /** + * Toggles a main window's nerfed state as popups are opened/disposed. + * + * Requires: setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ). + */ private static class NerfListener extends WindowAdapter { private Nerfable nerfObj; @@ -1007,7 +1029,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver nerfObj.setNerfed( true ); } @Override - public void windowClosing( WindowEvent e ) { + public void windowClosed( WindowEvent e ) { nerfObj.setNerfed( false ); } } diff --git a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java index 6e1bc37..2a25251 100644 --- a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java +++ b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java @@ -93,6 +93,7 @@ public class ModXMLSandbox extends JFrame implements ActionListener { public ModXMLSandbox( File dataDatFile ) { super( "Mod XML Sandbox" ); + this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); this.dataDatFile = dataDatFile; diff --git a/src/main/java/net/vhati/modmanager/ui/RegexDocument.java b/src/main/java/net/vhati/modmanager/ui/RegexDocument.java new file mode 100644 index 0000000..100ed72 --- /dev/null +++ b/src/main/java/net/vhati/modmanager/ui/RegexDocument.java @@ -0,0 +1,62 @@ +package net.vhati.modmanager.ui; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import javax.swing.text.AttributeSet; +import javax.swing.text.BadLocationException; +import javax.swing.text.PlainDocument; + + +/** + * When applied to a JTextField via setDocument(), you can only enter a limited set of characters. + */ +public class RegexDocument extends PlainDocument { + private boolean dontCheck = false; + private Pattern p = null; + + + public RegexDocument( String regex ) { + if ( regex == null || regex.length()==0 ) dontCheck = true; + + try { + p = Pattern.compile(regex); + } catch (PatternSyntaxException e) {dontCheck = true;} + } + + public RegexDocument() { + dontCheck = true; + } + + + @Override + public void insertString( int offs, String str, AttributeSet a ) throws BadLocationException { + if ( str == null ) return; + + boolean proceed = true; + + if ( dontCheck == false ) { + String tmp = super.getText(0, offs) + str + (super.getLength()>offs ? super.getText(offs,super.getLength()-offs) : ""); + Matcher m = p.matcher(tmp); + proceed = m.matches(); + } + + if ( proceed == true ) super.insertString( offs, str, a ); + } + + + @Override + public void remove( int offs, int len ) throws BadLocationException { + boolean proceed = true; + + if ( dontCheck == false ) { + try { + String tmp = super.getText(0, offs) + (super.getLength()>(offs+len) ? super.getText(offs+len, super.getLength()-(offs+len)) : ""); + Matcher m = p.matcher(tmp); + proceed = m.matches(); + } catch (BadLocationException f) {} + } + + if ( proceed == true ) super.remove( offs, len ); + } +} diff --git a/src/main/java/net/vhati/modmanager/ui/SlipstreamConfigDialog.java b/src/main/java/net/vhati/modmanager/ui/SlipstreamConfigDialog.java new file mode 100644 index 0000000..99d43ed --- /dev/null +++ b/src/main/java/net/vhati/modmanager/ui/SlipstreamConfigDialog.java @@ -0,0 +1,171 @@ +package net.vhati.modmanager.ui; + +import java.awt.BorderLayout; +import java.awt.Dimension; +import java.awt.Point; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.event.AncestorEvent; +import javax.swing.event.AncestorListener; + +import net.vhati.modmanager.core.FTLUtilities; +import net.vhati.modmanager.core.SlipstreamConfig; +import net.vhati.modmanager.ui.FieldEditorPanel; +import net.vhati.modmanager.ui.FieldEditorPanel.ContentType; + + +public class SlipstreamConfigDialog extends JFrame implements ActionListener { + protected static final String ALLOW_ZIP = "allow_zip"; + protected static final String NEVER_RUN_FTL = "never_run_ftl"; + protected static final String USE_DEFAULT_UI = "use_default_ui"; + protected static final String REMEMBER_GEOMETRY = "remember_geometry"; + protected static final String UPDATE_CATALOG = "update_catalog"; + protected static final String UPDATE_APP = "update_app"; + protected static final String FTL_DATS_PATH = "ftl_dats_path"; + + protected SlipstreamConfig appConfig; + + protected FieldEditorPanel editorPanel; + protected JButton applyBtn; + + + public SlipstreamConfigDialog( SlipstreamConfig appConfig ) { + super( "Preferences..." ); + this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE ); + + this.appConfig = appConfig; + + editorPanel = new FieldEditorPanel( false ); + editorPanel.setBorder( BorderFactory.createEmptyBorder( 10, 10, 0, 10 ) ); + editorPanel.setNameWidth( 250 ); + editorPanel.addRow( ALLOW_ZIP, ContentType.BOOLEAN ); + editorPanel.addTextRow( "Treat .zip files as .ftl files." ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( NEVER_RUN_FTL, ContentType.BOOLEAN ); + editorPanel.addTextRow( "Don't offer to run FTL after patching." ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( USE_DEFAULT_UI, ContentType.BOOLEAN ); + editorPanel.addTextRow( "Don't attempt to resemble a native GUI." ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( REMEMBER_GEOMETRY, ContentType.BOOLEAN ); + editorPanel.addTextRow( "Save window geometry on exit." ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( UPDATE_CATALOG, ContentType.INTEGER ); + editorPanel.addTextRow( "Check for new mod descriptions every N days. (0 to disable)" ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( UPDATE_APP, ContentType.INTEGER ); + editorPanel.addTextRow( "Check for newer app versions every N days. (0 to disable)" ); + editorPanel.addSeparatorRow(); + editorPanel.addRow( FTL_DATS_PATH, ContentType.CHOOSER ); + editorPanel.addTextRow( "Path to FTL's resources folder." ); + editorPanel.addSeparatorRow(); + editorPanel.addBlankRow(); + editorPanel.addTextRow( "Note: Some changes may have no immediate effect." ); + editorPanel.addBlankRow(); + editorPanel.addFillRow(); + + editorPanel.getBoolean( ALLOW_ZIP ).setSelected( appConfig.getProperty( ALLOW_ZIP, "false" ).equals( "true" ) ); + editorPanel.getBoolean( NEVER_RUN_FTL ).setSelected( appConfig.getProperty( NEVER_RUN_FTL, "false" ).equals( "true" ) ); + editorPanel.getBoolean( USE_DEFAULT_UI ).setSelected( appConfig.getProperty( USE_DEFAULT_UI, "false" ).equals( "true" ) ); + editorPanel.getBoolean( REMEMBER_GEOMETRY ).setSelected( appConfig.getProperty( REMEMBER_GEOMETRY, "true" ).equals( "true" ) ); + editorPanel.getInt( UPDATE_CATALOG ).setText( Integer.toString(appConfig.getPropertyAsInt( UPDATE_CATALOG, 0 )) ); + editorPanel.getInt( UPDATE_APP ).setText( Integer.toString(appConfig.getPropertyAsInt( UPDATE_APP, 0 )) ); + + JTextField ftlDatsPathField = editorPanel.getChooser( FTL_DATS_PATH ).getTextField(); + ftlDatsPathField.setText( appConfig.getProperty( FTL_DATS_PATH, "" ) ); + ftlDatsPathField.setPreferredSize( new Dimension(150, ftlDatsPathField.getPreferredSize().height) ); + editorPanel.getChooser( FTL_DATS_PATH ).getButton().addActionListener(this); + + JPanel ctrlPanel = new JPanel(); + ctrlPanel.setLayout( new BoxLayout( ctrlPanel, BoxLayout.X_AXIS ) ); + ctrlPanel.setBorder( BorderFactory.createEmptyBorder( 10, 0, 10, 0 ) ); + ctrlPanel.add( Box.createHorizontalGlue() ); + applyBtn = new JButton( "Apply" ); + applyBtn.addActionListener(this); + ctrlPanel.add( applyBtn ); + ctrlPanel.add( Box.createHorizontalGlue() ); + + final JScrollPane editorScroll = new JScrollPane( editorPanel ); + editorScroll.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS ); + editorScroll.getVerticalScrollBar().setUnitIncrement( 10 ); + int vbarWidth = editorScroll.getVerticalScrollBar().getPreferredSize().width; + editorScroll.setPreferredSize( new Dimension(editorPanel.getPreferredSize().width+vbarWidth+5, 400) ); + + JPanel contentPane = new JPanel( new BorderLayout() ); + contentPane.add( editorScroll, BorderLayout.CENTER ); + contentPane.add( ctrlPanel, BorderLayout.SOUTH ); + this.setContentPane( contentPane ); + this.pack(); + this.setMinimumSize( new Dimension(250, 250) ); + + + editorScroll.addAncestorListener(new AncestorListener() { + @Override + public void ancestorAdded( AncestorEvent e ) { + editorScroll.getViewport().setViewPosition( new Point(0, 0) ); + } + @Override + public void ancestorMoved( AncestorEvent e ) { + } + @Override + public void ancestorRemoved( AncestorEvent e ) { + } + }); + } + + + @Override + public void actionPerformed( ActionEvent e ) { + Object source = e.getSource(); + + if ( source == applyBtn ) { + String tmp; + appConfig.setProperty( ALLOW_ZIP, editorPanel.getBoolean( ALLOW_ZIP ).isSelected() ? "true" : "false" ); + appConfig.setProperty( NEVER_RUN_FTL, editorPanel.getBoolean( NEVER_RUN_FTL ).isSelected() ? "true" : "false" ); + appConfig.setProperty( USE_DEFAULT_UI, editorPanel.getBoolean( USE_DEFAULT_UI ).isSelected() ? "true" : "false" ); + appConfig.setProperty( REMEMBER_GEOMETRY, editorPanel.getBoolean( REMEMBER_GEOMETRY ).isSelected() ? "true" : "false" ); + + tmp = editorPanel.getInt( UPDATE_CATALOG ).getText(); + try { + int n = Integer.parseInt( tmp ); + n = Math.max( 0, n ); + appConfig.setProperty( UPDATE_CATALOG, Integer.toString( n ) ); + } + catch ( NumberFormatException f ) {} + + tmp = editorPanel.getInt( UPDATE_APP ).getText(); + try { + int n = Integer.parseInt( tmp ); + n = Math.max( 0, n ); + appConfig.setProperty( UPDATE_APP, Integer.toString( n ) ); + } + catch ( NumberFormatException f ) {} + + tmp = editorPanel.getChooser( FTL_DATS_PATH ).getTextField().getText(); + if ( tmp.length() > 0 && FTLUtilities.isDatsDirValid( new File(tmp) ) ) { + appConfig.setProperty( FTL_DATS_PATH, tmp ); + } + + this.setVisible( false ); + this.dispose(); + } + else if ( source == editorPanel.getChooser( FTL_DATS_PATH ).getButton() ) { + File datsDir = FTLUtilities.promptForDatsDir( this ); + if ( datsDir != null ) { + editorPanel.getChooser( FTL_DATS_PATH ).getTextField().setText( datsDir.getAbsolutePath() ); + } + } + } +}