Added saved window geometry

This commit is contained in:
Vhati 2013-09-03 23:38:02 -04:00
parent c88ecf0bc8
commit 35ae7bacac
4 changed files with 150 additions and 48 deletions

View file

@ -5,6 +5,7 @@ Changelog
- Incorporated strict-then-sloppy XML parsing into the patch process - Incorporated strict-then-sloppy XML parsing into the patch process
- Added XML Sandbox for syntax tinkering - Added XML Sandbox for syntax tinkering
- Added scrollbars to progress popups to show long error messages - Added scrollbars to progress popups to show long error messages
- The main window's geometry is saved on exit
1.1: 1.1:
- Added a button to open the mods/ folder - Added a button to open the mods/ folder

View file

@ -2,12 +2,9 @@ package net.vhati.modmanager;
import java.io.File; import java.io.File;
import java.io.FileInputStream; import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream; import java.io.InputStream;
import java.io.InputStreamReader; import java.io.InputStreamReader;
import java.io.IOException; import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.util.Properties; import java.util.Properties;
import javax.swing.JOptionPane; import javax.swing.JOptionPane;
import javax.swing.SwingUtilities; import javax.swing.SwingUtilities;
@ -16,6 +13,7 @@ import javax.swing.UIManager;
import net.vhati.modmanager.cli.SlipstreamCLI; import net.vhati.modmanager.cli.SlipstreamCLI;
import net.vhati.modmanager.core.ComparableVersion; import net.vhati.modmanager.core.ComparableVersion;
import net.vhati.modmanager.core.FTLUtilities; import net.vhati.modmanager.core.FTLUtilities;
import net.vhati.modmanager.core.SlipstreamConfig;
import net.vhati.modmanager.ui.ManagerFrame; import net.vhati.modmanager.ui.ManagerFrame;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
@ -144,51 +142,36 @@ public class FTLModManager {
} }
final SlipstreamConfig appConfig = new SlipstreamConfig( config, configFile );
if ( writeConfig ) { if ( writeConfig ) {
OutputStream out = null;
try { try {
out = new FileOutputStream( configFile ); appConfig.writeConfig();
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();
} }
catch ( IOException e ) { catch ( IOException e ) {
log.error( "Error saving config to "+ configFile.getPath(), e ); String errorMsg = String.format( "Error writing config to \"%s\".", configFile.getPath() );
showErrorDialog( "Error saving config to "+ configFile.getPath() ); log.error( errorMsg, e );
} showErrorDialog( errorMsg );
finally {
try {if ( out != null ) out.close();}
catch ( IOException e ) {}
} }
} }
// Create the GUI. // Create the GUI.
try { SwingUtilities.invokeLater(new Runnable() {
final ManagerFrame frame = new ManagerFrame( config, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR ); @Override
frame.setVisible(true); public void run() {
try {
SwingUtilities.invokeLater(new Runnable() { ManagerFrame frame = new ManagerFrame( appConfig, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR );
@Override
public void run() {
frame.init(); 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 ) { private static void showErrorDialog( String message ) {
JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE); JOptionPane.showMessageDialog(null, message, "Error", JOptionPane.ERROR_MESSAGE);
} }

View file

@ -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 ) {}
}
}
}

View file

@ -5,6 +5,7 @@ import java.awt.Component;
import java.awt.Desktop; import java.awt.Desktop;
import java.awt.Dimension; import java.awt.Dimension;
import java.awt.Insets; import java.awt.Insets;
import java.awt.Rectangle;
import java.awt.event.ActionEvent; import java.awt.event.ActionEvent;
import java.awt.event.ActionListener; import java.awt.event.ActionListener;
import java.awt.event.KeyEvent; import java.awt.event.KeyEvent;
@ -32,7 +33,8 @@ import java.util.HashMap;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; 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.BorderFactory;
import javax.swing.BoxLayout; import javax.swing.BoxLayout;
import javax.swing.DropMode; 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.ModUtilities;
import net.vhati.modmanager.core.Report; import net.vhati.modmanager.core.Report;
import net.vhati.modmanager.core.Report.ReportFormatter; import net.vhati.modmanager.core.Report.ReportFormatter;
import net.vhati.modmanager.core.SlipstreamConfig;
import net.vhati.modmanager.json.GrognakCatalogFetcher; import net.vhati.modmanager.json.GrognakCatalogFetcher;
import net.vhati.modmanager.json.JacksonGrognakCatalogReader; import net.vhati.modmanager.json.JacksonGrognakCatalogReader;
import net.vhati.modmanager.ui.ChecklistTableModel; 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 catalogFile = new File( backupDir, "current_catalog.json" );
private File catalogETagFile = new File( backupDir, "current_catalog_etag.txt" ); private File catalogETagFile = new File( backupDir, "current_catalog_etag.txt" );
private Properties config; private SlipstreamConfig appConfig;
private String appName; private String appName;
private ComparableVersion appVersion; private ComparableVersion appVersion;
private String appURL; private String appURL;
@ -122,15 +125,15 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
private JButton toggleAllBtn; private JButton toggleAllBtn;
private JButton validateBtn; private JButton validateBtn;
private JButton modsFolderBtn; private JButton modsFolderBtn;
private JSplitPane splitPane;
private ModInfoArea infoArea; private ModInfoArea infoArea;
private JLabel statusLbl; 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(); super();
this.config = config; this.appConfig = appConfig;
this.appName = appName; this.appName = appName;
this.appVersion = appVersion; this.appVersion = appVersion;
this.appURL = appURL; this.appURL = appURL;
@ -211,7 +214,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
infoArea = new ModInfoArea(); infoArea = new ModInfoArea();
infoArea.setPreferredSize( new Dimension(504, 220) ); infoArea.setPreferredSize( new Dimension(504, 220) );
JSplitPane splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT ); splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
splitPane.setTopComponent( topPanel ); splitPane.setTopComponent( topPanel );
splitPane.setBottomComponent( infoArea ); splitPane.setBottomComponent( infoArea );
mainPane.add( splitPane, BorderLayout.CENTER ); mainPane.add( splitPane, BorderLayout.CENTER );
@ -236,6 +239,22 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
} }
saveModOrder( sortedMods ); 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 ); 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.setMinimumSize( new Dimension( 300, modActionsPanel.getPreferredSize().height+90 ) );
this.setLocationRelativeTo(null); this.setLocationRelativeTo(null);
setGeometryFromConfig();
showAboutInfo(); 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. * Extra initialization that must be called after the constructor.
* This must be called on the Swing event thread (use invokeLater()). * 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. // 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 ( !updatesAllowed.equals("true") ) needNewCatalog = false;
if ( needNewCatalog ) { if ( needNewCatalog ) {
@ -487,7 +541,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
modFileHashes.clear(); modFileHashes.clear();
localModsTableModel.removeAllItems(); 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 ) ); File[] modFiles = modsDir.listFiles( new ModFileFilter( allowZip ) );
List<ModFileInfo> unsortedMods = new ArrayList<ModFileInfo>(); List<ModFileInfo> unsortedMods = new ArrayList<ModFileInfo>();
@ -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(); BackedUpDat dataDat = new BackedUpDat();
dataDat.datFile = new File( datsDir, "data.dat" ); dataDat.datFile = new File( datsDir, "data.dat" );
@ -683,7 +737,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
File extractDir = extractChooser.getSelectedFile(); 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 dataDatFile = new File( datsDir, "data.dat" );
File resDatFile = new File( datsDir, "resource.dat" ); File resDatFile = new File( datsDir, "resource.dat" );
File[] datFiles = new File[] {dataDatFile, resDatFile}; File[] datFiles = new File[] {dataDatFile, resDatFile};
@ -693,7 +747,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
extractDlg.setVisible( true ); extractDlg.setVisible( true );
} }
else if ( source == sandboxMenuItem ) { 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" ); File dataDatFile = new File( datsDir, "data.dat" );
ModXMLSandbox sandboxFrame = new ModXMLSandbox( dataDatFile ); ModXMLSandbox sandboxFrame = new ModXMLSandbox( dataDatFile );
@ -738,9 +792,9 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver
private class SpawnGameTask implements Runnable { private class SpawnGameTask implements Runnable {
@Override @Override
public void run() { public void run() {
String neverRunFtl = config.getProperty( "never_run_ftl", "false" ); String neverRunFtl = appConfig.getProperty( "never_run_ftl", "false" );
if ( !neverRunFtl.equals("true") ) { 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 ); File exeFile = FTLUtilities.findGameExe( datsDir );
if ( exeFile != null ) { if ( exeFile != null ) {