diff --git a/pom.xml b/pom.xml
index 53264db..9cbb927 100644
--- a/pom.xml
+++ b/pom.xml
@@ -67,7 +67,7 @@
info.picocli
picocli
- 2.2.0
+ 4.7.7
diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java
index c431262..edc78a1 100644
--- a/src/main/java/net/vhati/modmanager/FTLModManager.java
+++ b/src/main/java/net/vhati/modmanager/FTLModManager.java
@@ -1,14 +1,8 @@
package net.vhati.modmanager;
import java.io.File;
-import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Date;
-import javax.swing.JFileChooser;
-import javax.swing.JOptionPane;
-import javax.swing.LookAndFeel;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
import ch.qos.logback.classic.LoggerContext;
import ch.qos.logback.classic.spi.ILoggingEvent;
@@ -20,22 +14,16 @@ import org.slf4j.bridge.SLF4JBridgeHandler;
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;
public class FTLModManager {
- private static final Logger log = LoggerFactory.getLogger( FTLModManager.class );
+ private static final Logger log = LoggerFactory.getLogger(FTLModManager.class);
public static final String APP_NAME = "Slipstream Mod Manager";
- public static final ComparableVersion APP_VERSION = new ComparableVersion( "1.9.1" );
- public static final String APP_URL = "TODO";
- public static final String APP_AUTHOR = "jan-leila";
+ public static final ComparableVersion APP_VERSION = new ComparableVersion("1.9.1");
-
- public static void main( String[] args ) {
+ public static void main(String[] args) {
// Redirect any libraries' java.util.Logging messages.
SLF4JBridgeHandler.removeHandlersForRootLogger();
SLF4JBridgeHandler.install();
@@ -47,299 +35,29 @@ public class FTLModManager {
LoggerContext lc = (LoggerContext)LoggerFactory.getILoggerFactory();
PatternLayoutEncoder encoder = new PatternLayoutEncoder();
- encoder.setContext( lc );
+ encoder.setContext(lc);
encoder.setCharset(StandardCharsets.UTF_8);
- encoder.setPattern( "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" );
+ encoder.setPattern("%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n");
encoder.start();
- FileAppender fileAppender = new FileAppender();
- fileAppender.setContext( lc );
- fileAppender.setName( "LogFile" );
- fileAppender.setFile( new File( "./modman-log.txt" ).getAbsolutePath() );
- fileAppender.setAppend( false );
- fileAppender.setEncoder( encoder );
+ FileAppender fileAppender = new FileAppender<>();
+ fileAppender.setContext(lc);
+ fileAppender.setName("LogFile");
+ fileAppender.setFile(new File("./modman-log.txt").getAbsolutePath());
+ fileAppender.setAppend(false);
+ fileAppender.setEncoder(encoder);
fileAppender.start();
- lc.getLogger( Logger.ROOT_LOGGER_NAME ).addAppender( fileAppender );
+ lc.getLogger(Logger.ROOT_LOGGER_NAME).addAppender(fileAppender);
// Log a welcome message.
- log.debug( "Started: {}", new Date() );
- log.debug( "{} v{}", APP_NAME, APP_VERSION );
- log.debug( "OS: {} {}", System.getProperty( "os.name" ), System.getProperty( "os.version" ) );
- log.debug( "VM: {}, {}, {}", System.getProperty( "java.vm.name" ), System.getProperty( "java.version" ), System.getProperty( "os.arch" ) );
+ log.debug("Started: {}", new Date());
+ log.debug("{} v{}", APP_NAME, APP_VERSION);
+ log.debug("OS: {} {}", System.getProperty("os.name"), System.getProperty("os.version"));
+ log.debug("VM: {}, {}, {}", System.getProperty("java.vm.name"), System.getProperty("java.version"), System.getProperty("os.arch"));
- Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException( Thread t, Throwable e ) {
- log.error("Uncaught exception in thread: {}", t.toString(), e);
- }
- });
+ Thread.setDefaultUncaughtExceptionHandler((t, e) -> log.error("Uncaught exception in thread: {}", t.toString(), e));
- if ( args.length > 0 ) {
- SlipstreamCLI.main( args );
- return;
- }
-
- // Ensure all popups are triggered from the event dispatch thread.
- SwingUtilities.invokeLater(FTLModManager::guiInit);
- }
-
-
- private static void guiInit() {
- try {
- // TODO: get mods file from env var
- // Nag if the jar was double-clicked.
- if (!new File("./mods/").exists()) {
- String currentPath = new File( "." ).getAbsoluteFile().getParentFile().getAbsolutePath();
-
- log.error( String.format( "Slipstream could not find its own folder (Currently in \"%s\"), exiting...", currentPath ) );
- showErrorDialog( String.format( "Slipstream could not find its own folder.\nCurrently in: %s\n\nRun one of the following instead of the jar...\nWindows: modman.exe or modman_admin.exe\nLinux/OSX: modman.command or modman-cli.sh\n\nSlipstream will now exit.", currentPath ) );
-
- throw new ExitException();
- }
-
- // TODO: get config file from env var
- File configFile = new File( "modman.cfg" );
-
- SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
-
- // Look-and-Feel.
- boolean useDefaultUI = Boolean.parseBoolean(appConfig.getProperty(SlipstreamConfig.USE_DEFAULT_UI, "false"));
-
- if ( !useDefaultUI ) {
- LookAndFeel defaultLaf = UIManager.getLookAndFeel();
- log.debug( "Default look and feel is: "+ defaultLaf.getName() );
-
- try {
- log.debug( "Setting system look and feel: "+ UIManager.getSystemLookAndFeelClassName() );
-
- // SystemLaf is risky. It may throw an exception, or lead to graphical bugs.
- // Problems are generally caused by custom Windows themes.
- UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
- }
- catch ( Exception e ) {
- log.error( "Failed to set system look and feel", e );
- log.info( "Setting "+ SlipstreamConfig.USE_DEFAULT_UI +"=true in the config file to prevent this error..." );
-
- appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, "true" );
-
- try {
- UIManager.setLookAndFeel( defaultLaf );
- }
- catch ( Exception f ) {
- log.error( "Error returning to the default look and feel after failing to set system look and feel", f );
-
- // Write an emergency config and exit.
- try {
- appConfig.writeConfig();
- }
- catch ( IOException g ) {
- log.error( String.format( "Error writing config to \"%s\"", configFile.getPath(), g ) );
- }
-
- throw new ExitException();
- }
- }
- }
- else {
- log.debug( "Using default Look and Feel" );
- }
-
- // FTL Resources Path.
- File datsDir = null;
- String datsPath = appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH, "" );
-
- if ( datsPath.length() > 0 ) {
- log.info( "Using FTL dats path from config: "+ datsPath );
- datsDir = new File( datsPath );
- if ( FTLUtilities.isDatsDirValid( datsDir ) == false ) {
- log.error( "The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid" );
- datsDir = null;
- }
- }
- else {
- log.debug( "No "+ SlipstreamConfig.FTL_DATS_PATH +" previously set" );
- }
-
- // Find/prompt for the path to set in the config.
- if ( datsDir == null ) {
- datsDir = FTLUtilities.findDatsDir();
- if ( datsDir != null ) {
- int response = JOptionPane.showConfirmDialog( null, "FTL resources were found in:\n"+ datsDir.getPath() +"\nIs this correct?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.NO_OPTION ) datsDir = null;
- }
-
- if ( datsDir == null ) {
- log.debug( "FTL dats path was not located automatically. Prompting user for location" );
- datsDir = FTLUtilities.promptForDatsDir( null );
- }
-
- if ( datsDir != null ) {
- appConfig.setProperty( SlipstreamConfig.FTL_DATS_PATH, datsDir.getAbsolutePath() );
- log.info( "FTL dats located at: "+ datsDir.getAbsolutePath() );
- }
- }
-
- if ( datsDir == null ) {
- showErrorDialog( "FTL resources were not found.\nSlipstream will now exit." );
- log.debug( "No FTL dats path found, exiting" );
-
- throw new ExitException();
- }
-
- // Ask about Steam.
- if ( appConfig.getProperty( SlipstreamConfig.STEAM_DISTRO, "" ).length() == 0 ) {
- int steamBasedResponse = JOptionPane.showConfirmDialog( null, "Was FTL installed via Steam?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( steamBasedResponse == JOptionPane.YES_OPTION ) {
- appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "true" );
- }
- else {
- appConfig.setProperty( SlipstreamConfig.STEAM_DISTRO, "false" );
- }
- }
-
- // If this is a Steam distro.
- if ( "true".equals( appConfig.getProperty( SlipstreamConfig.STEAM_DISTRO, "false" ) ) ) {
-
- // Find Steam's executable.
- if ( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ).length() == 0 ) {
-
- File steamExeFile = FTLUtilities.findSteamExe();
-
- if ( steamExeFile == null && System.getProperty( "os.name" ).startsWith( "Windows" ) ) {
- try {
- String registryExePath = FTLUtilities.queryRegistryKey( "HKCU\\Software\\Valve\\Steam", "SteamExe", "REG_SZ" );
- if ( registryExePath != null && !(steamExeFile=new File( registryExePath )).exists() ) {
- steamExeFile = null;
- }
- }
- catch( IOException e ) {
- log.error( "Error while querying registry for Steam's path", e );
- }
- }
-
- if ( steamExeFile != null ) {
- int response = JOptionPane.showConfirmDialog( null, "Steam was found at:\n"+ steamExeFile.getPath() +"\nIs this correct?", "Confirm", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.NO_OPTION ) steamExeFile = null;
- }
-
- if ( steamExeFile == null ) {
- log.debug( "Steam was not located automatically. Prompting user for location" );
-
- String steamPrompt = ""
- + "You will be prompted to locate Steam's executable.\n"
- + "- Windows: Steam.exe\n"
- + "- Linux: steam\n"
- + "- OSX: Steam.app\n"
- + "\n"
- + "If you can't find it, you can cancel and set it later.";
- JOptionPane.showMessageDialog( null, steamPrompt, "Find Steam", JOptionPane.INFORMATION_MESSAGE );
-
- JFileChooser steamExeChooser = new JFileChooser();
- steamExeChooser.setDialogTitle( "Find Steam.exe or steam or Steam.app" );
- steamExeChooser.setFileHidingEnabled( false );
- steamExeChooser.setMultiSelectionEnabled( false );
-
- if ( steamExeChooser.showOpenDialog( null ) == JFileChooser.APPROVE_OPTION ) {
- steamExeFile = steamExeChooser.getSelectedFile();
- if ( !steamExeFile.exists() ) steamExeFile = null;
- }
- }
-
- if ( steamExeFile != null ) {
- appConfig.setProperty( SlipstreamConfig.STEAM_EXE_PATH, steamExeFile.getAbsolutePath() );
- log.info( "Steam located at: "+ steamExeFile.getAbsolutePath() );
- }
- }
-
- if ( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ).length() > 0 ) {
-
- if ( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "" ).length() == 0 ) {
-
- String[] launchOptions = new String[] {"Directly", "Steam"};
- int launchResponse = JOptionPane.showOptionDialog( null, "Would you prefer to launch FTL directly, or via Steam?", "How to Launch?", JOptionPane.DEFAULT_OPTION, JOptionPane.PLAIN_MESSAGE, null, launchOptions, launchOptions[1] );
- if ( launchResponse == 0 ) {
- appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" );
- }
- else if ( launchResponse == 1 ) {
- appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, "true" );
- }
- }
- }
- }
-
- // Prompt if update_catalog is invalid or hasn't been set.
- boolean askAboutUpdates = false;
- if ( !appConfig.getProperty( SlipstreamConfig.UPDATE_CATALOG, "" ).matches( "^\\d+$" ) )
- askAboutUpdates = true;
- if ( !appConfig.getProperty( SlipstreamConfig.UPDATE_APP, "" ).matches( "^\\d+$" ) )
- askAboutUpdates = true;
-
- if ( askAboutUpdates ) {
- String updatePrompt = ""
- + "Would you like Slipstream to periodically check for updates?\n"
- + "\n"
- + "You can change this later.";
-
- int response = JOptionPane.showConfirmDialog( null, updatePrompt, "Updates", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.YES_OPTION ) {
- appConfig.setProperty( SlipstreamConfig.UPDATE_CATALOG, "7" );
- appConfig.setProperty( SlipstreamConfig.UPDATE_APP, "4" );
- }
- else {
- appConfig.setProperty( SlipstreamConfig.UPDATE_CATALOG, "0" );
- appConfig.setProperty( SlipstreamConfig.UPDATE_APP, "0" );
- }
- }
-
- ManagerFrame frame = null;
- try {
- frame = new ManagerFrame( appConfig, APP_NAME, APP_VERSION, APP_URL, APP_AUTHOR );
- frame.init();
- frame.setVisible( true );
- }
- catch ( Exception e ) {
- log.error( "Failed to create and init the main window", e );
-
- // If the frame is constructed, but an exception prevents it
- // becoming visible, that *must* be caught. The frame registers
- // itself as a global uncaught exception handler. It doesn't
- // dispose() itself in the handler, so EDT will wait forever
- // for an invisible window to close.
-
- if ( frame != null && frame.isDisplayable() ) {
- frame.setDisposeNormally( false );
- frame.dispose();
- }
-
- throw new ExitException();
- }
- }
- catch ( ExitException e ) {
- System.gc();
- // System.exit( 1 ); // Don't do this (InterruptedException). Let EDT end gracefully.
- return;
- }
- }
-
- private static void showErrorDialog( String message ) {
- JOptionPane.showMessageDialog( null, message, "Error", JOptionPane.ERROR_MESSAGE );
- }
-
- private static class ExitException extends RuntimeException {
- public ExitException() {
- }
-
- public ExitException( String message ) {
- super( message );
- }
-
- public ExitException( Throwable cause ) {
- super( cause );
- }
-
- public ExitException( String message, Throwable cause ) {
- super( message, cause );
- }
+ SlipstreamCLI.main(args);
}
}
diff --git a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
index a10f407..330051d 100644
--- a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
+++ b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java
@@ -7,21 +7,13 @@ import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
-import java.io.InputStreamReader;
import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
-import java.util.Properties;
+import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
import picocli.CommandLine;
-import picocli.CommandLine.Command;
-import picocli.CommandLine.IVersionProvider;
-import picocli.CommandLine.Option;
-import picocli.CommandLine.ParameterException;
-import picocli.CommandLine.Parameters;
+import picocli.CommandLine.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -41,443 +33,633 @@ import net.vhati.modmanager.core.Report.ReportFormatter;
import net.vhati.modmanager.core.Report.ReportMessage;
import net.vhati.modmanager.core.SlipstreamConfig;
-
public class SlipstreamCLI {
- private static final Logger log = LoggerFactory.getLogger( SlipstreamCLI.class );
+ private static final Logger log = LoggerFactory.getLogger(SlipstreamCLI.class);
- private static File backupDir = new File( "./backup/" );
- private static File modsDir = new File( "./mods/" );
+ private static final File backupDir = new File("./backup/");
+ private static final File modsDir = new File("./mods/");
private static Thread.UncaughtExceptionHandler exceptionHandler = null;
- public static void main( String[] args ) {
+ public static void main(String[] args) {
- exceptionHandler = new Thread.UncaughtExceptionHandler() {
- @Override
- public void uncaughtException( Thread t, Throwable e ) {
- log.error( "Uncaught exception in thread: "+ t.toString(), e );
- System.exit( 1 );
- }
- };
+ exceptionHandler = (t, e) -> {
+ log.error("Uncaught exception in thread: {}", t.toString(), e);
+ System.exit(1);
+ };
- SlipstreamCommand slipstreamCmd = new SlipstreamCommand();
- CommandLine commandLine = new CommandLine( slipstreamCmd );
- try {
- commandLine.parse( args );
- }
- catch ( ParameterException e ) {
- //For multiple subcommands, e.getCommandLine() returns the one that failed.
+ CommandLine commandLine = new CommandLine(new BaseCommand())
+ .addSubcommand("start", new StartCommand())
+ .addSubcommand("patch", new PatchCommand())
+ .addSubcommand("list", new ListCommand())
+ .addSubcommand("clean", new CleanCommand());
- System.err.println( "Error parsing commandline: "+ e.getMessage() );
- System.exit( 1 );
- }
+ commandLine.execute(args);
- if ( commandLine.isUsageHelpRequested() ) {
- commandLine.usage( System.out );
- System.exit( 0 );
- }
- if ( commandLine.isVersionHelpRequested() ) {
- commandLine.printVersionHelp( System.out );
- System.exit( 0 );
- }
+// try {
+// commandLine.parse(args);
+// }
+// catch (ParameterException e) {
+// //For multiple subcommands, e.getCommandLine() returns the one that failed.
+//
+// System.err.println("Error parsing commandline: "+ e.getMessage());
+// System.exit(1);
+// }
+//
+// ActionFlow actionFlow = getCommandAction(commandLine);
+//
+// switch (actionFlow) {
+// case VERSION -> {
+// commandLine.usage(System.out);
+// System.exit(0);
+// }
+// case HELP -> {
+// commandLine.printVersionHelp(System.out);
+// System.exit(0);
+// }
+// }
- if ( slipstreamCmd.validate ) {
- boolean success = validate(slipstreamCmd.modFileNames);
- System.exit( success ? 0 : 1);
- }
-
- File configFile = new File( "modman.cfg" );
- SlipstreamConfig appConfig = new SlipstreamConfig( configFile );
-
- if ( slipstreamCmd.listMods ) {
- listMods(appConfig);
- System.exit( 0 );
- }
-
- File datsDir = null;
- if ( slipstreamCmd.extractDatsDir != null ||
- slipstreamCmd.patch ||
- slipstreamCmd.runftl ) {
- datsDir = getDatsDir( appConfig );
- }
-
- if ( slipstreamCmd.extractDatsDir != null ) {
- extractDatsDir(slipstreamCmd, datsDir);
- System.exit( 0 );
- }
-
- if ( slipstreamCmd.patch ) {
- boolean success = patch(slipstreamCmd, datsDir);
- if (!success) {
- System.exit( 1 );
- }
- }
-
- if ( slipstreamCmd.runftl ) {
- boolean success = runFtl(appConfig, datsDir);
- System.exit(success ? 0 : 1 );
- }
-
- System.exit( 0 );
+// if (slipstreamCmd.validate) {
+// boolean success = validate(slipstreamCmd.modFileNames);
+// System.exit(success ? 0 : 1);
+// }
+//
+// File configFile = new File("modman.cfg");
+// SlipstreamConfig appConfig = new SlipstreamConfig(configFile);
+//
+// if (slipstreamCmd.listMods) {
+// listMods(appConfig);
+// System.exit(0);
+// }
+//
+// File datsDir = null;
+// if (slipstreamCmd.extractDatsDir != null ||
+// slipstreamCmd.patch ||
+// slipstreamCmd.runftl) {
+// datsDir = getDatsDir(appConfig);
+// }
+//
+// if (slipstreamCmd.extractDatsDir != null) {
+// extractDatsDir(slipstreamCmd, datsDir);
+// System.exit(0);
+// }
+//
+// if (slipstreamCmd.patch) {
+// boolean success = patch(slipstreamCmd, datsDir);
+// if (!success) {
+// System.exit(1);
+// }
+// }
+//
+// if (slipstreamCmd.runftl) {
+// boolean success = runFtl(appConfig, datsDir);
+// System.exit(success ? 0: 1);
+// }
+//
+// System.exit(0);
}
- private static void extractDatsDir(SlipstreamCommand slipstreamCmd, File datsDir) {
- log.info( "Extracting dats..." );
+// private static ActionFlow getCommandAction(CommandLine commandLine) {
+// if (commandLine.isUsageHelpRequested()) {
+// return ActionFlow.HELP;
+// }
+// if (commandLine.isVersionHelpRequested()) {
+// return ActionFlow.VERSION;
+// }
+//
+// return null;
+// }
- File extractDir = slipstreamCmd.extractDatsDir;
+// private static void extractDatsDir(RunCommand slipstreamCmd, File datsDir) {
+// log.info("Extracting dats...");
+//
+// File extractDir = slipstreamCmd.extractDatsDir;
+//
+// FolderPack dstPack = null;
+// List srcPacks = new ArrayList(2);
+// InputStream is = null;
+// try {
+// File ftlDatFile = new File(datsDir, "ftl.dat");
+// File dataDatFile = new File(datsDir, "data.dat");
+// File resourceDatFile = new File(datsDir, "resource.dat");
+//
+// if (ftlDatFile.exists()) { // FTL 1.6.1.
+// AbstractPack ftlPack = new PkgPack(ftlDatFile, "r");
+// srcPacks.add(ftlPack);
+// }
+// else if (dataDatFile.exists() && resourceDatFile.exists()) { // FTL 1.01-1.5.13.
+// AbstractPack dataPack = new FTLPack(dataDatFile, "r");
+// AbstractPack resourcePack = new FTLPack(resourceDatFile, "r");
+// srcPacks.add(dataPack);
+// srcPacks.add(resourcePack);
+// }
+// else {
+// throw new FileNotFoundException(String.format("Could not find either \"%s\" or both \"%s\" and \"%s\"", ftlDatFile.getName(), dataDatFile.getName(), resourceDatFile.getName()));
+// }
+//
+// if (!extractDir.exists()) {
+// boolean success = extractDir.mkdirs();
+// if (success) {
+// log.error("Error extracting dats");
+// System.exit(1);
+// }
+// };
+//
+// dstPack = new FolderPack(extractDir);
+//
+// for (AbstractPack srcPack : srcPacks) {
+// List innerPaths = srcPack.list();
+//
+// for (String innerPath : innerPaths) {
+// if (dstPack.contains(innerPath)) {
+// log.info("While extracting resources, this file was overwritten: "+ innerPath);
+// dstPack.remove(innerPath);
+// }
+// is = srcPack.getInputStream(innerPath);
+// dstPack.add(innerPath, is);
+// }
+// srcPack.close();
+// }
+// }
+// catch (IOException e) {
+// log.error("Error extracting dats", e);
+// System.exit(1);
+// }
+// finally {
+// try {if (is != null) is.close();}
+// catch (IOException ignored) {}
+//
+// try {if (dstPack != null) dstPack.close();}
+// catch (IOException ignored) {}
+//
+// for (AbstractPack pack : srcPacks) {
+// try {pack.close();}
+// catch (IOException ignored) {}
+// }
+// }
+// }
+//
+// private static boolean patch(RunCommand slipstreamCmd, File datsDir) {
+// log.info("Patching...");
+//
+// DelayedDeleteHook deleteHook = new DelayedDeleteHook();
+// Runtime.getRuntime().addShutdownHook(deleteHook);
+//
+// List modFiles = new ArrayList();
+// if (slipstreamCmd.modFileNames != null) {
+// for (String modFileName : slipstreamCmd.modFileNames) {
+// File modFile = new File(modsDir, modFileName);
+//
+// if (modFile.isDirectory()) {
+// log.info("Zipping dir: {}/", modFile.getName());
+// try {
+// modFile = createTempMod(modFile);
+// deleteHook.addDoomedFile(modFile);
+// }
+// catch (IOException e) {
+// log.error("Error zipping dir: {}/", modFile.getName(), e);
+// return false;
+// }
+// }
+//
+// modFiles.add(modFile);
+// }
+// }
+//
+// boolean globalPanic = slipstreamCmd.globalPanic;
+//
+// SilentPatchObserver patchObserver = new SilentPatchObserver();
+// ModPatchThread patchThread = new ModPatchThread(modFiles, datsDir, backupDir, globalPanic, patchObserver);
+// Thread.setDefaultUncaughtExceptionHandler(exceptionHandler);
+// deleteHook.addWatchedThread(patchThread);
+//
+// patchThread.start();
+// while (patchThread.isAlive()) {
+// try {patchThread.join();}
+// catch (InterruptedException ignored) {}
+// }
+//
+// return patchObserver.hasSucceeded();
+// }
- FolderPack dstPack = null;
- List srcPacks = new ArrayList( 2 );
- InputStream is = null;
- try {
- File ftlDatFile = new File( datsDir, "ftl.dat" );
- File dataDatFile = new File( datsDir, "data.dat" );
- File resourceDatFile = new File( datsDir, "resource.dat" );
+// private static boolean validate(String[] modFileNames) {
+// DelayedDeleteHook deleteHook = new DelayedDeleteHook();
+// Runtime.getRuntime().addShutdownHook(deleteHook);
+//
+// log.info("Validating...");
+//
+// StringBuilder resultBuf = new StringBuilder();
+// ReportFormatter formatter = new ReportFormatter();
+// boolean anyInvalid = false;
+//
+// for (String modFileName : modFileNames) {
+// File modFile = new File(modsDir, modFileName);
+//
+// if (modFile.isDirectory()) {
+// log.info("Zipping dir: {}/", modFile.getName());
+// try {
+// modFile = createTempMod(modFile);
+// deleteHook.addDoomedFile(modFile);
+// }
+// catch (IOException e) {
+// log.error("Error zipping dir: {}/", modFile.getName(), e);
+//
+// List tmpMessages = new ArrayList();
+// tmpMessages.add(new ReportMessage(ReportMessage.SECTION, modFileName));
+// tmpMessages.add(new ReportMessage(ReportMessage.EXCEPTION, e.getMessage()));
+//
+// formatter.format(tmpMessages, resultBuf, 0);
+// resultBuf.append("\n");
+//
+// anyInvalid = true;
+// continue;
+// }
+// }
+//
+// Report validateReport = ModUtilities.validateModFile(modFile);
+//
+// formatter.format(validateReport.messages, resultBuf, 0);
+// resultBuf.append("\n");
+//
+// if (!validateReport.outcome) anyInvalid = true;
+// }
+// if (resultBuf.isEmpty()) {
+// resultBuf.append("No mods were checked.");
+// }
+//
+// System.out.println();
+// System.out.println(resultBuf);
+// return !anyInvalid;
+// }
+//
+// private static void listMods(SlipstreamConfig appConfig) {
+// log.info("Listing mods...");
+//
+// boolean allowZip = appConfig.getProperty(SlipstreamConfig.ALLOW_ZIP, "false").equals("true");
+// File[] modFiles = modsDir.listFiles(new ModAndDirFileFilter(allowZip, true));
+// List dirList = new ArrayList<>();
+// List fileList = new ArrayList<>();
+// assert modFiles != null;
+// for (File f : modFiles) {
+// if (f.isDirectory())
+// dirList.add(f.getName() +"/");
+// else
+// fileList.add(f.getName());
+// }
+// Collections.sort(dirList);
+// Collections.sort(fileList);
+// for (String s : dirList) System.out.println(s);
+// for (String s : fileList) System.out.println(s);
+// }
+//
+// private static boolean runFtl(SlipstreamConfig appConfig, File datsDir) {
+// log.info("Running FTL...");
+//
+// File exeFile = null;
+// String[] exeArgs = null;
+//
+// // Try to run via Steam.
+// if ("true".equals(appConfig.getProperty(SlipstreamConfig.RUN_STEAM_FTL, "false"))) {
+//
+// String steamPath = appConfig.getProperty(SlipstreamConfig.STEAM_EXE_PATH);
+// if (!steamPath.isEmpty()) {
+// exeFile = new File(steamPath);
+//
+// if (exeFile.exists()) {
+// exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
+// }
+// else {
+// log.warn(String.format("%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath()));
+// exeFile = null;
+// }
+// }
+//
+// if (exeFile == null) {
+// log.warn("Steam executable could not be found, so FTL will be launched directly");
+// }
+//
+// }
+// // Try to run directly.
+// if (exeFile == null) {
+// exeFile = FTLUtilities.findGameExe(datsDir);
+//
+// if (exeFile != null) {
+// exeArgs = new String[0];
+// } else {
+// log.warn("FTL executable could not be found");
+// }
+// }
+//
+// if (exeFile != null) {
+// try {
+// FTLUtilities.launchExe(exeFile, exeArgs);
+// }
+// catch (Exception e) {
+// log.error("Error launching FTL", e);
+// return false;
+// }
+// }
+// else {
+// log.error("No executables were found to launch FTL");
+// return false;
+// }
+//
+// return true;
+// }
- if ( ftlDatFile.exists() ) { // FTL 1.6.1.
- AbstractPack ftlPack = new PkgPack( ftlDatFile, "r" );
- srcPacks.add( ftlPack );
- }
- else if ( dataDatFile.exists() && resourceDatFile.exists() ) { // FTL 1.01-1.5.13.
- AbstractPack dataPack = new FTLPack( dataDatFile, "r" );
- AbstractPack resourcePack = new FTLPack( resourceDatFile, "r" );
- srcPacks.add( dataPack );
- srcPacks.add( resourcePack );
- }
- else {
- throw new FileNotFoundException( String.format( "Could not find either \"%s\" or both \"%s\" and \"%s\"", ftlDatFile.getName(), dataDatFile.getName(), resourceDatFile.getName() ) );
- }
-
- if ( !extractDir.exists() ) extractDir.mkdirs();
-
- dstPack = new FolderPack( extractDir );
-
- for ( AbstractPack srcPack : srcPacks ) {
- List innerPaths = srcPack.list();
-
- for ( String innerPath : innerPaths ) {
- if ( dstPack.contains( innerPath ) ) {
- log.info( "While extracting resources, this file was overwritten: "+ innerPath );
- dstPack.remove( innerPath );
- }
- is = srcPack.getInputStream( innerPath );
- dstPack.add( innerPath, is );
- }
- srcPack.close();
- }
- }
- catch ( IOException e ) {
- log.error( "Error extracting dats", e );
- System.exit( 1 );
- }
- finally {
- try {if ( is != null ) is.close();}
- catch ( IOException ex ) {}
-
- try {if ( dstPack != null ) dstPack.close();}
- catch ( IOException ex ) {}
-
- for ( AbstractPack pack : srcPacks ) {
- try {pack.close();}
- catch ( IOException ex ) {}
- }
- }
- }
-
- private static boolean patch(SlipstreamCommand slipstreamCmd, File datsDir) {
- log.info( "Patching..." );
-
- DelayedDeleteHook deleteHook = new DelayedDeleteHook();
- Runtime.getRuntime().addShutdownHook( deleteHook );
-
- List modFiles = new ArrayList();
- if ( slipstreamCmd.modFileNames != null ) {
- for ( String modFileName : slipstreamCmd.modFileNames ) {
- File modFile = new File( modsDir, modFileName );
-
- if ( modFile.isDirectory() ) {
- log.info( String.format( "Zipping dir: %s/", modFile.getName() ) );
- try {
- modFile = createTempMod( modFile );
- deleteHook.addDoomedFile( modFile );
- }
- catch ( IOException e ) {
- log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
- return false;
- }
- }
-
- modFiles.add( modFile );
- }
- }
-
- boolean globalPanic = slipstreamCmd.globalPanic;
-
- SilentPatchObserver patchObserver = new SilentPatchObserver();
- ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, globalPanic, patchObserver );
- patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler );
- deleteHook.addWatchedThread( patchThread );
-
- patchThread.start();
- while ( patchThread.isAlive() ) {
- try {patchThread.join();}
- catch ( InterruptedException e ) {}
- }
-
- if ( !patchObserver.hasSucceeded() ) return false;
- return true;
- }
-
- private static boolean validate(String[] modFileNames) {
- DelayedDeleteHook deleteHook = new DelayedDeleteHook();
- Runtime.getRuntime().addShutdownHook( deleteHook );
-
- log.info( "Validating..." );
-
- StringBuilder resultBuf = new StringBuilder();
- ReportFormatter formatter = new ReportFormatter();
- boolean anyInvalid = false;
-
- for ( String modFileName : modFileNames ) {
- File modFile = new File( modsDir, modFileName );
-
- if ( modFile.isDirectory() ) {
- log.info( String.format( "Zipping dir: %s/", modFile.getName() ) );
- try {
- modFile = createTempMod( modFile );
- deleteHook.addDoomedFile( modFile );
- }
- catch ( IOException e ) {
- log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e );
-
- List tmpMessages = new ArrayList();
- tmpMessages.add( new ReportMessage( ReportMessage.SECTION, modFileName ) );
- tmpMessages.add( new ReportMessage( ReportMessage.EXCEPTION, e.getMessage() ) );
-
- formatter.format( tmpMessages, resultBuf, 0 );
- resultBuf.append( "\n" );
-
- anyInvalid = true;
- continue;
- }
- }
-
- Report validateReport = ModUtilities.validateModFile( modFile );
-
- formatter.format( validateReport.messages, resultBuf, 0 );
- resultBuf.append( "\n" );
-
- if ( validateReport.outcome == false ) anyInvalid = true;
- }
- if ( resultBuf.length() == 0 ) {
- resultBuf.append( "No mods were checked." );
- }
-
- System.out.println();
- System.out.println(resultBuf);
- return !anyInvalid;
- }
-
- private static void listMods(SlipstreamConfig appConfig) {
- log.info( "Listing mods..." );
-
- boolean allowZip = appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ).equals( "true" );
- File[] modFiles = modsDir.listFiles( new ModAndDirFileFilter( allowZip, true ) );
- List dirList = new ArrayList();
- List fileList = new ArrayList();
- for ( File f : modFiles ) {
- if ( f.isDirectory() )
- dirList.add( f.getName() +"/" );
- else
- fileList.add( f.getName() );
- }
- Collections.sort( dirList );
- Collections.sort( fileList );
- for ( String s : dirList ) System.out.println( s );
- for ( String s : fileList ) System.out.println( s );
- }
-
- private static boolean runFtl(SlipstreamConfig appConfig, File datsDir) {
- log.info( "Running FTL..." );
-
- File exeFile = null;
- String[] exeArgs = null;
-
- // Try to run via Steam.
- if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) {
-
- String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH );
- if ( steamPath.length() > 0 ) {
- exeFile = new File( steamPath );
-
- if ( exeFile.exists() ) {
- exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
- }
- else {
- log.warn( String.format( "%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath() ) );
- exeFile = null;
- }
- }
-
- if ( exeFile == null ) {
- log.warn( "Steam executable could not be found, so FTL will be launched directly" );
- }
-
- }
- // Try to run directly.
- if ( exeFile == null ) {
- exeFile = FTLUtilities.findGameExe( datsDir );
-
- if ( exeFile != null ) {
- exeArgs = new String[0];
- } else {
- log.warn( "FTL executable could not be found" );
- }
- }
-
- if ( exeFile != null ) {
- try {
- FTLUtilities.launchExe( exeFile, exeArgs );
- }
- catch ( Exception e ) {
- log.error( "Error launching FTL", e );
- return false;
- }
- }
- else {
- log.error( "No executables were found to launch FTL" );
- return false;
- }
-
- return true;
- }
-
- /**
- * Checks the validity of the config's dats path and returns it.
- * Or exits if the path is invalid.
- */
- private static File getDatsDir( SlipstreamConfig appConfig ) {
- File datsDir = null;
- String datsPath = appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH, "" );
-
- if ( datsPath.length() > 0 ) {
- log.info( "Using FTL dats path from config: "+ datsPath );
- datsDir = new File( datsPath );
- if ( FTLUtilities.isDatsDirValid( datsDir ) == false ) {
- log.error( "The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid" );
- datsDir = null;
- }
- }
- else {
- log.error( "No FTL dats path previously set" );
- }
- if ( datsDir == null ) {
- log.error( "Run the GUI once, or edit the config file, and try again" );
- System.exit( 1 );
- }
-
- return datsDir;
- }
+// /**
+// * Checks the validity of the config's dats path and returns it.
+// * Or exits if the path is invalid.
+// */
+// private static File getDatsDir(SlipstreamConfig appConfig) {
+// File datsDir = null;
+// String datsPath = appConfig.getProperty(SlipstreamConfig.FTL_DATS_PATH, "");
+//
+// if (!datsPath.isEmpty()) {
+// log.info("Using FTL dats path from config: "+ datsPath);
+// datsDir = new File(datsPath);
+// if (!FTLUtilities.isDatsDirValid(datsDir)) {
+// log.error("The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid");
+// datsDir = null;
+// }
+// }
+// else {
+// log.error("No FTL dats path previously set");
+// }
+// if (datsDir == null) {
+// log.error("Run the GUI once, or edit the config file, and try again");
+// System.exit(1);
+// }
+//
+// return datsDir;
+// }
/**
* Returns a temporary zip made from a directory.
- *
* Empty subdirs will be omitted.
* The archive will be not be deleted on exit (handle that elsewhere).
*/
- private static File createTempMod( File dir ) throws IOException {
- File tempFile = File.createTempFile( dir.getName() +"_temp-", ".zip" );
+ private static File createTempMod(File dir) throws IOException {
+ File tempFile = File.createTempFile(dir.getName() +"_temp-", ".zip");
- FileOutputStream fos = null;
- try {
- fos = new FileOutputStream( tempFile );
- ZipOutputStream zos = new ZipOutputStream( new BufferedOutputStream( fos ) );
- addDirToArchive( zos, dir, null );
- zos.close();
- }
- finally {
- try {if ( fos != null ) fos.close();}
- catch ( IOException e ) {}
- }
+ try (FileOutputStream fos = new FileOutputStream(tempFile)) {
+ ZipOutputStream zos = new ZipOutputStream(new BufferedOutputStream(fos));
+ addDirToArchive(zos, dir, null);
+ zos.close();
+ } catch (IOException ignored) {
+ }
return tempFile;
}
- private static void addDirToArchive( ZipOutputStream zos, File dir, String pathPrefix ) throws IOException {
- if ( pathPrefix == null ) pathPrefix = "";
+ private static void addDirToArchive(ZipOutputStream zos, File dir, String pathPrefix) throws IOException {
+ if (pathPrefix == null) pathPrefix = "";
- for ( File f : dir.listFiles() ) {
- if ( f.isDirectory() ) {
- addDirToArchive( zos, f, pathPrefix + f.getName() +"/" );
+ for (File f : Objects.requireNonNull(dir.listFiles())) {
+ if (f.isDirectory()) {
+ addDirToArchive(zos, f, pathPrefix + f.getName() +"/");
continue;
}
- FileInputStream is = null;
- try {
- is = new FileInputStream( f );
- zos.putNextEntry( new ZipEntry( pathPrefix + f.getName() ) );
+ try (FileInputStream is = new FileInputStream(f)) {
+ zos.putNextEntry(new ZipEntry(pathPrefix + f.getName()));
- byte[] buf = new byte[4096];
- int len;
- while ( (len = is.read(buf)) >= 0 ) {
- zos.write( buf, 0, len );
- }
+ byte[] buf = new byte[4096];
+ int len;
+ while ((len = is.read(buf)) >= 0) {
+ zos.write(buf, 0, len);
+ }
- zos.closeEntry();
- }
- finally {
- try {if ( is != null ) is.close();}
- catch ( IOException e ) {}
- }
+ zos.closeEntry();
+ } catch (IOException ignored) {
+ }
}
}
-
+ @Command(
+ name = "list",
+ description = "list all available mods"
+ )
+ public static class ListCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("list command");
+ }
+ }
@Command(
- name = "modman",
- abbreviateSynopsis = true,
- sortOptions = false,
- description = "Perform actions against an FTL installation and/or a list of named mods.",
- footer = "%nIf a named mod is a directory, a temporary zip will be created.",
- versionProvider = SlipstreamVersionProvider.class
+ name = "patch",
+ description = "create a patched binary using the available mods"
)
- public static class SlipstreamCommand {
- @Option(names = "--extract-dats", paramLabel = "DIR", description = "extract FTL resources into a dir")
- File extractDatsDir;
+ public static class PatchCommand implements Runnable {
+ @Parameters(index = "0", description = "the location unpatched binary is located at")
+ File binary;
- @Option(names = "--global-panic", description = "patch as if advanced find tags had panic='true'")
- boolean globalPanic;
+ @Parameters(description = "list of mods that the binary will be patched with before it starts. (defaults to all mods if non are provided)", mapFallbackValue = Option.NULL_VALUE)
+ String mods;
- @Option(names = "--list-mods", description = "list available mod names")
- boolean listMods;
+ @Option(names = "--dry", description = "skip the patching step but still run the rest of the application")
+ boolean dry;
- @Option(names = "--runftl", description = "run the game (standalone or with 'patch')")
- boolean runftl;
+ @Override
+ public void run() {
+ System.out.println("patch command");
+ }
+ }
- @Option(names = "--patch", description = "revert to vanilla and add named mods (if any)")
- boolean patch;
+ @Command(
+ name = "clean",
+ description = "remove all patched binaries"
+ )
+ public static class CleanCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("clean command");
+ }
+ }
- @Option(names = "--validate", description = "check named mods for problems")
- boolean validate;
+ @Command(
+ name = "install",
+ description = "install the slipstream mod manager"
+ )
+ public static class InstallCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("install command");
+ }
+ }
- @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help and exit")
- boolean helpRequested;
+ @Command(
+ name = "uninstall",
+ description = "uninstall the slipstream mod manager"
+ )
+ public static class UninstallCommand implements Runnable {
+ @Override
+ public void run() {
+ System.out.println("uninstall command");
+ }
+ }
+ @Command(
+ name = "start",
+ abbreviateSynopsis = true,
+ sortOptions = false,
+ description = "Creates and starts a patched version of FTL.",
+ versionProvider = SlipstreamVersionProvider.class
+ )
+ public static class StartCommand implements Runnable {
@Option(names = "--version", versionHelp = true, description = "output version information and exit")
- boolean versionRequested;
+ boolean isVersionCommand;
+
+ @Option(names = { "--game-folder", "--binary", "--data-folder" }, description = "the location of the game files", converter = GameDirectoryConverter.class, mapFallbackValue = Option.NULL_VALUE)
+ GameDirectory gameDirectory;
+
+ @Parameters(description = "list of mods that the binary will be patched with before it starts. (defaults to all mods if non are provided)", mapFallbackValue = Option.NULL_VALUE)
+ String[] mods;
+
+ @Option(names = "--dry", description = "skip the patching step but still run the rest of the application")
+ boolean dry;
+
+ @Override
+ public void run() {
+ System.out.println("start command: " + gameDirectory.root.getAbsolutePath());
+ }
+ }
+
+ static class GameDirectoryConverter implements ITypeConverter {
+
+ private GameDirectory getGameDirectory(String fileName) {
+ if (fileName == null ) {
+ // TODO: get this from env variables
+ // TODO: if env variable not set check some common places and prompt the user if they want to use that
+ return null;
+ }
+ return GameDirectory.createGameDirectory(new File(fileName));
+ }
+
+ @Override
+ public GameDirectory convert(String fileName) {
+ GameDirectory gameDirectory = getGameDirectory(fileName);
+ if (gameDirectory == null) {
+ System.out.println("Game not found. Please specify the --game-folder argument, or set the FTL_GAME_FOLDER environment variable.");
+ System.exit(1);
+ }
+ return gameDirectory;
+ }
+ }
+
+ static class GameDirectory {
+
+ private static final String GAME_FOLDER_NAME = "FTL Faster Than Light";
+ private static final String GAME_DATA_FOLDER_NAME = "data";
+ private static final String MODS_FOLDER_NAME = "mods";
+ private static final String BACKUP_FOLDER_NAME = "backup";
+ private static final String LAUNCH_SCRIPT_FILE_NAME = "FTL";
+ private static final String X86_BINARY_FILE_NAME = "FTL.x86";
+
+ private static boolean isValidGameDirectory(File gameDirectory) {
+ if (
+ !(
+ gameDirectory.exists()
+ && gameDirectory.isDirectory()
+ && gameDirectory.getName().equals(GAME_FOLDER_NAME)
+ )
+ ) {
+ return false;
+ }
+
+ File gameDataFolder = new File(gameDirectory, GAME_DATA_FOLDER_NAME);
+ if (
+ !(
+ gameDataFolder.exists()
+ && gameDataFolder.isDirectory()
+ )
+ ) {
+ return false;
+ }
+
+ File gameLaunchScript = new File(gameDataFolder, LAUNCH_SCRIPT_FILE_NAME);
+ if (
+ !(
+ gameLaunchScript.exists()
+ && gameLaunchScript.isFile()
+ )
+ ) {
+ return false;
+ }
+
+ // TODO: deal with FTL.amd64
+ File gameBinary = new File(gameDataFolder, X86_BINARY_FILE_NAME);
+ return gameBinary.exists() && gameBinary.isFile();
+ }
+
+ static GameDirectory createGameDirectory(File file) {
+ if (file.isFile()) {
+ String filename = file.getName();
+ if (filename.equals(X86_BINARY_FILE_NAME)) {
+ File root = file.getParentFile().getParentFile();
+ if (isValidGameDirectory(root)) {
+ return new GameDirectory((root));
+ }
+ }
+ }
+ if (file.isDirectory()){
+ if (isValidGameDirectory(file)) {
+ return new GameDirectory(file);
+ }
+
+ String directoryName = file.getName();
+ if (directoryName.equals(GAME_DATA_FOLDER_NAME)) {
+ File root = file.getParentFile();
+ if (isValidGameDirectory(root)) {
+ return new GameDirectory((root));
+ }
+ }
+ }
+ return null;
+ }
+
+ public final File root;
+ public final File dataDir;
+ public final File modsDir;
+ public final File backupDir;
+ public final File launchScript;
+ public final File binary;
+
+ GameDirectory(File root) {
+ this.root = root;
+ this.dataDir = new File(root, "data");
+ this.modsDir = new File(this.dataDir, "mods");
+ this.backupDir = new File(this.dataDir, "backup");
+ this.launchScript = new File(this.dataDir, "FTL");
+ // TODO: this should probably change when we need to use FTL.amd64 instead
+ this.binary = new File(this.dataDir, X86_BINARY_FILE_NAME);
+ }
+ }
+
+ @Command(name = "slipstream")
+ public static class BaseCommand implements Runnable {
+ @Option(names = {"-h", "--help"}, usageHelp = true, description = "display this help and exit")
+ boolean isHelpCommand;
+
+ @Spec
+ Model.CommandSpec spec;
+
+ @Override
+ public void run() {
+ // if the command was invoked without subcommand, show the usage help
+ spec.commandLine().usage(System.err);
+ }
- @Parameters(paramLabel = "MODFILE", description = "names of files or directories in the mods/ dir")
- String[] modFileNames;
}
public static class SlipstreamVersionProvider implements IVersionProvider {
@Override
public String[] getVersion() {
return new String[] {
- String.format( "%s %s", FTLModManager.APP_NAME, FTLModManager.APP_VERSION ),
+ String.format("%s %s", FTLModManager.APP_NAME, FTLModManager.APP_VERSION),
"Copyright (C) 2013,2014,2017,2018 David Millis",
"",
"This program is free software; you can redistribute it and/or modify",
@@ -503,19 +685,19 @@ public class SlipstreamCLI {
private boolean succeeded = false;
@Override
- public void patchingProgress( final int value, final int max ) {
+ public void patchingProgress(final int value, final int max) {
}
@Override
- public void patchingStatus( String message ) {
+ public void patchingStatus(String message) {
}
@Override
- public void patchingMod( File modFile ) {
+ public void patchingMod(File modFile) {
}
@Override
- public synchronized void patchingEnded( boolean outcome, Exception e ) {
+ public synchronized void patchingEnded(boolean outcome, Exception e) {
succeeded = outcome;
done = true;
}
@@ -525,26 +707,18 @@ public class SlipstreamCLI {
}
-
- private static class ModAndDirFileFilter implements FileFilter {
- private boolean allowZip;
- private boolean allowDirs;
-
- public ModAndDirFileFilter( boolean allowZip, boolean allowDirs ) {
- this.allowZip = allowZip;
- this.allowDirs = allowDirs;
- }
+ private record ModAndDirFileFilter(boolean allowZip, boolean allowDirs) implements FileFilter {
@Override
- public boolean accept( File f ) {
- if ( f.isDirectory() ) return allowDirs;
+ public boolean accept(File f) {
+ if (f.isDirectory()) return allowDirs;
- if ( f.getName().endsWith(".ftl") ) return true;
+ if (f.getName().endsWith(".ftl")) return true;
- if ( allowZip ) {
- if ( f.getName().endsWith(".zip") ) return true;
+ if (allowZip) {
+ return f.getName().endsWith(".zip");
+ }
+ return false;
}
- return false;
}
- }
}
diff --git a/src/main/java/net/vhati/modmanager/ui/ClipboardMenuMouseListener.java b/src/main/java/net/vhati/modmanager/ui/ClipboardMenuMouseListener.java
deleted file mode 100644
index 0a15eec..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ClipboardMenuMouseListener.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.Toolkit;
-import java.awt.datatransfer.DataFlavor;
-import java.awt.event.ActionEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import javax.swing.AbstractAction;
-import javax.swing.Action;
-import javax.swing.JMenuItem;
-import javax.swing.JPopupMenu;
-import javax.swing.text.JTextComponent;
-
-
-/**
- * A Cut/Copy/Paste/SelectAll context menu for JTextComponents.
- *
- * Add this listener after any others. Any pressed/released listeners that want
- * to preempt this menu should call e.consume().
- */
-public class ClipboardMenuMouseListener extends MouseAdapter {
-
- private JPopupMenu popup = new JPopupMenu();
-
- private Action cutAction;
- private Action copyAction;
- private Action pasteAction;
- private Action selectAllAction;
-
- private JTextComponent textComponent = null;
-
-
- public ClipboardMenuMouseListener() {
- cutAction = new AbstractAction( "Cut" ) {
- @Override
- public void actionPerformed( ActionEvent ae ) {
- textComponent.cut();
- }
- };
- copyAction = new AbstractAction( "Copy" ) {
- @Override
- public void actionPerformed( ActionEvent ae ) {
- textComponent.copy();
- }
- };
- pasteAction = new AbstractAction( "Paste" ) {
- @Override
- public void actionPerformed( ActionEvent ae ) {
- textComponent.paste();
- }
- };
- selectAllAction = new AbstractAction( "Select All" ) {
- @Override
- public void actionPerformed( ActionEvent ae ) {
- textComponent.selectAll();
- }
- };
-
- popup.add( cutAction );
- popup.add( copyAction );
- popup.add( pasteAction );
- popup.addSeparator();
- popup.add( selectAllAction );
- }
-
-
- @Override
- public void mousePressed( MouseEvent e ) {
- if ( e.isConsumed() ) return;
- if ( e.isPopupTrigger() ) showMenu( e );
- }
-
- @Override
- public void mouseReleased( MouseEvent e ) {
- if ( e.isConsumed() ) return;
- if ( e.isPopupTrigger() ) showMenu( e );
- }
-
- public void showMenu( MouseEvent e ) {
- if ( e.getSource() instanceof JTextComponent == false ) return;
-
- textComponent = (JTextComponent)e.getSource();
- textComponent.requestFocus();
-
- boolean enabled = textComponent.isEnabled();
- boolean editable = textComponent.isEditable();
- boolean nonempty = !(textComponent.getText() == null || textComponent.getText().equals( "" ));
- boolean marked = textComponent.getSelectedText() != null;
-
- boolean pasteAvailable = Toolkit.getDefaultToolkit().getSystemClipboard().getContents( null ).isDataFlavorSupported( DataFlavor.stringFlavor );
-
- cutAction.setEnabled( enabled && editable && marked );
- copyAction.setEnabled( enabled && marked );
- pasteAction.setEnabled( enabled && editable && pasteAvailable );
- selectAllAction.setEnabled( enabled && nonempty );
-
- int nx = e.getX();
- if ( nx > 500 ) nx = nx - popup.getSize().width;
-
- popup.show( e.getComponent(), nx, e.getY() - popup.getSize().height );
-
- e.consume();
- }
-}
\ No newline at end of file
diff --git a/src/main/java/net/vhati/modmanager/ui/CreateModDialog.java b/src/main/java/net/vhati/modmanager/ui/CreateModDialog.java
deleted file mode 100644
index bda5804..0000000
--- a/src/main/java/net/vhati/modmanager/ui/CreateModDialog.java
+++ /dev/null
@@ -1,213 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Desktop;
-import java.awt.Dimension;
-import java.awt.Frame;
-import java.awt.Point;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.io.File;
-import java.io.IOException;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JDialog;
-import javax.swing.JOptionPane;
-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 org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.vhati.modmanager.ui.FieldEditorPanel;
-import net.vhati.modmanager.ui.FieldEditorPanel.ContentType;
-import net.vhati.modmanager.ui.RegexDocument;
-import net.vhati.modmanager.xml.JDOMModMetadataWriter;
-
-
-public class CreateModDialog extends JDialog implements ActionListener {
-
- private static final Logger log = LoggerFactory.getLogger( CreateModDialog.class );
-
- protected static final String DIR_NAME = "Directory Name";
- protected static final String AUDIO_ROOT = "audio/";
- protected static final String DATA_ROOT = "data/";
- protected static final String FONTS_ROOT = "fonts/";
- protected static final String IMG_ROOT = "img/";
- protected static final String XML_COMMENTS = "XML Comments";
- protected static final String TITLE = "Title";
- protected static final String URL = "Thread URL";
- protected static final String AUTHOR = "Author";
- protected static final String VERSION = "Version";
- protected static final String DESC = "Description";
-
- protected FieldEditorPanel editorPanel;
- protected JButton applyBtn;
-
- protected File modsDir;
-
-
- public CreateModDialog( Frame owner, File modsDir ) {
- super( owner, "New Mod" );
- this.setDefaultCloseOperation( JDialog.DISPOSE_ON_CLOSE );
-
- this.modsDir = modsDir;
-
- editorPanel = new FieldEditorPanel( false );
- editorPanel.setBorder( BorderFactory.createEmptyBorder( 10, 10, 0, 10 ) );
- editorPanel.setNameWidth( 100 );
- editorPanel.setValueWidth( 350 );
-
- editorPanel.addRow( DIR_NAME, ContentType.STRING );
- editorPanel.getString( DIR_NAME ).setDocument( new RegexDocument( "[^\\/:;*?<>|^\"]*" ) );
- editorPanel.addTextRow( String.format( "The name of a directory to create in the %s/ folder.", modsDir.getName() ) );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( AUDIO_ROOT, ContentType.BOOLEAN );
- editorPanel.addRow( DATA_ROOT, ContentType.BOOLEAN );
- editorPanel.addRow( FONTS_ROOT, ContentType.BOOLEAN );
- editorPanel.addRow( IMG_ROOT, ContentType.BOOLEAN );
- editorPanel.getBoolean( AUDIO_ROOT ).setHorizontalAlignment( javax.swing.SwingConstants.LEFT );
- editorPanel.getBoolean( DATA_ROOT ).setHorizontalAlignment( javax.swing.SwingConstants.LEFT );
- editorPanel.getBoolean( FONTS_ROOT ).setHorizontalAlignment( javax.swing.SwingConstants.LEFT );
- editorPanel.getBoolean( IMG_ROOT ).setHorizontalAlignment( javax.swing.SwingConstants.LEFT );
- editorPanel.addTextRow( "Create empty top-level directories?" );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( XML_COMMENTS, ContentType.BOOLEAN );
- editorPanel.getBoolean( XML_COMMENTS ).setHorizontalAlignment( javax.swing.SwingConstants.LEFT );
- editorPanel.addTextRow( "Include XML comments about the purpose of these fields?" );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( TITLE, ContentType.STRING );
- editorPanel.addTextRow( "The title of this mod." );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( URL, ContentType.STRING );
- editorPanel.addTextRow( "This mod's thread on the forum." );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( AUTHOR, ContentType.STRING );
- editorPanel.addTextRow( "Your forum user name." );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( VERSION, ContentType.STRING );
- editorPanel.addTextRow( "The revision/variant of this release, preferably at least a number." );
- editorPanel.addSeparatorRow();
- editorPanel.addRow( DESC, ContentType.TEXT_AREA );
- editorPanel.getTextArea( DESC ).setRows( 15 );
- editorPanel.addTextRow( "Summary of gameplay effects; flavor; features; concerns about compatibility, recommended patch order, requirements; replaced ship slot; etc." );
-
- editorPanel.getBoolean( XML_COMMENTS ).setSelected( true );
-
- 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( "Generate Mod" );
- 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 dirName = editorPanel.getString( DIR_NAME ).getText().trim();
- String modTitle = editorPanel.getString( TITLE ).getText().trim();
- String modURL = editorPanel.getString( URL ).getText().trim();
- String modAuthor = editorPanel.getString( AUTHOR ).getText().trim();
- String modVersion = editorPanel.getString( VERSION ).getText().trim();
- String modDesc = editorPanel.getTextArea( DESC ).getText().trim();
- boolean xmlComments = editorPanel.getBoolean( XML_COMMENTS ).isSelected();
-
- if ( dirName.length() == 0 ) {
- JOptionPane.showMessageDialog( CreateModDialog.this, "No directory name was given.", "Nothing to do", JOptionPane.WARNING_MESSAGE );
- return;
- }
-
- File genDir = new File( modsDir, dirName );
- if ( !genDir.exists() ) {
- try {
- // Generate the mod.
- if ( genDir.mkdir() ) {
- File appendixDir = new File ( genDir, "mod-appendix" );
- if ( appendixDir.mkdir() ) {
- File metadataFile = new File( appendixDir, "metadata.xml" );
-
- JDOMModMetadataWriter.writeMetadata( metadataFile, modTitle, modURL, modAuthor, modVersion, modDesc, xmlComments );
- }
- else {
- throw new IOException( String.format( "Failed to create directory: %s", appendixDir.getName() ) );
- }
- }
- else {
- throw new IOException( String.format( "Failed to create directory: %s", genDir.getName() ) );
- }
-
- // Create root dirs.
- if ( editorPanel.getBoolean( AUDIO_ROOT ).isSelected() )
- new File( genDir, "audio" ).mkdir();
- if ( editorPanel.getBoolean( DATA_ROOT ).isSelected() )
- new File( genDir, "data" ).mkdir();
- if ( editorPanel.getBoolean( FONTS_ROOT ).isSelected() )
- new File( genDir, "fonts" ).mkdir();
- if ( editorPanel.getBoolean( IMG_ROOT ).isSelected() )
- new File( genDir, "img" ).mkdir();
-
- // Show the folder.
- try {
- if ( Desktop.isDesktopSupported() ) {
- Desktop.getDesktop().open( genDir.getCanonicalFile() );
- } else {
- log.error( String.format( "Java cannot open the %s/ folder for you on this OS", genDir.getName() ) );
- }
- }
- catch ( IOException f ) {
- log.error( String.format( "Error opening %s/ folder", genDir.getName() ), f );
- }
-
- // All done.
- CreateModDialog.this.dispose();
- }
- catch ( IOException f ) {
- log.error( String.format( "Failed to generate new mod: %s", genDir.getName() ), f );
-
- JOptionPane.showMessageDialog( CreateModDialog.this, String.format( "Failed to generate new mod: %s\n%s", genDir.getName(), f.getMessage() ), "Error", JOptionPane.ERROR_MESSAGE );
- }
- }
- else {
- JOptionPane.showMessageDialog( CreateModDialog.this, String.format( "A directory named \"%s\" already exists.", genDir.getName() ), "Nothing to do", JOptionPane.WARNING_MESSAGE );
- }
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java b/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java
deleted file mode 100644
index eeb4151..0000000
--- a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java
+++ /dev/null
@@ -1,163 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.Frame;
-import java.io.File;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.List;
-import javax.swing.JDialog;
-import javax.swing.SwingUtilities;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.vhati.ftldat.AbstractPack;
-import net.vhati.ftldat.FolderPack;
-import net.vhati.ftldat.FTLPack;
-import net.vhati.ftldat.PkgPack;
-
-import net.vhati.modmanager.ui.ProgressDialog;
-
-
-public class DatExtractDialog extends ProgressDialog {
-
- private static final Logger log = LoggerFactory.getLogger( DatExtractDialog.class );
-
- private boolean started = false;
-
- private File extractDir = null;
- private File datsDir = null;
-
- private DatExtractThread workerThread = null;
-
-
- public DatExtractDialog( Frame owner, File extractDir, File datsDir ) {
- super( owner, false );
- this.setTitle( "Extracting..." );
-
- this.extractDir = extractDir;
- this.datsDir = datsDir;
-
- this.setSize( 400, 160 );
- this.setMinimumSize( this.getPreferredSize() );
- this.setLocationRelativeTo( owner );
-
- workerThread = new DatExtractThread( extractDir, datsDir );
- }
-
- /**
- * Returns the worker thread that does the extracting.
- *
- * This method is provided so other classes can customize the thread
- * before calling extract().
- */
- public Thread getWorkerThread() {
- return workerThread;
- }
-
- /**
- * Starts the background extraction thread.
- * Call this immediately before setVisible().
- */
- public void extract() {
- if ( started ) return;
-
- workerThread.start();
- started = true;
- }
-
- @Override
- protected void setTaskOutcome( boolean outcome, Exception e ) {
- super.setTaskOutcome( outcome, e );
- if ( !this.isShowing() ) return;
-
- if ( succeeded ) {
- setStatusText( "All resources extracted successfully." );
- } else {
- setStatusText( String.format( "Error extracting dats: %s", e ) );
- }
- }
-
-
-
- private class DatExtractThread extends Thread {
-
- private File extractDir = null;
- private File datsDir = null;
-
-
- public DatExtractThread( File extractDir, File datsDir ) {
- this.extractDir = extractDir;
- this.datsDir = datsDir;
- }
-
- @Override
- public void run() {
- AbstractPack dstPack = null;
- List srcPacks = new ArrayList( 2 );
- InputStream is = null;
- int progress = 0;
-
- try {
- File ftlDatFile = new File( datsDir, "ftl.dat" );
- File dataDatFile = new File( datsDir, "data.dat" );
- File resourceDatFile = new File( datsDir, "resource.dat" );
-
- if ( ftlDatFile.exists() ) { // FTL 1.6.1.
- AbstractPack ftlPack = new PkgPack( ftlDatFile, "r" );
- srcPacks.add( ftlPack );
- }
- else if ( dataDatFile.exists() && resourceDatFile.exists() ) { // FTL 1.01-1.5.13.
- AbstractPack dataPack = new FTLPack( dataDatFile, "r" );
- AbstractPack resourcePack = new FTLPack( resourceDatFile, "r" );
- srcPacks.add( dataPack );
- srcPacks.add( resourcePack );
- }
- else {
- throw new FileNotFoundException( String.format( "Could not find either \"%s\" or both \"%s\" and \"%s\"", ftlDatFile.getName(), dataDatFile.getName(), resourceDatFile.getName() ) );
- }
-
- if ( !extractDir.exists() ) extractDir.mkdirs();
-
- dstPack = new FolderPack( extractDir );
-
- for ( AbstractPack srcPack : srcPacks ) {
- progress = 0;
- List innerPaths = srcPack.list();
- setProgressLater( progress, innerPaths.size() );
-
- for ( String innerPath : innerPaths ) {
- setStatusTextLater( innerPath );
- if ( dstPack.contains( innerPath ) ) {
- log.info( "While extracting resources, this file was overwritten: "+ innerPath );
- dstPack.remove( innerPath );
- }
- is = srcPack.getInputStream( innerPath );
- dstPack.add( innerPath, is );
- setProgressLater( progress++ );
- }
- srcPack.close();
- }
- setTaskOutcomeLater( true, null );
- }
- catch ( Exception e ) {
- log.error( "Error extracting dats", e );
- setTaskOutcomeLater( false, e );
- }
- finally {
- try {if ( is != null ) is.close();}
- catch ( IOException e ) {}
-
- try {if ( dstPack != null ) dstPack.close();}
- catch ( IOException e ) {}
-
- for ( AbstractPack pack : srcPacks ) {
- try {pack.close();}
- catch ( IOException ex ) {}
- }
- }
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java b/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java
deleted file mode 100644
index ee4a149..0000000
--- a/src/main/java/net/vhati/modmanager/ui/FieldEditorPanel.java
+++ /dev/null
@@ -1,457 +0,0 @@
-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.BorderFactory;
-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, TEXT_AREA, INTEGER, BOOLEAN, SLIDER, COMBO, CHOOSER }
-
- private Map wrappedLabelMap = new HashMap();
- private Map labelMap = new HashMap();
- private Map stringMap = new HashMap();
- private Map textAreaMap = 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.TEXT_AREA ) {
- gridC.anchor = GridBagConstraints.WEST;
- JTextArea valueArea = new JTextArea();
- valueArea.setEditable( true );
- valueArea.setBorder( BorderFactory.createCompoundBorder( BorderFactory.createEtchedBorder(), BorderFactory.createEmptyBorder( 2, 2, 2, 2 ) ) );
- valueArea.setLineWrap( true );
- valueArea.setWrapStyleWord( true );
- valueArea.setFocusable( true );
- valueArea.setFont( UIManager.getFont( "TextField.font" ) ); // Override small default font on systemLaf.
-
- textAreaMap.put( valueName, valueArea );
- this.add( valueArea, 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( 8 ) );
- JSeparator sep = new JSeparator();
- sep.setPreferredSize( new Dimension( 1, sep.getPreferredSize().height ) );
- panel.add( sep );
- panel.add( Box.createVerticalStrut( 8 ) );
-
- 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 JTextArea getTextArea( String valueName ) {
- return textAreaMap.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 ( JTextArea valueArea : textAreaMap.values() )
- valueArea.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() {
- wrappedLabelMap.clear();
- labelMap.clear();
- stringMap.clear();
- textAreaMap.clear();
- intMap.clear();
- boolMap.clear();
- sliderMap.clear();
- comboMap.clear();
- chooserMap.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/InertPanel.java b/src/main/java/net/vhati/modmanager/ui/InertPanel.java
deleted file mode 100644
index df73ec0..0000000
--- a/src/main/java/net/vhati/modmanager/ui/InertPanel.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.Component;
-import java.awt.Cursor;
-import java.awt.KeyEventDispatcher;
-import java.awt.KeyboardFocusManager;
-import java.awt.Window;
-import java.awt.event.KeyAdapter;
-import java.awt.event.KeyEvent;
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import javax.swing.JFrame;
-import javax.swing.JPanel;
-import javax.swing.SwingUtilities;
-
-
-/**
- * A panel that consumes all mouse/keyboard events, for use as a glass pane.
- */
-public class InertPanel extends JPanel {
-
- private KeyEventDispatcher nullDispatcher;
-
-
- public InertPanel() {
- super();
-
- nullDispatcher = new KeyEventDispatcher() {
- @Override
- public boolean dispatchKeyEvent( KeyEvent e ) {
- Object source = e.getSource();
- if ( source instanceof Component == false ) return false;
-
- Window ancestor = SwingUtilities.getWindowAncestor( (Component)source );
- if ( ancestor instanceof JFrame == false ) return false;
-
- return ( InertPanel.this == ((JFrame)ancestor).getGlassPane() );
- }
- };
-
- this.addMouseListener(new MouseAdapter() {
- @Override
- public void mousePressed( MouseEvent e ) {e.consume();}
- @Override
- public void mouseReleased( MouseEvent e ) {e.consume();}
- });
-
- this.setCursor(Cursor.getPredefinedCursor( Cursor.WAIT_CURSOR ));
- this.setOpaque( false );
- }
-
-
- @Override
- public void setVisible( boolean b ) {
- super.setVisible( b );
- if ( b ) {
- KeyboardFocusManager.getCurrentKeyboardFocusManager().addKeyEventDispatcher( nullDispatcher );
- } else {
- KeyboardFocusManager.getCurrentKeyboardFocusManager().removeKeyEventDispatcher( nullDispatcher );
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java
deleted file mode 100644
index 742e6f7..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java
+++ /dev/null
@@ -1,1171 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Color;
-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;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileFilter;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.OutputStreamWriter;
-import java.nio.charset.Charset;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.Collections;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.locks.Condition;
-import java.util.concurrent.locks.Lock;
-import java.util.concurrent.locks.ReentrantLock;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.swing.BorderFactory;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JFileChooser;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JMenu;
-import javax.swing.JMenuBar;
-import javax.swing.JMenuItem;
-import javax.swing.JOptionPane;
-import javax.swing.JPanel;
-import javax.swing.JSeparator;
-import javax.swing.JSplitPane;
-import javax.swing.JTable;
-import javax.swing.JTextArea;
-import javax.swing.SwingUtilities;
-import javax.swing.UIManager;
-import javax.swing.event.ListSelectionEvent;
-import javax.swing.event.ListSelectionListener;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.vhati.modmanager.core.AutoUpdateInfo;
-import net.vhati.modmanager.core.ComparableVersion;
-import net.vhati.modmanager.core.FTLUtilities;
-import net.vhati.modmanager.core.ModDB;
-import net.vhati.modmanager.core.ModFileInfo;
-import net.vhati.modmanager.core.ModInfo;
-import net.vhati.modmanager.core.ModPatchThread;
-import net.vhati.modmanager.core.ModsScanObserver;
-import net.vhati.modmanager.core.ModsScanThread;
-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.JacksonCatalogWriter;
-import net.vhati.modmanager.json.URLFetcher;
-import net.vhati.modmanager.ui.InertPanel;
-import net.vhati.modmanager.ui.ManagerInitThread;
-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.table.ChecklistTablePanel;
-import net.vhati.modmanager.ui.table.ListState;
-
-
-public class ManagerFrame extends JFrame implements ActionListener, ModsScanObserver, Nerfable, Statusbar, Thread.UncaughtExceptionHandler {
-
- private static final Logger log = LoggerFactory.getLogger( ManagerFrame.class );
-
- public static final String CATALOG_URL = "https://raw.github.com/Vhati/Slipstream-Mod-Manager/master/skel_common/backup/current_catalog.json";
- public static final String APP_UPDATE_URL = "https://raw.github.com/Vhati/Slipstream-Mod-Manager/master/skel_common/backup/auto_update.json";
-
- private File backupDir = new File( "./backup/" );
- private File modsDir = new File( "./mods/" );
-
- private File modsTableStateFile = new File( modsDir, "modorder.txt" );
-
- private File metadataFile = new File( backupDir, "cached_metadata.json" );
-
- private File catalogFile = new File( backupDir, "current_catalog.json" );
- private File catalogETagFile = new File( backupDir, "current_catalog_etag.txt" );
-
- private File appUpdateFile = new File( backupDir, "auto_update.json" );
- private File appUpdateETagFile = new File( backupDir, "auto_update_etag.txt" );
-
- private boolean disposeNormally = true;
- private boolean ranInit = false;
- private Thread.UncaughtExceptionHandler previousUncaughtExceptionHandler = null;
-
- private final Lock managerLock = new ReentrantLock();
- private final Condition scanEndedCond = managerLock.newCondition();
- private boolean scanning = false;
-
- private SlipstreamConfig appConfig;
- private String appName;
- private ComparableVersion appVersion;
- private String appURL;
- private String appAuthor;
-
- private Map modFileHashes = new HashMap();
- private Map modFileDates = new HashMap();
- private ModDB catalogModDB = new ModDB();
- private ModDB localModDB = new ModDB();
-
- private AutoUpdateInfo appUpdateInfo = null;
- private Color updateBtnDisabledColor = UIManager.getColor( "Button.foreground" );
- private Color updateBtnEnabledColor = new Color( 0, 124, 0 );
-
- private NerfListener nerfListener = new NerfListener( this );
-
- private ChecklistTablePanel modsTablePanel;
-
- private JMenuBar menubar;
- private JMenu fileMenu;
- private JMenuItem rescanMenuItem;
- private JMenuItem extractDatsMenuItem;
- private JMenuItem createModMenuItem;
- private JMenuItem sandboxMenuItem;
- private JMenuItem configMenuItem;
- private JMenuItem exitMenuItem;
- private JMenu helpMenu;
- private JMenuItem deleteBackupsMenuItem;
- private JMenuItem steamVerifyIntegrityMenuItem;
- private JMenuItem aboutMenuItem;
-
- private JButton patchBtn;
- private JButton toggleAllBtn;
- private JButton validateBtn;
- private JButton modsFolderBtn;
- private JButton updateBtn;
- private JSplitPane splitPane;
- private ModInfoArea infoArea;
-
- private JLabel statusLbl;
-
-
- public ManagerFrame( SlipstreamConfig appConfig, String appName, ComparableVersion appVersion, String appURL, String appAuthor ) {
- super();
- this.appConfig = appConfig;
- this.appName = appName;
- this.appVersion = appVersion;
- this.appURL = appURL;
- this.appAuthor = appAuthor;
-
- this.setTitle( String.format( "%s v%s", appName, appVersion ) );
- this.setDefaultCloseOperation( JFrame.DO_NOTHING_ON_CLOSE );
-
- JPanel contentPane = new JPanel( new BorderLayout() );
-
- JPanel mainPane = new JPanel( new BorderLayout() );
- contentPane.add( mainPane, BorderLayout.CENTER );
-
- JPanel topPanel = new JPanel( new BorderLayout() );
-
- modsTablePanel = new ChecklistTablePanel();
- topPanel.add( modsTablePanel, BorderLayout.CENTER );
-
- JPanel modActionsPanel = new JPanel();
- modActionsPanel.setLayout( new BoxLayout( modActionsPanel, BoxLayout.Y_AXIS ) );
- modActionsPanel.setBorder( BorderFactory.createEmptyBorder( 5,5,5,5 ) );
- Insets actionInsets = new Insets( 5,10,5,10 );
-
- patchBtn = new JButton( "Patch" );
- patchBtn.setMargin( actionInsets );
- patchBtn.addMouseListener( new StatusbarMouseListener( this, "Incorporate all selected mods into the game. Or revert to vanilla, if none are." ) );
- patchBtn.addActionListener( this );
- modActionsPanel.add( patchBtn );
-
- toggleAllBtn = new JButton( "Toggle All" );
- toggleAllBtn.setMargin( actionInsets );
- toggleAllBtn.addMouseListener( new StatusbarMouseListener( this, "Select all mods, or none." ) );
- toggleAllBtn.addActionListener( this );
- modActionsPanel.add( toggleAllBtn );
-
- validateBtn = new JButton( "Validate" );
- validateBtn.setMargin( actionInsets );
- validateBtn.addMouseListener( new StatusbarMouseListener( this, "Check selected mods for problems." ) );
- validateBtn.addActionListener( this );
- modActionsPanel.add( validateBtn );
-
- modsFolderBtn = new JButton( "Open mods/" );
- modsFolderBtn.setMargin( actionInsets );
- modsFolderBtn.addMouseListener( new StatusbarMouseListener( this, String.format( "Open the %s/ folder.", modsDir.getName() ) ) );
- modsFolderBtn.addActionListener( this );
- modsFolderBtn.setEnabled( Desktop.isDesktopSupported() );
- modActionsPanel.add( modsFolderBtn );
-
- updateBtn = new JButton( "Update" );
- updateBtn.setMargin( actionInsets );
- updateBtn.addMouseListener( new StatusbarMouseListener( this, String.format( "Show info about the latest version of %s.", appName ) ) );
- updateBtn.addActionListener( this );
- updateBtn.setForeground( updateBtnDisabledColor );
- updateBtn.setEnabled( false );
- modActionsPanel.add( updateBtn );
-
- topPanel.add( modActionsPanel, BorderLayout.EAST );
-
- JButton[] actionBtns = new JButton[] {patchBtn, toggleAllBtn, validateBtn, modsFolderBtn, updateBtn };
- int actionBtnWidth = Integer.MIN_VALUE;
- int actionBtnHeight = Integer.MIN_VALUE;
- for ( JButton btn : actionBtns ) {
- actionBtnWidth = Math.max( actionBtnWidth, btn.getPreferredSize().width );
- actionBtnHeight = Math.max( actionBtnHeight, btn.getPreferredSize().height );
- }
- for ( JButton btn : actionBtns ) {
- Dimension size = new Dimension( actionBtnWidth, actionBtnHeight );
- btn.setPreferredSize( size );
- btn.setMinimumSize( size );
- btn.setMaximumSize( size );
- }
-
- infoArea = new ModInfoArea();
- infoArea.setPreferredSize( new Dimension( 504, 220 ) );
- infoArea.setStatusbar( this );
-
- splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
- splitPane.setTopComponent( topPanel );
- splitPane.setBottomComponent( infoArea );
- mainPane.add( splitPane, BorderLayout.CENTER );
-
- JPanel statusPanel = new JPanel();
- statusPanel.setLayout( new BoxLayout( statusPanel, BoxLayout.Y_AXIS ) );
- statusPanel.setBorder( BorderFactory.createLoweredBevelBorder() );
- statusLbl = new JLabel(" ");
- statusLbl.setBorder( BorderFactory.createEmptyBorder( 2, 4, 2, 4 ) );
- statusLbl.setAlignmentX( Component.LEFT_ALIGNMENT );
- statusPanel.add( statusLbl );
- contentPane.add( statusPanel, BorderLayout.SOUTH );
-
-
- this.addWindowListener(new WindowAdapter() {
- @Override
- public void windowClosing( WindowEvent e ) {
- // The close button was clicked.
-
- // This is where an "Are you sure?" popup could go.
- ManagerFrame.this.setVisible( false );
- ManagerFrame.this.dispose();
-
- // The following would also trigger this callback.
- //Window w = ...;
- //w.getToolkit().getSystemEventQueue().postEvent( new WindowEvent(w, WindowEvent.WINDOW_CLOSING) );
- }
-
- @Override
- public void windowClosed( WindowEvent e ) {
- // dispose() was called.
-
- // Restore the previous exception handler.
- if ( ranInit ) Thread.setDefaultUncaughtExceptionHandler( previousUncaughtExceptionHandler );
-
- if ( !disposeNormally ) return; // Something bad happened. Exit quickly.
-
- ListState tableState = getCurrentModsTableState();
- saveModsTableState( tableState );
-
- SlipstreamConfig appConfig = ManagerFrame.this.appConfig;
-
- if ( appConfig.getProperty( SlipstreamConfig.REMEMBER_GEOMETRY ).equals( "true" ) ) {
- 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( SlipstreamConfig.MANAGER_GEOMETRY, geometry );
- }
- }
-
- try {
- appConfig.writeConfig();
- }
- catch ( IOException f ) {
- log.error( String.format( "Error writing config to \"%s\"", appConfig.getConfigFile().getName() ), f );
- }
-
- try {
- JacksonCatalogWriter.write( localModDB.getCollatedModInfo(), metadataFile );
- }
- catch ( IOException f ) {
- log.error( String.format( "Error writing metadata from local mods to \"%s\"", metadataFile.getName() ), f );
- }
-
- System.gc();
- //System.exit( 0 ); // Don't do this (InterruptedException). Let EDT end gracefully.
- }
- });
-
- // Highlighted row shows mod info.
- modsTablePanel.getTable().getSelectionModel().addListSelectionListener(new ListSelectionListener() {
- @Override
- public void valueChanged( ListSelectionEvent e ) {
- if ( e.getValueIsAdjusting() ) return;
-
- int row = modsTablePanel.getTable().getSelectedRow();
- if ( row == -1 ) return;
-
- ModFileInfo modFileInfo = modsTablePanel.getTableModel().getItem( row );
- showLocalModInfo( modFileInfo );
- }
- });
-
- menubar = new JMenuBar();
- fileMenu = new JMenu( "File" );
- fileMenu.setMnemonic( KeyEvent.VK_F );
- rescanMenuItem = new JMenuItem( "Re-Scan mods/" );
- rescanMenuItem.addMouseListener( new StatusbarMouseListener( this, "Check the mods/ folder for new files." ) );
- rescanMenuItem.addActionListener( this );
- fileMenu.add( rescanMenuItem );
- extractDatsMenuItem = new JMenuItem( "Extract Dats..." );
- extractDatsMenuItem.addMouseListener( new StatusbarMouseListener( this, "Extract FTL resources into a folder." ) );
- extractDatsMenuItem.addActionListener( this );
- fileMenu.add( extractDatsMenuItem );
- fileMenu.add( new JSeparator() );
- createModMenuItem = new JMenuItem( "New Mod..." );
- createModMenuItem.addMouseListener( new StatusbarMouseListener( this, "Generate boilerplace for a new mod." ) );
- createModMenuItem.addActionListener( this );
- fileMenu.add( createModMenuItem );
- sandboxMenuItem = new JMenuItem( "XML Sandbox..." );
- sandboxMenuItem.addMouseListener( new StatusbarMouseListener( this, "Experiment with advanced mod syntax." ) );
- sandboxMenuItem.addActionListener( this );
- fileMenu.add( sandboxMenuItem );
- fileMenu.add( new JSeparator() );
- 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." ) );
- exitMenuItem.addActionListener( this );
- fileMenu.add( exitMenuItem );
- menubar.add( fileMenu );
- helpMenu = new JMenu( "Help" );
- helpMenu.setMnemonic( KeyEvent.VK_H );
- deleteBackupsMenuItem = new JMenuItem( "Delete Backups" );
- deleteBackupsMenuItem.addMouseListener( new StatusbarMouseListener( this, "Delete backed up resources." ) );
- deleteBackupsMenuItem.addActionListener( this );
- helpMenu.add( deleteBackupsMenuItem );
- steamVerifyIntegrityMenuItem = new JMenuItem( "Steam: Verify integrity of game files" );
- steamVerifyIntegrityMenuItem.addMouseListener( new StatusbarMouseListener( this, "Tell Steam to 'Verify integrity of game files'." ) );
- steamVerifyIntegrityMenuItem.addActionListener( this );
- helpMenu.add( steamVerifyIntegrityMenuItem );
- helpMenu.add( new JSeparator() );
- aboutMenuItem = new JMenuItem( "About" );
- aboutMenuItem.addMouseListener( new StatusbarMouseListener( this, "Show info about this application." ) );
- aboutMenuItem.addActionListener( this );
- helpMenu.add( aboutMenuItem );
- menubar.add( helpMenu );
- this.setJMenuBar( menubar );
-
- this.setGlassPane( new InertPanel() );
-
- this.setContentPane( contentPane );
- this.pack();
- this.setMinimumSize( new Dimension( 300, modActionsPanel.getPreferredSize().height+90 ) );
- this.setLocationRelativeTo( null );
-
- if ( appConfig.getProperty( SlipstreamConfig.REMEMBER_GEOMETRY ).equals( "true" ) )
- setGeometryFromConfig();
-
- showAboutInfo();
- }
-
- private void setGeometryFromConfig() {
- String geometry = appConfig.getProperty( SlipstreamConfig.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 one-time initialization that must be called after the constructor.
- */
- public void init() {
- if ( ranInit ) return;
- ranInit = true;
-
- previousUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
- Thread.setDefaultUncaughtExceptionHandler( this );
-
- ManagerInitThread initThread = new ManagerInitThread(
- this,
- new SlipstreamConfig( appConfig ),
- modsDir,
- modsTableStateFile,
- metadataFile,
- catalogFile,
- catalogETagFile,
- appUpdateFile,
- appUpdateETagFile
- );
- initThread.setDaemon( true );
- initThread.setPriority( Thread.MIN_PRIORITY );
- initThread.start();
- }
-
-
- /**
- * Returns a ListState describing content in the mods table.
- */
- public ListState getCurrentModsTableState() {
- ListState tableState = new ListState();
-
- for ( ModFileInfo modFileInfo : modsTablePanel.getAllItems() ) {
- tableState.addItem( modFileInfo );
- }
-
- return tableState;
- }
-
- /**
- * Synchronizes a mods table state with a pool of available items.
- *
- * Items in the table that also are in the pool are unchanged.
- * Items in the table that aren't in the pool are pruned.
- * Items in the pool that weren't in the table are appended in ascending
- * order.
- *
- * @param tableState an existing state to amend
- * @param unsortedMods the pool of currently available local mods
- */
- public void amendModsTableState( ListState tableState, List unsortedMods ) {
- List availableMods = new ArrayList( unsortedMods );
- Collections.sort( availableMods );
-
- for ( ModFileInfo modFileInfo : availableMods ) {
- if ( !tableState.containsItem( modFileInfo ) ) {
- tableState.addItem( modFileInfo );
- }
- }
- for ( ModFileInfo modFileInfo : tableState.getItems() ) {
- if ( !availableMods.contains( modFileInfo ) ) {
- tableState.removeItem( modFileInfo );
- }
- }
- }
-
-
- private void saveModsTableState( ListState tableState ) {
- BufferedWriter bw = null;
- try {
- FileOutputStream os = new FileOutputStream( modsTableStateFile );
- bw = new BufferedWriter(new OutputStreamWriter( os, Charset.forName( "UTF-8" ) ));
-
- for ( ModFileInfo modFileInfo : tableState.getItems() ) {
- bw.write( modFileInfo.getFile().getName() );
- bw.write( "\r\n" );
- }
- bw.flush();
- }
- catch ( IOException e ) {
- log.error( String.format( "Error writing \"%s\"", modsTableStateFile.getName() ), e );
- }
- finally {
- try {if ( bw != null ) bw.close();}
- catch (Exception e) {}
- }
- }
-
-
- /**
- * Clears and syncs the mods list with mods/ dir, then starts a new hash
- * thread.
- */
- public void rescanMods( ListState tableState ) {
- managerLock.lock();
- try {
- if ( scanning ) return;
- scanning = true;
- rescanMenuItem.setEnabled( !scanning );
- }
- finally {
- managerLock.unlock();
- }
-
- modFileHashes.clear();
- modsTablePanel.clear();
-
- boolean allowZip = appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ).equals( "true" );
- File[] modFiles = modsDir.listFiles( new ModFileFilter( allowZip ) );
-
- List unsortedMods = new ArrayList();
- for ( File f : modFiles ) {
- ModFileInfo modFileInfo = new ModFileInfo( f );
- unsortedMods.add( modFileInfo );
- }
- amendModsTableState( tableState, unsortedMods );
-
- for ( ModFileInfo modFileInfo : tableState.getItems() ) {
- modsTablePanel.getTableModel().addItem( modFileInfo );
- }
-
- ModsScanThread scanThread = new ModsScanThread( modFiles, localModDB, this );
- scanThread.setDaemon( true );
- scanThread.setPriority( Thread.MIN_PRIORITY );
- scanThread.start();
- }
-
-
- public void showAboutInfo() {
- String body = ""
- + "- Drag to reorder mods.\n"
- + "- Click the checkboxes to select.\n"
- + "- Click 'Patch' to apply mods ( select none for vanilla ).\n"
- + "\n"
- + "Thanks for using this mod manager.\n"
- + "Make sure to visit the forum for updates!";
-
- infoArea.setDescription( appName, appAuthor, appVersion.toString(), appURL, body );
- }
-
- public void showAppUpdateInfo() {
- StringBuilder buf = new StringBuilder();
-
- try {
- infoArea.clear();
- infoArea.appendTitleText( "What's New\n" );
-
- // Links.
- infoArea.appendRegularText( String.format( "Version %s: ", appUpdateInfo.getLatestVersion().toString() ) );
- boolean first = true;
- for ( Map.Entry entry : appUpdateInfo.getLatestURLs().entrySet() ) {
- if ( !first ) infoArea.appendRegularText( " " );
- infoArea.appendRegularText( "[" );
- infoArea.appendLinkText( entry.getValue(), entry.getKey() );
- infoArea.appendRegularText( "]" );
- first = false;
- }
- infoArea.appendRegularText( "\n" );
- infoArea.appendRegularText( "\n" );
-
- // Notice.
- if ( appUpdateInfo.getNotice() != null && appUpdateInfo.getNotice().length() > 0 ) {
- infoArea.appendRegularText( appUpdateInfo.getNotice() );
- infoArea.appendRegularText( "\n" );
- infoArea.appendRegularText( "\n" );
- }
-
- // Changelog.
- for ( Map.Entry> entry : appUpdateInfo.getChangelog().entrySet() ) {
- if ( appVersion.compareTo( entry.getKey() ) >= 0 ) break;
-
- if ( buf.length() > 0 ) buf.append( "\n" );
- buf.append( entry.getKey() ).append( ":\n" );
-
- for ( String change : entry.getValue() ) {
- buf.append( " - " ).append( change ).append( "\n" );
- }
- }
- infoArea.appendRegularText( buf.toString() );
-
- infoArea.setCaretPosition( 0 );
- }
- catch ( Exception e ) {
- log.error( "Error filling info text area", e );
- }
- }
-
- /**
- * Shows info about a local mod in the text area.
- *
- * Priority is given to embedded metadata.xml, but when that's absent,
- * the gatalog's info is used. If the catalog doesn't have the info,
- * an 'info missing' notice is shown instead.
- */
- public void showLocalModInfo( ModFileInfo modFileInfo ) {
- String modHash = modFileHashes.get( modFileInfo.getFile() );
-
- ModInfo modInfo = localModDB.getModInfo( modHash );
- if ( modInfo == null || modInfo.isBlank() ) {
- modInfo = catalogModDB.getModInfo( modHash );
- }
-
- if ( modInfo != null ) {
- infoArea.setDescription( modInfo.getTitle(), modInfo.getAuthor(), modInfo.getVersion(), modInfo.getURL(), modInfo.getDescription() );
- }
- else {
- boolean notYetReady = isScanning();
-
- if ( notYetReady ) {
- String body = ""
- + "No info is currently available for the selected mod.\n\n"
- + "But Slipstream has not yet finished scanning the mods/ folder. "
- + "Try clicking this mod again after waiting a few seconds.";
-
- infoArea.setDescription( modFileInfo.getName(), body );
- }
- else {
- Date modDate = modFileDates.get( modHash );
- if ( modDate == null ) {
- long epochTime = -1;
- try {
- epochTime = ModUtilities.getModFileTime( modFileInfo.getFile() );
- }
- catch ( IOException e ) {
- log.error( String.format( "Error while getting modified time of mod file contents for \"%s\"", modFileInfo.getFile() ), e );
- }
- if ( epochTime != -1 ) {
- modDate = new Date( epochTime );
- modFileDates.put( modHash, modDate );
- }
- }
-
- StringBuilder bodyBuf = new StringBuilder();
- bodyBuf.append( "No info is available for the selected mod.\n\n" );
-
- if ( modDate != null ) {
- SimpleDateFormat dateFormat = new SimpleDateFormat( "yyyy-MM-dd" );
- bodyBuf.append( String.format( "It was released some time after %s.\n\n", dateFormat.format( modDate ) ) );
- }
- else {
- bodyBuf.append( "The date of its release could not be determined.\n\n" );
- }
-
- bodyBuf.append( "Mods can include an embedded description, but this one did not.\n" );
-
- infoArea.setDescription( modFileInfo.getName(), bodyBuf.toString() );
- }
- }
- }
-
- public void exitApp() {
- this.setVisible( false );
- this.dispose();
- }
-
-
- @Override
- public void setStatusText( String text ) {
- if ( text.length() > 0 )
- statusLbl.setText( text );
- else
- statusLbl.setText( " " );
- }
-
-
- @Override
- public void actionPerformed( ActionEvent e ) {
- Object source = e.getSource();
-
- if ( source == patchBtn ) {
- List modFiles = new ArrayList();
-
- for ( ModFileInfo modFileInfo : modsTablePanel.getSelectedItems() ) {
- modFiles.add( modFileInfo.getFile() );
- }
-
- File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) );
-
- ModPatchDialog patchDlg = new ModPatchDialog( this, true );
-
- // Offer to run FTL.
- if ( !"true".equals( appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" ) ) ) {
- File exeFile = null;
- String[] exeArgs = null;
-
- // Try to run via Steam.
- if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) {
-
- String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH );
- if ( steamPath.length() > 0 ) {
- exeFile = new File( steamPath );
-
- if ( exeFile.exists() ) {
- exeArgs = new String[] {"-applaunch", FTLUtilities.STEAM_APPID_FTL};
- }
- else {
- log.warn( String.format( "%s does not exist: %s", SlipstreamConfig.STEAM_EXE_PATH, exeFile.getAbsolutePath() ) );
- exeFile = null;
- }
- }
-
- if ( exeFile == null ) {
- log.warn( "Steam executable could not be found, so FTL will be launched directly" );
- }
- }
- // Try to run directly.
- if ( exeFile == null ) {
- exeFile = FTLUtilities.findGameExe( datsDir );
-
- if ( exeFile != null ) {
- exeArgs = new String[0];
- } else {
- log.warn( "FTL executable could not be found" );
- }
- }
-
- if ( exeFile != null ) {
- patchDlg.setSuccessTask( new SpawnGameTask( exeFile, exeArgs ) );
- }
- }
-
- log.info( "" );
- log.info( "Patching..." );
- log.info( "" );
- ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, false, patchDlg );
- patchThread.start();
-
- patchDlg.setVisible( true );
- }
- else if ( source == toggleAllBtn ) {
- modsTablePanel.toggleAllItemSelection();
- }
- else if ( source == validateBtn ) {
- StringBuilder resultBuf = new StringBuilder();
- boolean anyInvalid = false;
-
- for ( ModFileInfo modFileInfo : modsTablePanel.getSelectedItems() ) {
- Report validateReport = ModUtilities.validateModFile( modFileInfo.getFile() );
-
- ReportFormatter formatter = new ReportFormatter();
- formatter.format( validateReport.messages, resultBuf, 0 );
- resultBuf.append( "\n" );
-
- if ( validateReport.outcome == false ) anyInvalid = true;
- }
-
- if ( resultBuf.length() == 0 ) {
- resultBuf.append( "No mods were checked." );
- }
- else if ( anyInvalid ) {
- resultBuf.append( "\n" );
- resultBuf.append( "FTL itself can tolerate lots of XML typos and still run. But malformed XML may " );
- resultBuf.append( "break tools that do proper parsing, and it hinders the development of new " );
- resultBuf.append( "tools.\n" );
- resultBuf.append( "\n" );
- resultBuf.append( "Slipstream will try to parse XML while patching: first strictly, then failing " );
- resultBuf.append( "over to a sloppy parser. The sloppy parser will tolerate similar errors, at the " );
- resultBuf.append( "risk of unforseen behavior, so satisfying the strict parser is advised.\n" );
- }
- infoArea.setDescription( "Results", resultBuf.toString() );
- }
- else if ( source == modsFolderBtn ) {
- try {
- if ( Desktop.isDesktopSupported() ) {
- Desktop.getDesktop().open( modsDir.getCanonicalFile() );
- } else {
- log.error( String.format( "Java cannot open the %s/ folder for you on this OS", modsDir.getName() ) );
- }
- }
- catch ( IOException f ) {
- log.error( "Error opening mods/ folder", f );
- }
- }
- else if ( source == updateBtn ) {
- showAppUpdateInfo();
- }
- else if ( source == rescanMenuItem ) {
- setStatusText( "" );
- if ( rescanMenuItem.isEnabled() == false ) return;
-
- ListState tableState = getCurrentModsTableState();
- rescanMods( tableState );
- }
- else if ( source == extractDatsMenuItem ) {
- setStatusText( "" );
- JFileChooser extractChooser = new JFileChooser();
- extractChooser.setDialogTitle( "Choose a dir to extract into" );
- extractChooser.setFileHidingEnabled( false );
- extractChooser.setFileSelectionMode( JFileChooser.DIRECTORIES_ONLY );
- extractChooser.setMultiSelectionEnabled( false );
-
- if ( extractChooser.showSaveDialog( this ) != JFileChooser.APPROVE_OPTION )
- return;
-
- File extractDir = extractChooser.getSelectedFile();
-
- File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) );
-
- DatExtractDialog extractDlg = new DatExtractDialog( this, extractDir, datsDir );
- extractDlg.extract();
- extractDlg.setVisible( true );
- }
- else if ( source == createModMenuItem ) {
- setStatusText( "" );
-
- CreateModDialog createModDlg = new CreateModDialog( ManagerFrame.this, modsDir );
- createModDlg.addWindowListener( nerfListener );
- //configDlg.setSize( 300, 400 );
- createModDlg.setLocationRelativeTo( null );
- createModDlg.setVisible( true );
- }
- else if ( source == sandboxMenuItem ) {
- setStatusText( "" );
- File datsDir = new File( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH ) );
-
- ModXMLSandbox sandboxFrame = new ModXMLSandbox( datsDir );
- sandboxFrame.addWindowListener( nerfListener );
- sandboxFrame.setSize( 800, 600 );
- sandboxFrame.setLocationRelativeTo( null );
- sandboxFrame.setVisible( true );
- }
- else if ( source == configMenuItem ) {
- setStatusText( "" );
-
- SlipstreamConfigDialog configDlg = new SlipstreamConfigDialog( ManagerFrame.this, appConfig );
- configDlg.addWindowListener( nerfListener );
- //configDlg.setSize( 300, 400 );
- configDlg.setLocationRelativeTo( null );
- configDlg.setVisible( true );
- }
- else if ( source == exitMenuItem ) {
- setStatusText( "" );
- exitApp();
- }
- else if ( source == deleteBackupsMenuItem ) {
- String deletePrompt = ""
- + "Slipstream uses backups to revert FTL to a state without mods.\n"
- + "You are about to delete them.\n"
- + "\n"
- + "The next time you click 'patch', Slipstream will create fresh backups.\n"
- + "\n"
- + "FTL *must be* in a working unmodded state *before* you click 'patch'.\n"
- + "\n"
- + "To get FTL into a working unmodded state, you may need to reinstall FTL\n"
- + "or use Steam's \"Verify integrity of game files\" feature.\n"
- + "\n"
- + "Whenever FTL is updated, you will need to delete stale backups or the\n"
- + "game will break.\n"
- + "\n"
- + "Are you sure you want to continue?";
-
- int response = JOptionPane.showConfirmDialog( ManagerFrame.this, deletePrompt, "Continue?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.YES_OPTION ) {
-
- List failures = new ArrayList( 2 );
- boolean backupsExist = false;
- for ( String datName : new String[] {"ftl.dat", "data.dat", "resource.dat"} ) {
- File bakFile = new File( backupDir, datName +".bak" );
- if ( bakFile.exists() ) {
- backupsExist = true;
-
- if ( !bakFile.delete() ) {
- log.error( "Unable to delete backup: "+ bakFile.getName() );
- failures.add( bakFile.getName() );
- }
- }
- }
- if ( !backupsExist ) {
- JOptionPane.showMessageDialog( ManagerFrame.this, "There were no backups to delete.", "Nothing to do", JOptionPane.INFORMATION_MESSAGE );
- }
- else if ( failures.isEmpty() ) {
- JOptionPane.showMessageDialog( ManagerFrame.this, "Backups were deleted successfully.", "Success", JOptionPane.INFORMATION_MESSAGE );
- }
- else {
- StringBuilder failBuf = new StringBuilder( "The following files couldn't be deleted:" );
- for ( String s : failures ) {
- failBuf.append( "- \"" ).append( s ).append( "\"\n" );
- }
- failBuf.append( "\nTry going in the \"SMM/backup/\" folder and deleting them manually?" );
- JOptionPane.showMessageDialog( ManagerFrame.this, failBuf.toString(), "Error", JOptionPane.ERROR_MESSAGE );
-
- try {
- if ( Desktop.isDesktopSupported() ) {
- Desktop.getDesktop().open( backupDir.getCanonicalFile() );
- } else {
- log.error( String.format( "Java cannot open the %s/ folder for you on this OS", backupDir.getName() ) );
- }
- }
- catch ( IOException f ) {
- log.error( String.format( "Error opening %s/ folder", backupDir.getName() ), f );
- }
- }
- }
- }
- else if ( source == steamVerifyIntegrityMenuItem ) {
- String exePath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" );
- File exeFile = null;
- if ( exePath.length() == 0 || !(exeFile=new File( exePath )).exists() ) {
- log.warn( "Steam's location was either not set or doesn't exist" );
- JOptionPane.showMessageDialog( ManagerFrame.this, "Steam's location was either not set or doesn't exist.", "Nothing to do", JOptionPane.WARNING_MESSAGE );
- return;
- }
-
- String verifyPrompt = ""
- + "Slipstream is about to tell Steam to re-download FTL's resources. This will get\n"
- + "the game back to a working unmodded state, but it could take a while.\n"
- + "\n"
- + "You can do it manually like this...\n"
- + "- Go to Steam's Library.\n"
- + "- Right-click FTL, choose \"Properties\".\n"
- + "- Click the \"Verify integrity of game files...\" button.\n"
- + "\n"
- + "If you do not have Steam, you will need to reinstall FTL instead.\n"
- + "\n"
- + "Either way, you should delete Slipstream's backups as well.\n"
- + "\n"
- + "Are you sure you want to continue?";
-
- int response = JOptionPane.showConfirmDialog( ManagerFrame.this, verifyPrompt, "Continue?", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.YES_OPTION ) {
- try {
- FTLUtilities.verifySteamGameCache( exeFile, FTLUtilities.STEAM_APPID_FTL );
- }
- catch ( IOException f ) {
- log.error( "Couldn't tell Steam to 'verify integrity of game files'", f );
- }
- }
-
- }
- else if ( source == aboutMenuItem ) {
- setStatusText( "" );
- showAboutInfo();
- }
- }
-
-
- @Override
- public void hashCalculated( final File f, final String hash ) {
- Runnable r = new Runnable() {
- @Override
- public void run() { modFileHashes.put( f, hash ); }
- };
- if ( SwingUtilities.isEventDispatchThread() ) r.run();
- else SwingUtilities.invokeLater( r );
- }
-
- @Override
- public void localModDBUpdated( ModDB newDB ) {
- setLocalModDB( newDB );
- }
-
- @Override
- public void modsScanEnded() {
- Runnable r = new Runnable() {
- @Override
- public void run() {
- managerLock.lock();
- try {
- scanning = false;
- rescanMenuItem.setEnabled( !scanning );
- scanEndedCond.signalAll();
- }
- finally {
- managerLock.unlock();
- }
- }
- };
- if ( SwingUtilities.isEventDispatchThread() ) r.run();
- else SwingUtilities.invokeLater( r );
- }
-
-
- /**
- * Returns a lock for synchronizing thread operations.
- */
- public Lock getLock() {
- return managerLock;
- }
-
- /**
- * Returns a condition that will signal when the "mods/" dir has been scanned.
- *
- * Call getLock().lock() first.
- * Loop while isScanning() is true, calling this condition's await().
- * Finally, call getLock().unlock().
- */
- public Condition getScanEndedCondition() {
- return scanEndedCond;
- }
-
- /**
- * Returns true if the "mods/" folder is currently being scanned. (thread-safe)
- */
- public boolean isScanning() {
- managerLock.lock();
- try {
- return scanning;
- }
- finally {
- managerLock.unlock();
- }
- }
-
-
- @Override
- public void setNerfed( boolean b ) {
- Component glassPane = this.getGlassPane();
- if (b) {
- glassPane.setVisible( true );
- glassPane.requestFocusInWindow();
- } else {
- glassPane.setVisible( false );
- }
- }
-
-
- /**
- * Sets the ModDB for local metadata. (thread-safe)
- */
- public void setLocalModDB( final ModDB newDB ) {
- Runnable r = new Runnable() {
- @Override
- public void run() { localModDB = newDB; }
- };
- if ( SwingUtilities.isEventDispatchThread() ) r.run();
- else SwingUtilities.invokeLater( r );
- }
-
- /**
- * Sets the ModDB for the catalog.
- */
- public void setCatalogModDB( ModDB newDB ) {
- catalogModDB = newDB;
- }
-
- /**
- * Sets info about available app updates.
- */
- public void setAppUpdateInfo( AutoUpdateInfo aui ) {
- appUpdateInfo = aui;
- boolean isUpdateAvailable = ( appVersion.compareTo(appUpdateInfo.getLatestVersion()) < 0 );
- updateBtn.setForeground( isUpdateAvailable ? updateBtnEnabledColor : updateBtnDisabledColor );
- updateBtn.setEnabled( isUpdateAvailable );
- }
-
- /**
- * Toggles whether to perform the usual actions after disposal.
- *
- * Set this to false before an abnormal exit.
- */
- public void setDisposeNormally( boolean b ) {
- disposeNormally = b;
- }
-
- @Override
- public void uncaughtException( Thread t, Throwable e ) {
- log.error( "Uncaught exception in thread: "+ t.toString(), e );
-
- final String threadString = t.toString();
- final String errString = e.toString();
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- String message = ""
- + "An unexpected error has occurred.\n"
- + "\n"
- + "Error: "+ errString +"\n"
- + "\n"
- + "See the log for details.\n"
- + "\n"
- + "If this interrupted patching, FTL's resources were probably corrupted.\n"
- + "Restart Slipstream and patch without mods to restore vanilla backups.";
-
- JOptionPane.showMessageDialog( ManagerFrame.this, message, "Error", JOptionPane.ERROR_MESSAGE );
- }
- });
- }
-
-
-
- private class SpawnGameTask implements Runnable {
- private final File exeFile;
- private final String[] exeArgs;
-
- public SpawnGameTask( File exeFile, String... exeArgs ) {
- if ( exeArgs == null ) exeArgs = new String[0];
- this.exeFile = exeFile;
- this.exeArgs = new String[exeArgs.length];
- System.arraycopy( exeArgs, 0, this.exeArgs, 0, exeArgs.length );
- }
-
- @Override
- public void run() {
- if ( exeFile != null ) {
- int response = JOptionPane.showConfirmDialog( ManagerFrame.this, "Do you want to run the game now?", "Ready to Play", JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE );
- if ( response == JOptionPane.YES_OPTION ) {
- log.info( "Running FTL..." );
- try {
- FTLUtilities.launchExe( exeFile, exeArgs );
- }
- catch ( Exception e ) {
- log.error( "Error launching FTL", e );
- }
- exitApp();
- }
- }
- }
- }
-
-
-
- /**
- * 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;
-
- public NerfListener( Nerfable nerfObj ) {
- this.nerfObj = nerfObj;
- }
-
- @Override
- public void windowOpened( WindowEvent e ) {
- nerfObj.setNerfed( true );
- }
- @Override
- public void windowClosed( WindowEvent e ) {
- nerfObj.setNerfed( false );
- }
- }
-
-
-
- private static class ModFileFilter implements FileFilter {
- boolean allowZip;
-
- public ModFileFilter( boolean allowZip ) {
- this.allowZip = allowZip;
- }
-
- @Override
- public boolean accept( File f ) {
- if ( f.isFile() ) {
- if ( f.getName().endsWith( ".ftl" ) ) return true;
-
- if ( allowZip ) {
- if ( f.getName().endsWith( ".zip" ) ) return true;
- }
- }
- return false;
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerInitThread.java b/src/main/java/net/vhati/modmanager/ui/ManagerInitThread.java
deleted file mode 100644
index 7ad4762..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ManagerInitThread.java
+++ /dev/null
@@ -1,243 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileNotFoundException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.io.IOException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.List;
-import java.util.concurrent.locks.Lock;
-import javax.swing.SwingUtilities;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.vhati.modmanager.core.AutoUpdateInfo;
-import net.vhati.modmanager.core.ModDB;
-import net.vhati.modmanager.core.ModFileInfo;
-import net.vhati.modmanager.core.SlipstreamConfig;
-import net.vhati.modmanager.json.JacksonAutoUpdateReader;
-import net.vhati.modmanager.json.JacksonCatalogReader;
-import net.vhati.modmanager.json.URLFetcher;
-import net.vhati.modmanager.ui.ManagerFrame;
-import net.vhati.modmanager.ui.table.ListState;
-
-
-/**
- * Performs I/O-related setup for ManagerFrame in the background.
- *
- * Reads cached local metadata.
- * Rescans the "mods/" folder.
- * Reads saved catalog, and redownloads if stale.
- * Reads saved info about app updates, and redownloads if stale.
- */
-public class ManagerInitThread extends Thread {
-
- private static final Logger log = LoggerFactory.getLogger( ManagerInitThread.class );
-
- private final ManagerFrame frame;
- private final SlipstreamConfig appConfig;
- private final File modsDir;
- private final File modsTableStateFile;
- private final File metadataFile;
- private final File catalogFile;
- private final File catalogETagFile;
- private final File appUpdateFile;
- private final File appUpdateETagFile;
-
-
- public ManagerInitThread( ManagerFrame frame, SlipstreamConfig appConfig, File modsDir, File modsTableStateFile, File metadataFile, File catalogFile, File catalogETagFile, File appUpdateFile, File appUpdateETagFile ) {
- super( "init" );
- this.frame = frame;
- this.appConfig = appConfig;
- this.modsDir = modsDir;
- this.modsTableStateFile = modsTableStateFile;
- this.metadataFile = metadataFile;
- this.catalogFile = catalogFile;
- this.catalogETagFile = catalogETagFile;
- this.appUpdateFile = appUpdateFile;
- this.appUpdateETagFile = appUpdateETagFile;
- }
-
-
- @Override
- public void run() {
- try {
- init();
- }
- catch ( Exception e ) {
- log.error( "Error during ManagerFrame init.", e );
- }
- }
-
-
- private void init() throws InterruptedException {
-
- if ( metadataFile.exists() ) {
- // Load cached metadata first, before scanning for new info.
- ModDB cachedDB = JacksonCatalogReader.parse( metadataFile );
- if ( cachedDB != null ) frame.setLocalModDB( cachedDB );
- }
-
- final ListState tableState = loadModsTableState();
-
- Lock managerLock = frame.getLock();
- managerLock.lock();
- try {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() { frame.rescanMods( tableState ); }
- });
-
- // Wait until notified that "mods/" has been scanned.
- while ( frame.isScanning() ) {
- frame.getScanEndedCondition().await();
- }
- }
- finally {
- managerLock.unlock();
- }
-
- int catalogUpdateInterval = appConfig.getPropertyAsInt( "update_catalog", 0 );
- boolean needNewCatalog = false;
-
- // Load the catalog first, before downloading.
- if ( catalogFile.exists() ) reloadCatalog();
-
- if ( catalogUpdateInterval > 0 ) {
- if ( catalogFile.exists() ) {
- // Check if the downloaded catalog is stale.
- if ( isFileStale( catalogFile, catalogUpdateInterval ) ) {
- log.debug( String.format( "Catalog is older than %d days", catalogUpdateInterval ) );
- needNewCatalog = true;
- } else {
- log.debug( "Catalog isn't stale yet" );
- }
- }
- else {
- // Catalog file doesn't exist.
- needNewCatalog = true;
- }
- }
-
- if ( needNewCatalog ) {
- boolean fetched = URLFetcher.refetchURL( ManagerFrame.CATALOG_URL, catalogFile, catalogETagFile );
- if ( fetched && catalogFile.exists() ) {
- reloadCatalog();
- }
- }
-
- // Load the cached info first, before downloading.
- if ( appUpdateFile.exists() ) reloadAppUpdateInfo();
-
- int appUpdateInterval = appConfig.getPropertyAsInt( SlipstreamConfig.UPDATE_APP, 0 );
- boolean needAppUpdate = false;
-
- if ( appUpdateInterval > 0 ) {
- if ( appUpdateFile.exists() ) {
- // Check if the app update info is stale.
- if ( isFileStale( appUpdateFile, appUpdateInterval ) ) {
- log.debug( String.format( "App update info is older than %d days", appUpdateInterval ) );
- needAppUpdate = true;
- } else {
- log.debug( "App update info isn't stale yet" );
- }
- }
- else {
- // App update file doesn't exist.
- needAppUpdate = true;
- }
- }
-
- if ( needAppUpdate ) {
- boolean fetched = URLFetcher.refetchURL( ManagerFrame.APP_UPDATE_URL, appUpdateFile, appUpdateETagFile );
- if ( fetched && appUpdateFile.exists() ) {
- reloadAppUpdateInfo();
- }
- }
- }
-
-
- /**
- * Reads modorder.txt and returns a list of mod names in preferred order.
- */
- private ListState loadModsTableState() {
- List fileNames = new ArrayList();
-
- BufferedReader br = null;
- try {
- FileInputStream is = new FileInputStream( modsTableStateFile );
- br = new BufferedReader( new InputStreamReader( is, Charset.forName( "UTF-8" ) ) );
- String line;
- while ( (line = br.readLine()) != null ) {
- fileNames.add( line );
- }
- }
- catch ( FileNotFoundException e ) {
- }
- catch ( IOException e ) {
- log.error( String.format( "Error reading \"%s\"", modsTableStateFile.getName() ), e );
- fileNames.clear();
- }
- finally {
- try {if ( br != null ) br.close();}
- catch ( Exception e ) {}
- }
-
- ListState result = new ListState();
-
- for ( String fileName : fileNames ) {
- File modFile = new File( modsDir, fileName );
- ModFileInfo modFileInfo = new ModFileInfo( modFile );
- result.addItem( modFileInfo );
- }
-
- return result;
- }
-
-
- private void reloadCatalog() {
- final ModDB currentDB = JacksonCatalogReader.parse( catalogFile );
- if ( currentDB != null ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- frame.setCatalogModDB( currentDB );
- }
- });
- }
- }
-
- private void reloadAppUpdateInfo() {
- final AutoUpdateInfo aui = JacksonAutoUpdateReader.parse( appUpdateFile );
- if ( aui != null ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- frame.setAppUpdateInfo( aui );
- }
- });
- }
- }
-
-
- /**
- * Returns true if a file is older than N days.
- */
- private boolean isFileStale( File f, int maxDays ) {
- Calendar fileCal = Calendar.getInstance();
- fileCal.setTimeInMillis( f.lastModified() );
- fileCal.getTimeInMillis(); // Re-calculate calendar fields.
-
- Calendar freshCal = Calendar.getInstance();
- freshCal.add( Calendar.DATE, maxDays * -1 );
- freshCal.getTimeInMillis(); // Re-calculate calendar fields.
-
- return (fileCal.compareTo( freshCal ) < 0);
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ModInfoArea.java b/src/main/java/net/vhati/modmanager/ui/ModInfoArea.java
deleted file mode 100644
index f7dfae7..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ModInfoArea.java
+++ /dev/null
@@ -1,277 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.Color;
-import java.awt.Cursor;
-import java.awt.Desktop;
-import java.awt.Font;
-import java.awt.Toolkit;
-import java.awt.datatransfer.StringSelection;
-import java.awt.event.ActionEvent;
-import java.awt.event.MouseEvent;
-import java.net.URI;
-import javax.swing.AbstractAction;
-import javax.swing.JPopupMenu;
-import javax.swing.JScrollPane;
-import javax.swing.JTextPane;
-import javax.swing.SwingUtilities;
-import javax.swing.event.MouseInputAdapter;
-import javax.swing.text.AttributeSet;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.DefaultStyledDocument;
-import javax.swing.text.SimpleAttributeSet;
-import javax.swing.text.Style;
-import javax.swing.text.StyleConstants;
-import javax.swing.text.StyleContext;
-import javax.swing.text.StyledDocument;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-import net.vhati.modmanager.ui.Statusbar;
-
-
-public class ModInfoArea extends JScrollPane {
-
- private static final Logger log = LoggerFactory.getLogger( ModInfoArea.class );
-
- private static final String STYLE_REGULAR = "regular";
- private static final String STYLE_HYPERLINK = "hyperlink";
- private static final String STYLE_TITLE = "title";
- private static final String ATTR_HYPERLINK_TARGET = "hyperlink-target";
-
- public static Color COLOR_HYPER = Color.BLUE;
- public static final StyleContext DEFAULT_STYLES = ModInfoArea.getDefaultStyleContext();
-
- private JPopupMenu linkPopup = new JPopupMenu();
-
- private Statusbar statusbar = null;
- private String lastClickedLinkTarget = null;
-
- private JTextPane textPane;
- private StyledDocument doc;
- private boolean browseWorks;
-
-
- public ModInfoArea() {
- this( DEFAULT_STYLES );
- }
-
- public ModInfoArea( StyleContext styleContext ) {
- super();
-
- textPane = new JTextPane();
- textPane.setEditable( false );
-
- doc = new DefaultStyledDocument( styleContext );
- textPane.setStyledDocument( doc );
-
- Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
- if ( desktop != null && desktop.isSupported( Desktop.Action.BROWSE ) ) {
- browseWorks = true;
- }
-
- linkPopup.add( new AbstractAction( "Copy link address" ) {
- @Override
- public void actionPerformed( ActionEvent ae ) {
- if ( lastClickedLinkTarget != null ) {
- Toolkit.getDefaultToolkit().getSystemClipboard().setContents( new StringSelection( lastClickedLinkTarget ), null );
- }
- }
- });
-
- MouseInputAdapter hyperlinkListener = new MouseInputAdapter() {
- private Cursor defaultCursor = new Cursor( Cursor.DEFAULT_CURSOR );
- private Cursor linkCursor = new Cursor( Cursor.HAND_CURSOR );
- private boolean wasOverLink = false;
-
- @Override
- public void mousePressed( MouseEvent e ) {
- if ( e.isConsumed() ) return;
- if ( e.isPopupTrigger() ) showMenu( e );
- }
-
- @Override
- public void mouseReleased( MouseEvent e ) {
- if ( e.isConsumed() ) return;
- if ( e.isPopupTrigger() ) showMenu( e );
- }
-
- @Override
- public void mouseClicked( MouseEvent e ) {
- if ( e.isConsumed() ) return;
- if ( !SwingUtilities.isLeftMouseButton( e ) ) return;
-
- AttributeSet tmpAttr = doc.getCharacterElement( textPane.viewToModel( e.getPoint() ) ).getAttributes();
- Object targetObj = tmpAttr.getAttribute( ATTR_HYPERLINK_TARGET );
- if ( targetObj != null ) {
- Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
- if ( desktop != null && desktop.isSupported( Desktop.Action.BROWSE ) ) {
- try {
- desktop.browse( new URI( targetObj.toString() ) );
- }
- catch ( Exception f ) {
- log.error( "Error browsing clicked url: "+ targetObj.toString(), f );
- }
- }
- e.consume();
- }
- }
-
- @Override
- public void mouseMoved( MouseEvent e ) {
- AttributeSet tmpAttr = doc.getCharacterElement( textPane.viewToModel( e.getPoint() ) ).getAttributes();
- Object targetObj = tmpAttr.getAttribute( ATTR_HYPERLINK_TARGET );
- if ( targetObj != null ) {
- textPane.setCursor( linkCursor );
- if ( statusbar != null )
- statusbar.setStatusText( targetObj.toString() );
- wasOverLink = true;
- }
- else {
- if ( wasOverLink ) {
- textPane.setCursor( defaultCursor );
- if ( statusbar != null )
- statusbar.setStatusText( "" );
- }
- wasOverLink = false;
- }
- }
-
- private void showMenu( MouseEvent e ) {
- AttributeSet tmpAttr = doc.getCharacterElement( textPane.viewToModel( e.getPoint() ) ).getAttributes();
- Object targetObj = tmpAttr.getAttribute( ATTR_HYPERLINK_TARGET );
- if ( targetObj != null ) { // Link menu.
- textPane.requestFocus();
-
- lastClickedLinkTarget = targetObj.toString();
-
- int nx = e.getX();
- if ( nx > 500 ) nx = nx - linkPopup.getSize().width;
-
- linkPopup.show( e.getComponent(), nx, e.getY() - linkPopup.getSize().height );
-
- e.consume();
- }
- }
- };
- textPane.addMouseListener( hyperlinkListener );
- textPane.addMouseMotionListener( hyperlinkListener );
-
- textPane.addMouseListener( new ClipboardMenuMouseListener() );
-
- this.setViewportView( textPane );
- }
-
-
- public void setDescription( String title, String body ) {
- setDescription( title, null, null, null, body );
- }
-
- public void setDescription( String title, String author, String version, String url, String body ) {
- Style regularStyle = doc.getStyle( STYLE_REGULAR );
- try {
- doc.remove( 0, doc.getLength() );
- doc.insertString( doc.getLength(), title +"\n", doc.getStyle( STYLE_TITLE ) );
-
- boolean first = true;
- if ( author != null ) {
- doc.insertString( doc.getLength(), String.format( "%sby %s", (first ? "" : " "), author ), regularStyle );
- first = false;
- }
- if ( version != null ) {
- doc.insertString( doc.getLength(), String.format( "%s(version %s)", (first ? "" : " "), version ), regularStyle );
- first = false;
- }
- if ( !first ) {
- doc.insertString( doc.getLength(), "\n", regularStyle );
- }
-
- if ( url != null ) {
- doc.insertString( doc.getLength(), "Website: ", regularStyle );
-
- if ( browseWorks && url.matches( "^(?:https?|ftp)://.*" ) ) {
- SimpleAttributeSet tmpAttr = new SimpleAttributeSet( doc.getStyle( STYLE_HYPERLINK ) );
- tmpAttr.addAttribute( ATTR_HYPERLINK_TARGET, url );
- doc.insertString( doc.getLength(), "Link", tmpAttr );
- } else {
- doc.insertString( doc.getLength(), url, regularStyle );
- }
-
- doc.insertString( doc.getLength(), "\n", regularStyle );
- }
-
- doc.insertString( doc.getLength(), "\n", regularStyle );
-
- if ( body != null ) {
- doc.insertString( doc.getLength(), body, regularStyle );
- }
- }
- catch ( BadLocationException e ) {
- log.error( "Error filling info text area", e );
- }
-
- textPane.setCaretPosition( 0 );
- }
-
-
- public void setCaretPosition( int n ) {
- textPane.setCaretPosition( n );
- }
-
- public void clear() {
- try {
- doc.remove( 0, doc.getLength() );
- }
- catch ( BadLocationException e ) {
- log.error( "Error clearing info text area", e );
- }
- }
-
- public void appendTitleText( String s ) throws BadLocationException {
- doc.insertString( doc.getLength(), s, doc.getStyle( STYLE_TITLE ) );
- }
-
- public void appendRegularText( String s ) throws BadLocationException {
- doc.insertString( doc.getLength(), s, doc.getStyle( STYLE_REGULAR ) );
- }
-
- public void appendLinkText( String linkURL, String linkTitle ) throws BadLocationException {
- if ( browseWorks && linkURL.matches( "^(?:https?|ftp)://.*" ) ) {
- SimpleAttributeSet tmpAttr = new SimpleAttributeSet( doc.getStyle( STYLE_HYPERLINK ) );
- tmpAttr.addAttribute( ATTR_HYPERLINK_TARGET, linkURL );
- doc.insertString( doc.getLength(), linkTitle, tmpAttr );
- } else {
- doc.insertString( doc.getLength(), linkURL, doc.getStyle( STYLE_REGULAR ) );
- }
- }
-
-
- /**
- * Sets a component with a statusbar to be set during mouse events.
- */
- public void setStatusbar( Statusbar comp ) {
- this.statusbar = comp;
- }
-
-
- private static StyleContext getDefaultStyleContext() {
- StyleContext result = new StyleContext();
- Style defaultStyle = StyleContext.getDefaultStyleContext().getStyle( StyleContext.DEFAULT_STYLE );
- Style baseStyle = result.addStyle( "base", defaultStyle );
-
- Style regularStyle = result.addStyle( STYLE_REGULAR, baseStyle );
- StyleConstants.setFontFamily( regularStyle, Font.MONOSPACED );
- StyleConstants.setFontSize( regularStyle, 12 );
-
- Style hyperStyle = result.addStyle( STYLE_HYPERLINK, regularStyle );
- StyleConstants.setForeground( hyperStyle, COLOR_HYPER );
- StyleConstants.setUnderline( hyperStyle, true );
-
- Style titleStyle = result.addStyle( STYLE_TITLE, baseStyle );
- StyleConstants.setFontFamily( titleStyle, Font.SANS_SERIF );
- StyleConstants.setFontSize( titleStyle, 24 );
- StyleConstants.setBold( titleStyle, true );
-
- return result;
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java
deleted file mode 100644
index bfa7932..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.Frame;
-import java.io.File;
-import javax.swing.JDialog;
-import javax.swing.SwingUtilities;
-
-import net.vhati.modmanager.core.ModPatchObserver;
-import net.vhati.modmanager.ui.ProgressDialog;
-
-
-public class ModPatchDialog extends ProgressDialog implements ModPatchObserver {
-
-
- public ModPatchDialog( Frame owner, boolean continueOnSuccess ) {
- super( owner, continueOnSuccess );
- this.setTitle( "Patching..." );
-
- this.setSize( 400, 160 );
- this.setMinimumSize( this.getPreferredSize() );
- this.setLocationRelativeTo( owner );
- }
-
-
- /**
- * Updates the progress bar.
- *
- * If either arg is -1, the bar will become indeterminate.
- *
- * @param value the new value
- * @param max the new maximum
- */
- @Override
- public void patchingProgress( final int value, final int max ) {
- this.setProgressLater( value, max );
- }
-
- /**
- * Non-specific activity.
- *
- * @param message a string, or null
- */
- @Override
- public void patchingStatus( final String message ) {
- setStatusTextLater( message != null ? message : "..." );
- }
-
- /**
- * A mod is about to be processed.
- */
- @Override
- public void patchingMod( final File modFile ) {
- setStatusTextLater( String.format( "Installing mod \"%s\"...", modFile.getName() ) );
- }
-
- /**
- * Patching ended.
- *
- * If anything went wrong, e may be non-null.
- */
- @Override
- public void patchingEnded( boolean outcome, Exception e ) {
- setTaskOutcomeLater( outcome, e );
- }
-
-
- @Override
- protected void setTaskOutcome( boolean outcome, Exception e ) {
- super.setTaskOutcome( outcome, e );
- if ( !this.isShowing() ) return;
-
- if ( succeeded == true ) {
- setStatusText( "Patching completed." );
- } else {
- setStatusText( String.format( "Patching failed: %s", e ) );
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java b/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java
deleted file mode 100644
index 401ead3..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ModXMLSandbox.java
+++ /dev/null
@@ -1,591 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Component;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.event.ActionEvent;
-import java.awt.event.ActionListener;
-import java.awt.event.FocusAdapter;
-import java.awt.event.FocusEvent;
-import java.awt.event.InputEvent;
-import java.awt.event.KeyEvent;
-import java.io.File;
-import java.io.ByteArrayInputStream;
-import java.io.InputStream;
-import java.io.IOException;
-import java.io.FileNotFoundException;
-import java.io.StringWriter;
-import java.util.ArrayList;
-import java.util.Enumeration;
-import java.util.Iterator;
-import java.util.List;
-import java.util.Set;
-import java.util.TreeSet;
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-import javax.swing.AbstractAction;
-import javax.swing.BorderFactory;
-import javax.swing.Box;
-import javax.swing.BoxLayout;
-import javax.swing.JButton;
-import javax.swing.JComponent;
-import javax.swing.JFrame;
-import javax.swing.JLabel;
-import javax.swing.JPanel;
-import javax.swing.JOptionPane;
-import javax.swing.JScrollPane;
-import javax.swing.JSplitPane;
-import javax.swing.JTabbedPane;
-import javax.swing.JTextArea;
-import javax.swing.JTextField;
-import javax.swing.JTree;
-import javax.swing.KeyStroke;
-import javax.swing.SwingUtilities;
-import javax.swing.event.AncestorEvent;
-import javax.swing.event.AncestorListener;
-import javax.swing.event.CaretEvent;
-import javax.swing.event.CaretListener;
-import javax.swing.event.UndoableEditEvent;
-import javax.swing.event.UndoableEditListener;
-import javax.swing.text.BadLocationException;
-import javax.swing.text.Caret;
-import javax.swing.tree.DefaultMutableTreeNode;
-import javax.swing.tree.DefaultTreeModel;
-import javax.swing.tree.TreePath;
-import javax.swing.undo.CannotRedoException;
-import javax.swing.undo.UndoManager;
-
-import net.vhati.ftldat.AbstractPack;
-import net.vhati.ftldat.FTLPack;
-import net.vhati.ftldat.PkgPack;
-import net.vhati.modmanager.core.ModUtilities;
-import net.vhati.modmanager.core.SloppyXMLOutputProcessor;
-import net.vhati.modmanager.core.XMLPatcher;
-import net.vhati.modmanager.ui.ClipboardMenuMouseListener;
-
-import org.jdom2.JDOMException;
-
-
-/**
- * A basic text editor to test XML modding.
- */
-public class ModXMLSandbox extends JFrame implements ActionListener {
-
- private static final String baseTitle = "Mod XML Sandbox";
-
- private UndoManager undoManager = new UndoManager();
- private String mainText = null;
-
- private File datsDir;
-
- private JTabbedPane areasPane;
- private JScrollPane mainScroll;
- private JScrollPane appendScroll;
- private JScrollPane resultScroll;
- private JSplitPane splitPane;
- private JScrollPane messageScroll;
-
- private JTextArea mainArea;
- private JTextArea appendArea;
- private JTextArea resultArea;
- private JTextArea messageArea;
- private JTextField findField;
- private JButton openBtn;
- private JButton patchBtn;
- private JLabel statusLbl;
-
-
- public ModXMLSandbox( File datsDir ) {
- super( baseTitle );
- this.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
-
- this.datsDir = datsDir;
-
- Font sandboxFont = new Font( Font.MONOSPACED, Font.PLAIN, 13 );
-
- mainArea = new JTextArea();
- mainArea.setTabSize( 4 );
- mainArea.setFont( sandboxFont );
- mainArea.setEditable( false );
- mainArea.addMouseListener( new ClipboardMenuMouseListener() );
- mainScroll = new JScrollPane( mainArea );
-
- appendArea = new JTextArea();
- appendArea.setTabSize( 4 );
- appendArea.setFont( sandboxFont );
- appendArea.addMouseListener( new ClipboardMenuMouseListener() );
- appendScroll = new JScrollPane( appendArea );
-
- resultArea = new JTextArea();
- resultArea.setTabSize( 4 );
- resultArea.setFont( sandboxFont );
- resultArea.setEditable( false );
- resultArea.addMouseListener( new ClipboardMenuMouseListener() );
- resultScroll = new JScrollPane( resultArea );
-
- messageArea = new JTextArea();
- messageArea.setLineWrap( true );
- messageArea.setWrapStyleWord( true );
- messageArea.setTabSize( 4 );
- messageArea.setFont( sandboxFont );
- messageArea.setEditable( false );
- messageArea.addMouseListener( new ClipboardMenuMouseListener() );
- messageArea.setText( "This is a sandbox to tinker with advanced mod syntax.\n1) Open XML from data.dat to fill the 'main' tab. (ctrl-o)\n2) Write some tags in the 'append' tab. (alt-1,2,3)\n3) Click Patch to see what would happen. (ctrl-p)\nUndo/redo is available. (ctrl-z/ctrl-y)" );
- messageScroll = new JScrollPane( messageArea );
-
- JPanel ctrlPanel = new JPanel();
- ctrlPanel.setLayout( new BoxLayout( ctrlPanel, BoxLayout.X_AXIS ) );
-
- openBtn = new JButton( "Open Main..." );
- openBtn.addActionListener( this );
- ctrlPanel.add( openBtn );
-
- ctrlPanel.add( Box.createHorizontalGlue() );
-
- findField = new JTextField( "", 20 );
- findField.setMaximumSize( new Dimension( 60, findField.getPreferredSize().height ) );
- ctrlPanel.add( findField );
-
- ctrlPanel.add( Box.createHorizontalGlue() );
-
- patchBtn = new JButton( "Patch" );
- patchBtn.addActionListener( this );
- ctrlPanel.add( patchBtn );
-
- areasPane = new JTabbedPane( JTabbedPane.BOTTOM );
- areasPane.add( "Main", mainScroll );
- areasPane.add( "Append", appendScroll );
- areasPane.add( "Result", resultScroll );
-
- JPanel topPanel = new JPanel( new BorderLayout() );
- topPanel.add( areasPane, BorderLayout.CENTER );
- topPanel.add( ctrlPanel, BorderLayout.SOUTH );
-
- splitPane = new JSplitPane( JSplitPane.VERTICAL_SPLIT );
- splitPane.setTopComponent( topPanel );
- splitPane.setBottomComponent( messageScroll );
-
- JPanel statusPanel = new JPanel();
- statusPanel.setLayout( new BoxLayout( statusPanel, BoxLayout.Y_AXIS ) );
- statusPanel.setBorder( BorderFactory.createLoweredBevelBorder() );
- statusLbl = new JLabel( " " );
- statusLbl.setBorder( BorderFactory.createEmptyBorder( 2, 4, 2, 4 ) );
- statusLbl.setAlignmentX( Component.LEFT_ALIGNMENT );
- statusPanel.add( statusLbl );
-
- JPanel contentPane = new JPanel( new BorderLayout() );
- contentPane.add( splitPane, BorderLayout.CENTER );
- contentPane.add( statusPanel, BorderLayout.SOUTH );
- this.setContentPane( contentPane );
-
- findField.addFocusListener(new FocusAdapter() {
- @Override
- public void focusGained( FocusEvent e ) {
- findField.selectAll();
- }
- });
- CaretListener caretListener = new CaretListener() {
- @Override
- public void caretUpdate( CaretEvent e ) {
- JTextArea currentArea = getCurrentArea();
- if ( currentArea == null ) return;
- if ( e.getSource() != currentArea ) return;
- updateCaretStatus();
- }
- };
- mainArea.addCaretListener( caretListener );
- appendArea.addCaretListener( caretListener );
- resultArea.addCaretListener( caretListener );
-
- CaretAncestorListener caretAncestorListener = new CaretAncestorListener();
- mainArea.addAncestorListener( caretAncestorListener );
- appendArea.addAncestorListener( caretAncestorListener );
- resultArea.addAncestorListener( caretAncestorListener );
-
- appendArea.getDocument().addUndoableEditListener(new UndoableEditListener() {
- @Override
- public void undoableEditHappened( UndoableEditEvent e ) {
- undoManager.addEdit( e.getEdit() );
- }
- });
- AbstractAction undoAction = new AbstractAction( "Undo" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- try {undoManager.undo();}
- catch ( CannotRedoException f ) {}
- }
- };
- AbstractAction redoAction = new AbstractAction( "Redo" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- try {undoManager.redo();}
- catch ( CannotRedoException f ) {}
- }
- };
-
- AbstractAction openAction = new AbstractAction( "Open" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- open();
- }
- };
- AbstractAction patchAction = new AbstractAction( "Patch" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- patch();
- }
- };
- AbstractAction focusFindAction = new AbstractAction( "Focus Find" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- findField.requestFocusInWindow();
- }
- };
- AbstractAction findNextAction = new AbstractAction( "Find Next" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- findNext();
- }
- };
- AbstractAction findPreviousAction = new AbstractAction( "Find Previous" ) {
- @Override
- public void actionPerformed( ActionEvent e ) {
- findPrevious();
- }
- };
-
- KeyStroke undoShortcut = KeyStroke.getKeyStroke( "control Z" );
- appendArea.getInputMap().put( undoShortcut, "undo" );
- appendArea.getActionMap().put( "undo", undoAction );
- KeyStroke redoShortcut = KeyStroke.getKeyStroke( "control Y" );
- appendArea.getInputMap().put( redoShortcut, "redo" );
- appendArea.getActionMap().put( "redo", redoAction );
-
- KeyStroke openShortcut = KeyStroke.getKeyStroke( "control O" );
- contentPane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( openShortcut, "open" );
- contentPane.getActionMap().put( "open", openAction );
- KeyStroke patchShortcut = KeyStroke.getKeyStroke( "control P" );
- contentPane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( patchShortcut, "patch" );
- contentPane.getActionMap().put( "patch", patchAction );
- KeyStroke focusFindShortcut = KeyStroke.getKeyStroke( "control F" );
- contentPane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( focusFindShortcut, "focus find" );
- contentPane.getActionMap().put( "focus find", focusFindAction );
- KeyStroke findNextShortcut = KeyStroke.getKeyStroke( "F3" );
- contentPane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( findNextShortcut, "find next" );
- contentPane.getActionMap().put( "find next", findNextAction );
- KeyStroke findPreviousShortcut = KeyStroke.getKeyStroke( "shift F3" );
- contentPane.getInputMap( JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT ).put( findPreviousShortcut, "find previous" );
- contentPane.getActionMap().put( "find previous", findPreviousAction );
-
- findField.getInputMap().put( KeyStroke.getKeyStroke( "released ENTER" ), "find next" );
- findField.getActionMap().put( "find next", findNextAction );
-
- areasPane.setMnemonicAt( 0, KeyEvent.VK_1 );
- areasPane.setMnemonicAt( 1, KeyEvent.VK_2 );
- areasPane.setMnemonicAt( 2, KeyEvent.VK_3 );
- mainArea.addAncestorListener( new FocusAncestorListener( mainArea ) );
- appendArea.addAncestorListener( new FocusAncestorListener( appendArea ) );
- resultArea.addAncestorListener( new FocusAncestorListener( resultArea ) );
-
- this.pack();
- }
-
- @Override
- public void setVisible( boolean b ) {
- super.setVisible( b );
-
- if ( b ) {
- // Splitpane has to be realized before the divider can be moved.
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- splitPane.setDividerLocation( 0.80d );
- }
- });
- }
- }
-
- @Override
- public void actionPerformed( ActionEvent e ) {
- Object source = e.getSource();
-
- if ( source == openBtn ) {
- open();
- }
- else if ( source == patchBtn ) {
- patch();
- }
- }
-
- private void open() {
- messageArea.setText( "" );
-
- AbstractPack pack = null;
- InputStream is = null;
- try {
- File ftlDatFile = new File( datsDir, "ftl.dat" );
- File dataDatFile = new File( datsDir, "data.dat" );
-
- if ( ftlDatFile.exists() ) { // FTL 1.6.1.
- pack = new PkgPack( ftlDatFile, "r" );
- }
- else if ( dataDatFile.exists() ) { // FTL 1.01-1.5.13.
- pack = new FTLPack( dataDatFile, "r" );
- }
- else {
- throw new FileNotFoundException( String.format( "Could not find either \"%s\" or \"%s\"", ftlDatFile.getName(), dataDatFile.getName() ) );
- }
-
- List innerPaths = pack.list();
-
- String innerPath = promptForInnerPath( innerPaths );
- if ( innerPath == null ) return;
-
- is = pack.getInputStream( innerPath );
- InputStream rebuiltStream = ModUtilities.rebuildXMLFile( is, "windows-1252", pack.getName()+":"+innerPath );
- String rebuiltText = ModUtilities.decodeText( rebuiltStream, "Sandbox Main XML" ).text;
- is.close();
-
- mainArea.setText( rebuiltText );
- mainArea.setCaretPosition( 0 );
- areasPane.setSelectedComponent( mainScroll );
- resultArea.setText( "" );
- this.setTitle( String.format( "%s - %s", innerPath, baseTitle ) );
- }
- catch ( IOException f ) {
- messageArea.setText( f.getMessage() );
- messageArea.setCaretPosition( 0 );
- }
- catch ( JDOMException f ) {
- messageArea.setText( f.getMessage() );
- messageArea.setCaretPosition( 0 );
- }
- finally {
- try {if ( is != null ) is.close();}
- catch ( IOException f ) {}
-
- try {if ( pack != null ) pack.close();}
- catch ( IOException f ) {}
- }
- }
-
- private void patch() {
- String mainText = mainArea.getText();
- if ( mainText.length() == 0 ) return;
-
- messageArea.setText( "" );
-
- try {
- InputStream mainStream = new ByteArrayInputStream( mainText.getBytes( "UTF-8" ) );
-
- String appendText = appendArea.getText();
- InputStream appendStream = new ByteArrayInputStream( appendText.getBytes( "UTF-8" ) );
-
- InputStream resultStream = ModUtilities.patchXMLFile( mainStream, appendStream, "windows-1252", false, "Sandbox Main XML", "Sandbox Append XML" );
- String resultText = ModUtilities.decodeText( resultStream, "Sandbox Result XML" ).text;
-
- resultArea.setText( resultText );
- resultArea.setCaretPosition( 0 );
- areasPane.setSelectedComponent( resultScroll );
- }
- catch ( Exception e ) {
- messageArea.setText( e.toString() );
- messageArea.setCaretPosition( 0 );
- }
- }
-
- private void findNext() {
- JTextArea currentArea = getCurrentArea();
- if ( currentArea == null ) return;
-
- String query = findField.getText();
- if ( query.length() == 0 ) return;
-
- Caret caret = currentArea.getCaret();
- int from = Math.max( caret.getDot(), caret.getMark() );
-
- Pattern ptn = Pattern.compile( "(?i)"+ Pattern.quote( query ) );
- Matcher m = ptn.matcher( currentArea.getText() );
- if ( m.find(from) ) {
- caret.setDot( m.start() );
- caret.moveDot( m.end() );
- caret.setSelectionVisible( true );
- }
- }
-
- private void findPrevious() {
- JTextArea currentArea = getCurrentArea();
- if ( currentArea == null ) return;
-
- String query = findField.getText();
- if ( query.length() == 0 ) return;
-
- Caret caret = currentArea.getCaret();
- int from = Math.min( caret.getDot(), caret.getMark() );
-
- Pattern ptn = Pattern.compile( "(?i)"+ Pattern.quote(query) );
- Matcher m = ptn.matcher( currentArea.getText() );
- m.region( 0, from );
- int lastStart = -1;
- int lastEnd = -1;
- while ( m.find() ) {
- lastStart = m.start();
- lastEnd = m.end();
- }
- if ( lastStart != -1 ) {
- caret.setDot( lastStart );
- caret.moveDot( lastEnd );
- caret.setSelectionVisible( true );
- }
- }
-
- private void updateCaretStatus() {
- JTextArea currentArea = getCurrentArea();
- if ( currentArea == null ) return;
-
- try {
- int offset = currentArea.getCaretPosition();
- int line = currentArea.getLineOfOffset( offset );
- int lineStart = currentArea.getLineStartOffset( line );
- int col = offset - lineStart;
- int lineCount = currentArea.getLineCount();
- statusLbl.setText( String.format( "Line: %4d/%4d Col: %3d", line+1, lineCount, col+1 ) );
- }
- catch ( BadLocationException e ) {
- statusLbl.setText( String.format( "Line: ???/ ??? Col: ???" ) );
- }
- }
-
- private JTextArea getCurrentArea() {
- if ( areasPane.getSelectedIndex() == 0 )
- return mainArea;
- else if ( areasPane.getSelectedIndex() == 1 )
- return appendArea;
- else if ( areasPane.getSelectedIndex() == 2 )
- return resultArea;
- else
- return null;
- }
-
- /**
- * Shows a modal prompt with a JTree representing a list of paths.
- *
- * @return the selected path, null otherwise
- */
- private String promptForInnerPath( List innerPaths ) {
- String result = null;
-
- Set sortedPaths = new TreeSet( innerPaths );
- for ( Iterator it = sortedPaths.iterator(); it.hasNext(); ) {
- if ( !it.next().endsWith(".xml") ) it.remove();
- }
-
- DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "/" );
- DefaultTreeModel treeModel = new DefaultTreeModel( rootNode );
-
- for ( String innerPath : sortedPaths ) {
- buildTreeFromString( treeModel, innerPath );
- }
-
- JTree pathTree = new JTree( treeModel );
- pathTree.setRootVisible( false );
- for ( int i=0; i < pathTree.getRowCount(); i++ ) {
- pathTree.expandRow( i );
- }
- JScrollPane treeScroll = new JScrollPane( pathTree );
- treeScroll.setPreferredSize( new Dimension( pathTree.getPreferredSize().width, 300 ) );
-
- pathTree.addAncestorListener( new FocusAncestorListener( pathTree ) );
-
- int popupResult = JOptionPane.showOptionDialog( this, treeScroll, "Open an XML Resource", JOptionPane.OK_CANCEL_OPTION, JOptionPane.PLAIN_MESSAGE, null, new String[]{"OK"}, "OK" );
-
- if ( popupResult == JOptionPane.OK_OPTION ) {
- StringBuilder buf = new StringBuilder();
-
- TreePath selectedPath = pathTree.getSelectionPath();
- if ( selectedPath != null ) {
- for ( Object o : selectedPath.getPath() ) {
- DefaultMutableTreeNode pathComp = (DefaultMutableTreeNode)o;
- if ( !pathComp.isRoot() ) {
- Object userObject = pathComp.getUserObject();
- buf.append( userObject.toString() );
- }
- }
- if ( buf.length() > 0 ) result = buf.toString();
- }
- }
-
- return result;
- }
-
- /**
- * Adds TreeNodes, if they don't already exist, based on a shash-delimited string.
- */
- @SuppressWarnings("unchecked")
- private void buildTreeFromString( DefaultTreeModel treeModel, String path ) {
-// Method commented out to get application to compile. Figure out what this did and fix it
-// DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot();
-// DefaultMutableTreeNode currentNode = rootNode;
-//
-// String[] chunks = path.split( "/" );
-//
-// for ( int i=0; i < chunks.length; i++ ) {
-// String chunk = chunks[i];
-// if ( i < chunks.length-1 )
-// chunk += "/";
-//
-// boolean found = false;
-// Enumeration enumIt = currentNode.children();
-// while ( enumIt.hasMoreElements() ) {
-// DefaultMutableTreeNode tmpNode = enumIt.nextElement();
-// if ( chunk.equals( tmpNode.getUserObject() ) ) {
-// found = true;
-// currentNode = tmpNode;
-// break;
-// }
-// }
-// if ( !found ) {
-// DefaultMutableTreeNode newNode = new DefaultMutableTreeNode( chunk );
-// currentNode.insert( newNode, currentNode.getChildCount() );
-// currentNode = newNode;
-// }
-// }
- }
-
-
-
- private class CaretAncestorListener implements AncestorListener {
- @Override
- public void ancestorAdded( AncestorEvent e ) {
- updateCaretStatus();
- }
- @Override
- public void ancestorMoved( AncestorEvent e ) {
- }
- @Override
- public void ancestorRemoved( AncestorEvent e ) {
- }
- }
-
-
-
- private static class FocusAncestorListener implements AncestorListener {
- private JComponent comp;
-
- public FocusAncestorListener( JComponent comp ) {
- this.comp = comp;
- }
-
- @Override
- public void ancestorAdded( AncestorEvent e ) {
- comp.requestFocusInWindow();
- }
- @Override
- public void ancestorMoved( AncestorEvent e ) {
- }
- @Override
- public void ancestorRemoved( AncestorEvent e ) {
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/Nerfable.java b/src/main/java/net/vhati/modmanager/ui/Nerfable.java
deleted file mode 100644
index cba3762..0000000
--- a/src/main/java/net/vhati/modmanager/ui/Nerfable.java
+++ /dev/null
@@ -1,16 +0,0 @@
-package net.vhati.modmanager.ui;
-
-
-/*
- * An interface to en/disable user interaction.
- * It was written with JFrames and glass panes in mind.
- */
-public interface Nerfable {
-
- /*
- * Either nerf or restore user interaction.
- *
- * @param b the nerfed state
- */
- public void setNerfed( boolean b );
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java b/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java
deleted file mode 100644
index 7f98c88..0000000
--- a/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java
+++ /dev/null
@@ -1,224 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Frame;
-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.JDialog;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-import javax.swing.JTextArea;
-import javax.swing.SwingUtilities;
-
-
-public class ProgressDialog extends JDialog implements ActionListener {
-
- protected JScrollPane statusScroll;
- protected JProgressBar progressBar;
- protected JTextArea statusArea;
- protected JButton continueBtn;
-
- protected boolean continueOnSuccess = false;
- protected boolean done = false;
- protected boolean succeeded = false;
- protected Runnable successTask = null;
-
-
- public ProgressDialog( Frame owner, boolean continueOnSuccess ) {
- super( owner, true );
- this.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE );
-
- this.continueOnSuccess = continueOnSuccess;
-
- progressBar = new JProgressBar();
- progressBar.setBorderPainted( true );
-
- JPanel progressHolder = new JPanel( new BorderLayout() );
- progressHolder.setBorder( BorderFactory.createEmptyBorder( 10, 15, 0, 15 ) );
- progressHolder.add( progressBar );
- getContentPane().add( progressHolder, BorderLayout.NORTH );
-
- statusArea = new JTextArea( 5, 50 );
- statusArea.setLineWrap( true );
- statusArea.setWrapStyleWord( true );
- statusArea.setFont( statusArea.getFont().deriveFont( 13f ) );
- statusArea.setEditable( false );
- statusScroll = new JScrollPane( statusArea );
-
- JPanel statusHolder = new JPanel( new BorderLayout() );
- statusHolder.setBorder( BorderFactory.createEmptyBorder( 15, 15, 15, 15 ) );
- statusHolder.add( statusScroll );
- getContentPane().add( statusHolder, BorderLayout.CENTER );
-
- continueBtn = new JButton( "Continue" );
- continueBtn.setEnabled( false );
- continueBtn.addActionListener( this );
-
- JPanel continueHolder = new JPanel();
- continueHolder.setLayout( new BoxLayout( continueHolder, BoxLayout.X_AXIS ) );
- continueHolder.setBorder( BorderFactory.createEmptyBorder( 0, 0, 10, 0 ) );
- continueHolder.add( Box.createHorizontalGlue() );
- continueHolder.add( continueBtn );
- continueHolder.add( Box.createHorizontalGlue() );
- getContentPane().add( continueHolder, BorderLayout.SOUTH );
-
- this.pack();
- this.setMinimumSize( this.getPreferredSize() );
- this.setLocationRelativeTo( owner );
- }
-
-
- @Override
- public void actionPerformed( ActionEvent e ) {
- Object source = e.getSource();
-
- if ( source == continueBtn ) {
- this.setVisible( false );
- this.dispose();
-
- if ( done && succeeded && successTask != null ) {
- successTask.run();
- }
- }
- }
-
-
- /**
- * Updates the text area's content. (Thread-safe)
- *
- * @param message a string, or null
- */
- public void setStatusTextLater( final String message ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- setStatusText( message != null ? message : "..." );
- }
- });
- }
-
- protected void setStatusText( String message ) {
- statusArea.setText( message != null ? message : "..." );
- statusArea.setCaretPosition( 0 );
- }
-
-
- /**
- * Updates the progress bar. (Thread-safe)
- *
- * If the arg is -1, the bar will become indeterminate.
- *
- * @param value the new value
- */
- public void setProgressLater( final int value ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if ( value >= 0 ) {
- if ( progressBar.isIndeterminate() )
- progressBar.setIndeterminate( false );
-
- progressBar.setValue( value );
- }
- else {
- if ( !progressBar.isIndeterminate() )
- progressBar.setIndeterminate( true );
- progressBar.setValue( 0 );
- }
- }
- });
- }
-
- /**
- * Updates the progress bar. (Thread-safe)
- *
- * If either arg is -1, the bar will become indeterminate.
- *
- * @param value the new value
- * @param max the new maximum
- */
- public void setProgressLater( final int value, final int max ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- if ( value >= 0 && max >= 0 ) {
- if ( progressBar.isIndeterminate() )
- progressBar.setIndeterminate( false );
-
- if ( progressBar.getMaximum() != max ) {
- progressBar.setValue( 0 );
- progressBar.setMaximum( max );
- }
- progressBar.setValue( value );
- }
- else {
- if ( !progressBar.isIndeterminate() )
- progressBar.setIndeterminate( true );
- progressBar.setValue( 0 );
- }
- }
- });
- }
-
-
- /**
- * Triggers a response to the immediate task ending. (Thread-safe)
- *
- * If anything went wrong, e may be non-null.
- */
- public void setTaskOutcomeLater( final boolean success, final Exception e ) {
- SwingUtilities.invokeLater(new Runnable() {
- @Override
- public void run() {
- setTaskOutcome( success, e );
- }
- });
- }
-
- protected void setTaskOutcome( final boolean outcome, final Exception e ) {
- done = true;
- succeeded = outcome;
-
- if ( !ProgressDialog.this.isShowing() ) {
- // The window's not visible, no continueBtn to click.
- ProgressDialog.this.dispose();
-
- if ( succeeded && successTask != null ) {
- successTask.run();
- }
- }
- if ( continueOnSuccess && succeeded && successTask != null ) {
- ProgressDialog.this.setVisible( false );
- ProgressDialog.this.dispose();
- successTask.run();
- }
- else {
- continueBtn.setEnabled( true );
- continueBtn.requestFocusInWindow();
- }
- }
-
-
- /**
- * Sets a runnable to trigger after the immediate task ends successfully.
- */
- public void setSuccessTask( Runnable r ) {
- successTask = r;
- }
-
- /**
- * Shows or hides this component depending on the value of parameter b.
- *
- * If the immediate task has already completed,
- * this method will do nothing.
- */
- public void setVisible( boolean b ) {
- if ( !done ) super.setVisible( b );
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/RegexDocument.java b/src/main/java/net/vhati/modmanager/ui/RegexDocument.java
deleted file mode 100644
index e1a03e9..0000000
--- a/src/main/java/net/vhati/modmanager/ui/RegexDocument.java
+++ /dev/null
@@ -1,69 +0,0 @@
-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;
-
-
-/**
- * A Document thats restricts characters based on a regex.
- *
- * @see javax.swing.JTextField.setDocument(javax.swing.text.Ducument)
- */
-public class RegexDocument extends PlainDocument {
-
- private Pattern pattern = null;
-
-
- public RegexDocument( Pattern p ) {
- pattern = p;
- }
-
- public RegexDocument( String regex ) {
- try {
- if ( regex != null && regex.length() > 0 ) {
- pattern = Pattern.compile( regex );
- }
- }
- catch ( PatternSyntaxException e ) {}
- }
-
- public RegexDocument() {
- }
-
-
- @Override
- public void insertString( int offs, String str, AttributeSet a ) throws BadLocationException {
- if ( str == null ) return;
-
- boolean proceed = true;
-
- if ( pattern != null ) {
- String tmp = super.getText( 0, offs ) + str + (super.getLength()>offs ? super.getText( offs, super.getLength()-offs ) : "");
- Matcher m = pattern.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 ( pattern != null ) {
- try {
- String tmp = super.getText( 0, offs ) + (super.getLength()>(offs+len) ? super.getText( offs+len, super.getLength()-(offs+len) ) : "");
- Matcher m = pattern.matcher( tmp );
- proceed = m.matches();
- }
- catch ( BadLocationException e ) {}
- }
-
- 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
deleted file mode 100644
index 85bb94d..0000000
--- a/src/main/java/net/vhati/modmanager/ui/SlipstreamConfigDialog.java
+++ /dev/null
@@ -1,211 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.Frame;
-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.JDialog;
-import javax.swing.JFileChooser;
-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 JDialog implements ActionListener {
-
- protected static final String ALLOW_ZIP = SlipstreamConfig.ALLOW_ZIP;
- protected static final String RUN_STEAM_FTL = SlipstreamConfig.RUN_STEAM_FTL;
- protected static final String NEVER_RUN_FTL = SlipstreamConfig.NEVER_RUN_FTL;
- protected static final String USE_DEFAULT_UI = SlipstreamConfig.USE_DEFAULT_UI;
- protected static final String REMEMBER_GEOMETRY = SlipstreamConfig.REMEMBER_GEOMETRY;
- protected static final String UPDATE_CATALOG = SlipstreamConfig.UPDATE_CATALOG;
- protected static final String UPDATE_APP = SlipstreamConfig.UPDATE_APP;
- protected static final String FTL_DATS_PATH = SlipstreamConfig.FTL_DATS_PATH;
- protected static final String STEAM_EXE_PATH = SlipstreamConfig.STEAM_EXE_PATH;
-
- protected SlipstreamConfig appConfig;
-
- protected FieldEditorPanel editorPanel;
- protected JButton applyBtn;
-
-
- public SlipstreamConfigDialog( Frame owner, SlipstreamConfig appConfig ) {
- super( owner, "Preferences..." );
- this.setDefaultCloseOperation( JDialog.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( RUN_STEAM_FTL, ContentType.BOOLEAN );
- editorPanel.addTextRow( "Use Steam to run FTL, if possible." );
- 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.addRow( STEAM_EXE_PATH, ContentType.CHOOSER );
- editorPanel.addTextRow( "Path to Steam's executable." );
- editorPanel.addSeparatorRow();
- editorPanel.addBlankRow();
- editorPanel.addTextRow( "Note: Some changes may have no immediate effect." );
- editorPanel.addBlankRow();
- editorPanel.addFillRow();
-
- editorPanel.getBoolean( ALLOW_ZIP ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.ALLOW_ZIP, "false" ) ) );
- editorPanel.getBoolean( RUN_STEAM_FTL ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) );
- editorPanel.getBoolean( NEVER_RUN_FTL ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.NEVER_RUN_FTL, "false" ) ) );
- editorPanel.getBoolean( USE_DEFAULT_UI ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.USE_DEFAULT_UI, "false" ) ) );
- editorPanel.getBoolean( REMEMBER_GEOMETRY ).setSelected( "true".equals( appConfig.getProperty( SlipstreamConfig.REMEMBER_GEOMETRY, "true" ) ) );
- editorPanel.getInt( UPDATE_CATALOG ).setText( Integer.toString( appConfig.getPropertyAsInt( SlipstreamConfig.UPDATE_CATALOG, 0 ) ) );
- editorPanel.getInt( UPDATE_APP ).setText( Integer.toString( appConfig.getPropertyAsInt( SlipstreamConfig.UPDATE_APP, 0 ) ) );
-
- JTextField ftlDatsPathField = editorPanel.getChooser( FTL_DATS_PATH ).getTextField();
- ftlDatsPathField.setText( appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH, "" ) );
- ftlDatsPathField.setPreferredSize( new Dimension( 150, ftlDatsPathField.getPreferredSize().height ) );
- editorPanel.getChooser( FTL_DATS_PATH ).getButton().addActionListener( this );
-
- JTextField steamExePathField = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField();
- steamExePathField.setText( appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH, "" ) );
- steamExePathField.setPreferredSize( new Dimension( 150, steamExePathField.getPreferredSize().height ) );
- editorPanel.getChooser( STEAM_EXE_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( SlipstreamConfig.ALLOW_ZIP, editorPanel.getBoolean( ALLOW_ZIP ).isSelected() ? "true" : "false" );
- appConfig.setProperty( SlipstreamConfig.RUN_STEAM_FTL, editorPanel.getBoolean( RUN_STEAM_FTL ).isSelected() ? "true" : "false" );
- appConfig.setProperty( SlipstreamConfig.NEVER_RUN_FTL, editorPanel.getBoolean( NEVER_RUN_FTL ).isSelected() ? "true" : "false" );
- appConfig.setProperty( SlipstreamConfig.USE_DEFAULT_UI, editorPanel.getBoolean( USE_DEFAULT_UI ).isSelected() ? "true" : "false" );
- appConfig.setProperty( SlipstreamConfig.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( SlipstreamConfig.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( SlipstreamConfig.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( SlipstreamConfig.FTL_DATS_PATH, tmp );
- }
-
- tmp = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().getText();
- if ( tmp.length() > 0 && new File( tmp ).exists() ) {
- appConfig.setProperty( SlipstreamConfig.STEAM_EXE_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() );
- }
- }
- else if ( source == editorPanel.getChooser( STEAM_EXE_PATH ).getButton() ) {
- String currentPath = editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().getText();
-
- JFileChooser steamExeChooser = new JFileChooser();
- steamExeChooser.setDialogTitle( "Find Steam.exe or steam or Steam.app" );
- steamExeChooser.setFileHidingEnabled( false );
- steamExeChooser.setMultiSelectionEnabled( false );
- if ( currentPath.length() > 0 ) {
- steamExeChooser.setCurrentDirectory( new File( currentPath ) );
- }
-
- if ( steamExeChooser.showOpenDialog( null ) == JFileChooser.APPROVE_OPTION ) {
- File steamExeFile = steamExeChooser.getSelectedFile();
- if ( steamExeFile.exists() ) {
- editorPanel.getChooser( STEAM_EXE_PATH ).getTextField().setText( steamExeFile.getAbsolutePath() );
- }
- }
- }
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/Statusbar.java b/src/main/java/net/vhati/modmanager/ui/Statusbar.java
deleted file mode 100644
index e148bcd..0000000
--- a/src/main/java/net/vhati/modmanager/ui/Statusbar.java
+++ /dev/null
@@ -1,6 +0,0 @@
-package net.vhati.modmanager.ui;
-
-
-public interface Statusbar {
- public void setStatusText( String text );
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/StatusbarMouseListener.java b/src/main/java/net/vhati/modmanager/ui/StatusbarMouseListener.java
deleted file mode 100644
index b8685e2..0000000
--- a/src/main/java/net/vhati/modmanager/ui/StatusbarMouseListener.java
+++ /dev/null
@@ -1,38 +0,0 @@
-package net.vhati.modmanager.ui;
-
-import java.awt.event.MouseAdapter;
-import java.awt.event.MouseEvent;
-import java.awt.event.MouseListener;
-
-import net.vhati.modmanager.ui.Statusbar;
-
-
-/**
- * A MouseListener to show rollover help text in a status bar.
- *
- * Construct this with the help text, and a class
- * implementing the Statusbar interface.
- *
- * Then add this mouseListener to a component.
- */
-public class StatusbarMouseListener extends MouseAdapter {
-
- protected Statusbar bar = null;
- protected String text = null;
-
-
- public StatusbarMouseListener( Statusbar bar, String text ) {
- this.bar = bar;
- this.text = text;
- }
-
- @Override
- public void mouseEntered( MouseEvent e ) {
- bar.setStatusText( text );
- }
-
- @Override
- public void mouseExited( MouseEvent e ) {
- bar.setStatusText( "" );
- }
-}
diff --git a/src/main/java/net/vhati/modmanager/ui/table/ChecklistTableModel.java b/src/main/java/net/vhati/modmanager/ui/table/ChecklistTableModel.java
deleted file mode 100644
index 155d21e..0000000
--- a/src/main/java/net/vhati/modmanager/ui/table/ChecklistTableModel.java
+++ /dev/null
@@ -1,117 +0,0 @@
-package net.vhati.modmanager.ui.table;
-
-import java.util.ArrayList;
-import java.util.List;
-import javax.swing.table.AbstractTableModel;
-
-import net.vhati.modmanager.core.ModInfo;
-import net.vhati.modmanager.ui.table.Reorderable;
-
-
-public class ChecklistTableModel extends AbstractTableModel implements Reorderable {
-
- private static final int COLUMN_CHECK = 0;
- private static final int COLUMN_PAYLOAD = 1;
-
- private static final int DATA_CHECK = 0;
- private static final int DATA_PAYLOAD = 1;
-
- private String[] columnNames = new String[] {"?", "Name"};
- private Class[] columnTypes = new Class[] {Boolean.class, String.class};
-
- private List> rowsList = new ArrayList>();
-
-
- public void addItem( T o ) {
- insertItem( rowsList.size(), false, o );
- }
-
- public void insertItem( int row, boolean selected, T o ) {
- int newRowIndex = rowsList.size();
-
- List