diff --git a/skel_common/readme_changelog.txt b/skel_common/readme_changelog.txt index dd58813..77ed9cc 100644 --- a/skel_common/readme_changelog.txt +++ b/skel_common/readme_changelog.txt @@ -5,6 +5,7 @@ Changelog - Incorporated strict-then-sloppy XML parsing into the patch process - Added XML Sandbox for syntax tinkering - Added scrollbars to progress popups to show long error messages +- The main window's geometry is saved on exit 1.1: - Added a button to open the mods/ folder diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java index e8a747c..29261c2 100644 --- a/src/main/java/net/vhati/modmanager/FTLModManager.java +++ b/src/main/java/net/vhati/modmanager/FTLModManager.java @@ -2,12 +2,9 @@ package net.vhati.modmanager; import java.io.File; import java.io.FileInputStream; -import java.io.FileOutputStream; import java.io.InputStream; import java.io.InputStreamReader; import java.io.IOException; -import java.io.OutputStream; -import java.io.OutputStreamWriter; import java.util.Properties; import javax.swing.JOptionPane; import javax.swing.SwingUtilities; @@ -16,6 +13,7 @@ import javax.swing.UIManager; import net.vhati.modmanager.cli.SlipstreamCLI; import net.vhati.modmanager.core.ComparableVersion; import net.vhati.modmanager.core.FTLUtilities; +import net.vhati.modmanager.core.SlipstreamConfig; import net.vhati.modmanager.ui.ManagerFrame; import org.apache.logging.log4j.LogManager; @@ -144,51 +142,36 @@ public class FTLModManager { } + final SlipstreamConfig appConfig = new SlipstreamConfig( config, configFile ); if ( writeConfig ) { - OutputStream out = null; try { - out = new FileOutputStream( configFile ); - String configComments = ""; - configComments += "\n"; - configComments += " allow_zip - Sets whether to treat .zip files as .ftl files. Default: false.\n"; - 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 true, periodically download descriptions for the latest mods. If invalid, you'll be prompted.\n"; - configComments += " use_default_ui - If true, no attempt will be made to resemble a native GUI. Default: false.\n"; - - OutputStreamWriter writer = new OutputStreamWriter( out, "UTF-8" ); - config.store( writer, configComments ); - writer.flush(); + appConfig.writeConfig(); } catch ( IOException e ) { - log.error( "Error saving config to "+ configFile.getPath(), e ); - showErrorDialog( "Error saving config to "+ configFile.getPath() ); - } - finally { - try {if ( out != null ) out.close();} - catch ( IOException e ) {} + String errorMsg = String.format( "Error writing config to \"%s\".", configFile.getPath() ); + log.error( errorMsg, e ); + showErrorDialog( errorMsg ); } } // Create the GUI. - try { - final ManagerFrame frame = new ManagerFrame( config, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR ); - frame.setVisible(true); - - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + try { + ManagerFrame frame = new ManagerFrame( appConfig, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR ); frame.init(); + frame.setVisible(true); + } catch ( Exception e ) { + log.error( "Exception while creating ManagerFrame.", e ); + System.exit(1); } - }); - } - catch ( Exception e ) { - log.error( "Exception while creating ManagerFrame.", e ); - System.exit(1); - } + } + }); } + private static void showErrorDialog( String message ) { JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE); } diff --git a/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java new file mode 100644 index 0000000..9d1fe52 --- /dev/null +++ b/src/main/java/net/vhati/modmanager/core/SlipstreamConfig.java @@ -0,0 +1,64 @@ +package net.vhati.modmanager.core; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.util.Properties; + + +public class SlipstreamConfig { + + private Properties config; + private File configFile; + + + public SlipstreamConfig( Properties config, File configFile ) { + this.config = config; + this.configFile = configFile; + } + + + public Properties getConfig() { return config; } + + public File getConfigFile() { return configFile; } + + + public Object setProperty( String key, String value ) { + return config.setProperty( key, value ); + } + + public String getProperty( String key, String defaultValue ) { + return config.getProperty( key, defaultValue ); + } + + public String getProperty( String key ) { + return config.getProperty( key ); + } + + + public void writeConfig() throws IOException { + + OutputStream out = null; + try { + out = new FileOutputStream( configFile ); + String configComments = ""; + configComments += "\n"; + configComments += " allow_zip - Sets whether to treat .zip files as .ftl files. Default: false.\n"; + 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 true, periodically download descriptions for the latest mods. If invalid, you'll be prompted.\n"; + configComments += " use_default_ui - If true, no attempt will be made to resemble a native GUI. Default: false.\n"; + configComments += " manager_geometry - Saved position/size/etc of the main window.\n"; + + OutputStreamWriter writer = new OutputStreamWriter( out, "UTF-8" ); + config.store( writer, configComments ); + writer.flush(); + } + finally { + try {if ( out != null ) out.close();} + catch ( IOException e ) {} + } + } +} diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java index 3d81135..6543e67 100644 --- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java +++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java @@ -5,6 +5,7 @@ import java.awt.Component; import java.awt.Desktop; import java.awt.Dimension; import java.awt.Insets; +import java.awt.Rectangle; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; @@ -32,7 +33,8 @@ import java.util.HashMap; import java.util.ArrayList; import java.util.Iterator; import java.util.List; -import java.util.Properties; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import javax.swing.BorderFactory; import javax.swing.BoxLayout; import javax.swing.DropMode; @@ -69,6 +71,7 @@ import net.vhati.modmanager.core.ModPatchThread.BackedUpDat; import net.vhati.modmanager.core.ModUtilities; import net.vhati.modmanager.core.Report; import net.vhati.modmanager.core.Report.ReportFormatter; +import net.vhati.modmanager.core.SlipstreamConfig; import net.vhati.modmanager.json.GrognakCatalogFetcher; import net.vhati.modmanager.json.JacksonGrognakCatalogReader; import net.vhati.modmanager.ui.ChecklistTableModel; @@ -95,7 +98,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver private File catalogFile = new File( backupDir, "current_catalog.json" ); private File catalogETagFile = new File( backupDir, "current_catalog_etag.txt" ); - private Properties config; + private SlipstreamConfig appConfig; private String appName; private ComparableVersion appVersion; private String appURL; @@ -122,15 +125,15 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver private JButton toggleAllBtn; private JButton validateBtn; private JButton modsFolderBtn; - + private JSplitPane splitPane; private ModInfoArea infoArea; private JLabel statusLbl; - public ManagerFrame( Properties config, String appName, ComparableVersion appVersion, String appURL, String appAuthor ) { + public ManagerFrame( SlipstreamConfig appConfig, String appName, ComparableVersion appVersion, String appURL, String appAuthor ) { super(); - this.config = config; + this.appConfig = appConfig; this.appName = appName; this.appVersion = appVersion; this.appURL = appURL; @@ -211,7 +214,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver infoArea = new ModInfoArea(); infoArea.setPreferredSize( new Dimension(504, 220) ); - JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT ); + splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT ); splitPane.setTopComponent( topPanel ); splitPane.setBottomComponent( infoArea ); mainPane.add( splitPane, BorderLayout.CENTER ); @@ -236,6 +239,22 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver } saveModOrder( sortedMods ); + SlipstreamConfig appConfig = ManagerFrame.this.appConfig; + + if ( ManagerFrame.this.getExtendedState() == JFrame.NORMAL ) { + Rectangle managerBounds = ManagerFrame.this.getBounds(); + int dividerLoc = splitPane.getDividerLocation(); + String geometry = String.format( "x,%d;y,%d;w,%d;h,%d;divider,%d", managerBounds.x, managerBounds.y, managerBounds.width, managerBounds.height, dividerLoc ); + appConfig.setProperty( "manager_geometry", geometry ); + } + + try { + appConfig.writeConfig(); + } + catch ( IOException f ) { + log.error( String.format( "Error writing config to \"%s\".", appConfig.getConfigFile() ), f ); + } + System.exit( 0 ); } }); @@ -329,9 +348,44 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver this.setMinimumSize( new Dimension( 300, modActionsPanel.getPreferredSize().height+90 ) ); this.setLocationRelativeTo(null); + setGeometryFromConfig(); + showAboutInfo(); } + private void setGeometryFromConfig() { + String geometry = appConfig.getProperty( "manager_geometry" ); + if ( geometry != null ) { + int[] xywh = new int[4]; + int dividerLoc = -1; + Matcher m = Pattern.compile( "([^;,]+),(\\d+)" ).matcher( geometry ); + while ( m.find() ) { + if ( m.group(1).equals( "x" ) ) + xywh[0] = Integer.parseInt( m.group(2) ); + else if ( m.group(1).equals( "y" ) ) + xywh[1] = Integer.parseInt( m.group(2) ); + else if ( m.group(1).equals( "w" ) ) + xywh[2] = Integer.parseInt( m.group(2) ); + else if ( m.group(1).equals( "h" ) ) + xywh[3] = Integer.parseInt( m.group(2) ); + else if ( m.group(1).equals( "divider" ) ) + dividerLoc = Integer.parseInt( m.group(2) ); + } + boolean badGeometry = false; + for ( int n : xywh ) { + if ( n <= 0 ) { + badGeometry = true; + break; + } + } + if ( !badGeometry && dividerLoc > 0 ) { + Rectangle newBounds = new Rectangle( xywh[0], xywh[1], xywh[2], xywh[3] ); + ManagerFrame.this.setBounds( newBounds ); + splitPane.setDividerLocation( dividerLoc ); + } + } + } + /** * Extra initialization that must be called after the constructor. * This must be called on the Swing event thread (use invokeLater()). @@ -365,7 +419,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver } // Don't update if the user doesn't want to. - String updatesAllowed = config.getProperty( "update_catalog", "false" ); + String updatesAllowed = appConfig.getProperty( "update_catalog", "false" ); if ( !updatesAllowed.equals("true") ) needNewCatalog = false; if ( needNewCatalog ) { @@ -487,7 +541,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver modFileHashes.clear(); localModsTableModel.removeAllItems(); - boolean allowZip = config.getProperty( "allow_zip", "false" ).equals( "true" ); + boolean allowZip = appConfig.getProperty( "allow_zip", "false" ).equals( "true" ); File[] modFiles = modsDir.listFiles( new ModFileFilter( allowZip ) ); List unsortedMods = new ArrayList(); @@ -585,7 +639,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver } } - File datsDir = new File( config.getProperty( "ftl_dats_path" ) ); + File datsDir = new File( appConfig.getProperty( "ftl_dats_path" ) ); BackedUpDat dataDat = new BackedUpDat(); dataDat.datFile = new File( datsDir, "data.dat" ); @@ -683,7 +737,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver File extractDir = extractChooser.getSelectedFile(); - File datsDir = new File( config.getProperty( "ftl_dats_path" ) ); + File datsDir = new File( appConfig.getProperty( "ftl_dats_path" ) ); File dataDatFile = new File( datsDir, "data.dat" ); File resDatFile = new File( datsDir, "resource.dat" ); File[] datFiles = new File[] {dataDatFile, resDatFile}; @@ -693,7 +747,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver extractDlg.setVisible( true ); } else if ( source == sandboxMenuItem ) { - File datsDir = new File( config.getProperty( "ftl_dats_path" ) ); + File datsDir = new File( appConfig.getProperty( "ftl_dats_path" ) ); File dataDatFile = new File( datsDir, "data.dat" ); ModXMLSandbox sandboxFrame = new ModXMLSandbox( dataDatFile ); @@ -738,9 +792,9 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver private class SpawnGameTask implements Runnable { @Override public void run() { - String neverRunFtl = config.getProperty( "never_run_ftl", "false" ); + String neverRunFtl = appConfig.getProperty( "never_run_ftl", "false" ); if ( !neverRunFtl.equals("true") ) { - File datsDir = new File( config.getProperty( "ftl_dats_path" ) ); + File datsDir = new File( appConfig.getProperty( "ftl_dats_path" ) ); File exeFile = FTLUtilities.findGameExe( datsDir ); if ( exeFile != null ) {