diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java index c431262..3ea6dd5 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,9 +14,6 @@ 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 { @@ -75,271 +66,10 @@ public class FTLModManager { } }); - if ( args.length > 0 ) { - SlipstreamCLI.main( args ); + if (args.length == 0 ) { + System.out.println("Project called with no arguments."); 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..358df41 100644 --- a/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java +++ b/src/main/java/net/vhati/modmanager/cli/SlipstreamCLI.java @@ -7,12 +7,8 @@ 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; @@ -41,25 +37,21 @@ 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 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 ) { - 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 ); @@ -149,7 +141,13 @@ public class SlipstreamCLI { 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(); + if ( !extractDir.exists() ) { + boolean success = extractDir.mkdirs(); + if (success) { + log.error( "Error extracting dats"); + System.exit( 1 ); + } + }; dstPack = new FolderPack( extractDir ); @@ -173,14 +171,14 @@ public class SlipstreamCLI { } finally { try {if ( is != null ) is.close();} - catch ( IOException ex ) {} + catch ( IOException ignored) {} try {if ( dstPack != null ) dstPack.close();} - catch ( IOException ex ) {} + catch ( IOException ignored) {} for ( AbstractPack pack : srcPacks ) { try {pack.close();} - catch ( IOException ex ) {} + catch ( IOException ignored) {} } } } @@ -197,13 +195,13 @@ public class SlipstreamCLI { File modFile = new File( modsDir, modFileName ); if ( modFile.isDirectory() ) { - log.info( String.format( "Zipping dir: %s/", modFile.getName() ) ); + log.info("Zipping dir: {}/", modFile.getName()); try { modFile = createTempMod( modFile ); deleteHook.addDoomedFile( modFile ); } catch ( IOException e ) { - log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e ); + log.error("Error zipping dir: {}/", modFile.getName(), e); return false; } } @@ -216,18 +214,17 @@ public class SlipstreamCLI { SilentPatchObserver patchObserver = new SilentPatchObserver(); ModPatchThread patchThread = new ModPatchThread( modFiles, datsDir, backupDir, globalPanic, patchObserver ); - patchThread.setDefaultUncaughtExceptionHandler( exceptionHandler ); + Thread.setDefaultUncaughtExceptionHandler( exceptionHandler ); deleteHook.addWatchedThread( patchThread ); patchThread.start(); while ( patchThread.isAlive() ) { try {patchThread.join();} - catch ( InterruptedException e ) {} + catch ( InterruptedException ignored) {} } - if ( !patchObserver.hasSucceeded() ) return false; - return true; - } + return patchObserver.hasSucceeded(); + } private static boolean validate(String[] modFileNames) { DelayedDeleteHook deleteHook = new DelayedDeleteHook(); @@ -243,13 +240,13 @@ public class SlipstreamCLI { File modFile = new File( modsDir, modFileName ); if ( modFile.isDirectory() ) { - log.info( String.format( "Zipping dir: %s/", modFile.getName() ) ); + log.info("Zipping dir: {}/", modFile.getName()); try { modFile = createTempMod( modFile ); deleteHook.addDoomedFile( modFile ); } catch ( IOException e ) { - log.error( String.format( "Error zipping dir: %s/", modFile.getName() ), e ); + log.error("Error zipping dir: {}/", modFile.getName(), e); List tmpMessages = new ArrayList(); tmpMessages.add( new ReportMessage( ReportMessage.SECTION, modFileName ) ); @@ -268,9 +265,9 @@ public class SlipstreamCLI { formatter.format( validateReport.messages, resultBuf, 0 ); resultBuf.append( "\n" ); - if ( validateReport.outcome == false ) anyInvalid = true; + if (!validateReport.outcome) anyInvalid = true; } - if ( resultBuf.length() == 0 ) { + if (resultBuf.isEmpty()) { resultBuf.append( "No mods were checked." ); } @@ -284,9 +281,10 @@ public class SlipstreamCLI { 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 ) { + List dirList = new ArrayList<>(); + List fileList = new ArrayList<>(); + assert modFiles != null; + for ( File f : modFiles ) { if ( f.isDirectory() ) dirList.add( f.getName() +"/" ); else @@ -308,7 +306,7 @@ public class SlipstreamCLI { if ( "true".equals( appConfig.getProperty( SlipstreamConfig.RUN_STEAM_FTL, "false" ) ) ) { String steamPath = appConfig.getProperty( SlipstreamConfig.STEAM_EXE_PATH ); - if ( steamPath.length() > 0 ) { + if (!steamPath.isEmpty()) { exeFile = new File( steamPath ); if ( exeFile.exists() ) { @@ -361,10 +359,10 @@ public class SlipstreamCLI { File datsDir = null; String datsPath = appConfig.getProperty( SlipstreamConfig.FTL_DATS_PATH, "" ); - if ( datsPath.length() > 0 ) { + if (!datsPath.isEmpty()) { log.info( "Using FTL dats path from config: "+ datsPath ); datsDir = new File( datsPath ); - if ( FTLUtilities.isDatsDirValid( datsDir ) == false ) { + if (!FTLUtilities.isDatsDirValid(datsDir)) { log.error( "The config's "+ SlipstreamConfig.FTL_DATS_PATH +" does not exist, or it is invalid" ); datsDir = null; } @@ -383,24 +381,18 @@ public class SlipstreamCLI { /** * 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" ); - 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; } @@ -408,29 +400,24 @@ public class SlipstreamCLI { private static void addDirToArchive( ZipOutputStream zos, File dir, String pathPrefix ) throws IOException { if ( pathPrefix == null ) pathPrefix = ""; - for ( File f : dir.listFiles() ) { + 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) { + } } } @@ -527,8 +514,8 @@ public class SlipstreamCLI { private static class ModAndDirFileFilter implements FileFilter { - private boolean allowZip; - private boolean allowDirs; + private final boolean allowZip; + private final boolean allowDirs; public ModAndDirFileFilter( boolean allowZip, boolean allowDirs ) { this.allowZip = allowZip; @@ -542,7 +529,7 @@ public class SlipstreamCLI { if ( f.getName().endsWith(".ftl") ) return true; if ( allowZip ) { - if ( f.getName().endsWith(".zip") ) return true; + return f.getName().endsWith(".zip"); } 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 rowData = new ArrayList(); - rowData.add( new Boolean(selected) ); - rowData.add( o ); - rowsList.add( row, rowData ); - - fireTableRowsInserted( row, row ); - } - - public void removeItem( int row ) { - rowsList.remove( row ); - fireTableRowsDeleted( row, row ); - } - - public void removeAllItems() { - rowsList.clear(); - fireTableDataChanged(); - } - - @SuppressWarnings("unchecked") - public T getItem( int row ) { - return (T)rowsList.get(row).get( DATA_PAYLOAD ); - } - - @Override - public void reorder( int fromRow, int toRow ) { - if ( toRow > fromRow ) toRow--; - List rowData = rowsList.get( fromRow ); - rowsList.remove( fromRow ); - fireTableRowsDeleted( fromRow, fromRow ); - rowsList.add( toRow, rowData ); - fireTableRowsInserted( toRow, toRow ); - } - - public void setSelected( int row, boolean b ) { - rowsList.get(row).set( DATA_CHECK, new Boolean( b ) ); - fireTableRowsUpdated( row, row ); - } - - @SuppressWarnings("unchecked") - public boolean isSelected( int row ) { - return ((Boolean)rowsList.get( row ).get( DATA_CHECK )).booleanValue(); - } - - @Override - public int getColumnCount() { - return columnNames.length; - } - - @Override - public int getRowCount() { - return rowsList.size(); - } - - @Override - public Object getValueAt( int row, int column ) { - if ( column == COLUMN_CHECK ) { - return rowsList.get( row ).get( DATA_CHECK ); - } - else if ( column == COLUMN_PAYLOAD ) { - Object o = rowsList.get( row ).get( DATA_PAYLOAD ); - return o.toString(); - } - throw new ArrayIndexOutOfBoundsException(); - } - - @Override - @SuppressWarnings("unchecked") - public void setValueAt( Object o, int row, int column ) { - if ( column == COLUMN_CHECK ) { - Boolean bool = (Boolean)o; - rowsList.get(row).set( DATA_CHECK, bool ); - fireTableRowsUpdated( row, row ); - } - } - - @Override - public boolean isCellEditable( int row, int column ) { - if ( column == COLUMN_CHECK ) return true; - return false; - } - - @Override - public Class getColumnClass( int column ) { - return columnTypes[column]; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/table/ChecklistTablePanel.java b/src/main/java/net/vhati/modmanager/ui/table/ChecklistTablePanel.java deleted file mode 100644 index 3130f28..0000000 --- a/src/main/java/net/vhati/modmanager/ui/table/ChecklistTablePanel.java +++ /dev/null @@ -1,136 +0,0 @@ -package net.vhati.modmanager.ui.table; - -import java.awt.BorderLayout; -import java.awt.Dimension; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.util.ArrayList; -import java.util.List; -import javax.swing.DropMode; -import javax.swing.ListSelectionModel; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTable; - -import net.vhati.modmanager.core.ModFileInfo; -import net.vhati.modmanager.ui.table.ChecklistTableModel; -import net.vhati.modmanager.ui.table.TableRowTransferHandler; - - -public class ChecklistTablePanel extends JPanel { - - protected ChecklistTableModeltableModel; - protected JTable table; - - - public ChecklistTablePanel() { - super( new BorderLayout() ); - - tableModel = new ChecklistTableModel(); - - table = new JTable( tableModel ); - table.setFillsViewportHeight( true ); - table.setSelectionMode( ListSelectionModel.SINGLE_SELECTION ); - table.setTableHeader( null ); - table.getColumnModel().getColumn( 0 ).setMinWidth( 30 ); - table.getColumnModel().getColumn( 0 ).setMaxWidth( 30 ); - table.getColumnModel().getColumn( 0 ).setPreferredWidth( 30 ); - - JScrollPane scrollPane = new JScrollPane( null, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER ); - scrollPane.setViewportView( table ); - //scrollPane.setColumnHeaderView( null ); // Counterpart to setTableHeader(). - scrollPane.setPreferredSize( new Dimension( Integer.MIN_VALUE, Integer.MIN_VALUE ) ); - this.add( scrollPane, BorderLayout.CENTER ); - - - // Double-click toggles checkboxes. - table.addMouseListener(new MouseAdapter() { - int prevRow = -1; - int streak = 0; - - @Override - public void mouseClicked( MouseEvent e ) { - if ( e.getSource() != table ) return; - int thisRow = table.rowAtPoint( e.getPoint() ); - - // Reset on first click and when no longer on that row. - if ( e.getClickCount() == 1 ) prevRow = -1; - - if ( thisRow != prevRow || thisRow == -1 ) { - streak = 1; - prevRow = thisRow; - return; - } - else { - streak++; - } - if ( streak % 2 != 0 ) return; // Respond only to click pairs. - - // Don't further toggle a multi-clicked checkbox. - int viewCol = table.columnAtPoint( e.getPoint() ); - int modelCol = table.getColumnModel().getColumn( viewCol ).getModelIndex(); - if ( modelCol == 0 ) return; - - int selRow = table.getSelectedRow(); - if ( selRow != -1 ) { - boolean selected = tableModel.isSelected( selRow ); - tableModel.setSelected( selRow, !selected ); - } - } - }); - - table.setTransferHandler( new TableRowTransferHandler( table ) ); - table.setDropMode( DropMode.INSERT ); // Drop between rows, not on them. - table.setDragEnabled( true ); - } - - - public void clear() { - tableModel.removeAllItems(); - } - - - public List getAllItems() { - List results = new ArrayList(); - - for ( int i=0; i < tableModel.getRowCount(); i++ ) { - results.add( tableModel.getItem( i ) ); - } - - return results; - } - - public List getSelectedItems() { - List results = new ArrayList(); - - for ( int i=0; i < tableModel.getRowCount(); i++ ) { - if ( tableModel.isSelected( i ) ) { - results.add( tableModel.getItem( i ) ); - } - } - - return results; - } - - - public void toggleAllItemSelection() { - int selectedCount = 0; - for ( int i = tableModel.getRowCount()-1; i >= 0; i-- ) { - if ( tableModel.isSelected( i ) ) selectedCount++; - } - boolean b = ( selectedCount != tableModel.getRowCount() ); - - for ( int i = tableModel.getRowCount()-1; i >= 0; i-- ) { - tableModel.setSelected( i, b ); - } - } - - - public ChecklistTableModel getTableModel() { - return tableModel; - } - - public JTable getTable() { - return table; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/table/ListState.java b/src/main/java/net/vhati/modmanager/ui/table/ListState.java deleted file mode 100644 index 916fd55..0000000 --- a/src/main/java/net/vhati/modmanager/ui/table/ListState.java +++ /dev/null @@ -1,39 +0,0 @@ -package net.vhati.modmanager.ui.table; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - - -/** - * An implementation-agnostic model to pass between the GUI thread and the - * (de)serializer. - */ -public class ListState { - - protected List items = new ArrayList(); - - - public ListState() { - } - - - public void addItem( T item ) { - items.add( item ); - } - - /** - * Returns a new list containing items in this state. - */ - public List getItems() { - return new ArrayList( items ); - } - - public void removeItem( T item ) { - items.remove( item ); - } - - public boolean containsItem( T item ) { - return items.contains( item ); - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/table/Reorderable.java b/src/main/java/net/vhati/modmanager/ui/table/Reorderable.java deleted file mode 100644 index 64ce492..0000000 --- a/src/main/java/net/vhati/modmanager/ui/table/Reorderable.java +++ /dev/null @@ -1,9 +0,0 @@ -package net.vhati.modmanager.ui.table; - - -public interface Reorderable { - /** - * Moves an element at fromIndex to toIndex. - */ - public void reorder( int fromIndex, int toIndex ); -} diff --git a/src/main/java/net/vhati/modmanager/ui/table/TableRowTransferHandler.java b/src/main/java/net/vhati/modmanager/ui/table/TableRowTransferHandler.java deleted file mode 100644 index cd7208f..0000000 --- a/src/main/java/net/vhati/modmanager/ui/table/TableRowTransferHandler.java +++ /dev/null @@ -1,131 +0,0 @@ -package net.vhati.modmanager.ui.table; - -import java.awt.Cursor; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DragSource; -import javax.swing.JComponent; -import javax.swing.JTable; -import javax.swing.TransferHandler; - -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import net.vhati.modmanager.ui.table.Reorderable; - - -/** - * Allows drag and drop reordering of JTable rows. - * - * Its TableModel must implement the Reorderable interface. - */ -public class TableRowTransferHandler extends TransferHandler { - - private static final Logger log = LoggerFactory.getLogger( TableRowTransferHandler.class ); - - private DataFlavor localIntegerFlavor = null; - - private JTable table = null; - - - public TableRowTransferHandler( JTable table ) { - super(); - if ( table.getModel() instanceof Reorderable == false ) { - throw new IllegalArgumentException( "The tableModel doesn't implement Reorderable." ); - } - this.table = table; - - try { - localIntegerFlavor = new DataFlavor( String.format( "%s;class=\"%s\"", DataFlavor.javaJVMLocalObjectMimeType, Integer.class.getName() ) ); - } - catch ( ClassNotFoundException e ) { - log.error( "Failed to construct a table row transfer handler", e ); - } - } - - @Override - protected Transferable createTransferable( JComponent c ) { - assert ( c == table ); - int row = table.getSelectedRow(); - return new IntegerTransferrable( new Integer( row ) ); - } - - @Override - public boolean canImport( TransferHandler.TransferSupport ts ) { - boolean b = ( ts.getComponent() == table && ts.isDrop() && ts.isDataFlavorSupported( localIntegerFlavor ) ); - table.setCursor( b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop ); - return b; - } - - @Override - public int getSourceActions( JComponent comp ) { - return TransferHandler.MOVE; - } - - @Override - @SuppressWarnings("Unchecked") - public boolean importData( TransferHandler.TransferSupport ts ) { - if ( !canImport( ts ) ) return false; - - JTable target = (JTable)ts.getComponent(); - JTable.DropLocation dl = (JTable.DropLocation)ts.getDropLocation(); - int dropRow = dl.getRow(); - int rowCount = table.getModel().getRowCount(); - if ( dropRow < 0 || dropRow > rowCount ) dropRow = rowCount; - - target.setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); - try { - Integer draggedRow = (Integer)ts.getTransferable().getTransferData( localIntegerFlavor ); - if ( draggedRow != -1 && draggedRow != dropRow ) { - ((Reorderable)table.getModel()).reorder( draggedRow, dropRow ); - if ( dropRow > draggedRow ) dropRow--; - target.getSelectionModel().addSelectionInterval( dropRow, dropRow ); - return true; - } - } - catch ( Exception e ) { - log.error( "Dragging failed", e ); - } - return false; - } - - @Override - protected void exportDone( JComponent source, Transferable data, int action ) { - if ( action == TransferHandler.MOVE || action == TransferHandler.NONE ) { - table.setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); - } - } - - - - /** - * Drag and drop Integer data, constructed with a raw object - * from a drag source, to be transformed into a flavor - * suitable for the drop target. - */ - private class IntegerTransferrable implements Transferable { - private Integer data; - - public IntegerTransferrable( Integer data ) { - this.data = data; - } - - @Override - public Object getTransferData( DataFlavor flavor ) { - if ( flavor.equals( localIntegerFlavor ) ) { - return data; - } - return null; - } - - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] {localIntegerFlavor}; - } - - @Override - public boolean isDataFlavorSupported( DataFlavor flavor ) { - return flavor.equals( localIntegerFlavor ); - } - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeCellRenderer.java b/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeCellRenderer.java deleted file mode 100644 index 9e11063..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeCellRenderer.java +++ /dev/null @@ -1,91 +0,0 @@ -package net.vhati.modmanager.ui.tree; - -import java.awt.BorderLayout; -import java.awt.Component; -import javax.swing.JPanel; -import javax.swing.JTree; -import javax.swing.tree.TreeCellRenderer; -import javax.swing.tree.TreePath; - -import net.vhati.modmanager.ui.tree.ChecklistTreePathFilter; -import net.vhati.modmanager.ui.tree.ChecklistTreeSelectionModel; -import net.vhati.modmanager.ui.tree.TristateCheckBox; -import net.vhati.modmanager.ui.tree.TristateButtonModel.TristateState; - - -/** - * A cell renderer that augments an existing renderer with a checkbox. - */ -public class ChecklistTreeCellRenderer extends JPanel implements TreeCellRenderer { - - protected ChecklistTreeSelectionModel selectionModel; - protected ChecklistTreePathFilter checklistFilter; - protected TreeCellRenderer delegate; - protected TristateCheckBox checkbox = new TristateCheckBox(); - protected int checkMaxX = 0; - - - /** - * Constructor. - * - * @param delegate a traditional TreeCellRenderer - * @param selectionModel a model to query for checkbox states - * @param checklistFilter a TreePath filter, or null to always show a checkbox - */ - public ChecklistTreeCellRenderer( TreeCellRenderer delegate, ChecklistTreeSelectionModel selectionModel, ChecklistTreePathFilter checklistFilter ) { - super(); - this.delegate = delegate; - this.selectionModel = selectionModel; - this.checklistFilter = checklistFilter; - - this.setLayout( new BorderLayout() ); - this.setOpaque( false ); - checkbox.setOpaque( false ); - - checkMaxX = checkbox.getPreferredSize().width; - } - - - @Override - public Component getTreeCellRendererComponent( JTree tree, Object value, boolean sel, boolean expanded, boolean leaf, int row, boolean hasFocus ) { - this.removeAll(); - checkbox.setState( TristateState.DESELECTED ); - - Component delegateComp = delegate.getTreeCellRendererComponent( tree, value, sel, expanded, leaf, row, hasFocus ); - - TreePath path = tree.getPathForRow( row ); - if ( path != null ) { - if ( selectionModel.isPathSelected( path, selectionModel.isDigged() ) ) { - checkbox.setState( TristateState.SELECTED ); - } else { - checkbox.setState( ( selectionModel.isDigged() && selectionModel.isPartiallySelected( path ) ) ? TristateState.INDETERMINATE : TristateState.DESELECTED ); - } - } - checkbox.setVisible( path == null || checklistFilter == null || checklistFilter.isSelectable( path ) ); - checkbox.setEnabled( tree.isEnabled() ); - - this.add( checkbox, BorderLayout.WEST ); - this.add( delegateComp, BorderLayout.CENTER ); - return this; - } - - - public void setDelegate( TreeCellRenderer delegate ) { - this.delegate = delegate; - } - - public TreeCellRenderer getDelegate() { - return delegate; - } - - - /** - * Returns the checkbox's right edge (in the renderer component's coordinate space). - * - * Values less than that can be interpreted as within the checkbox's bounds. - * X=0 is the renderer component's left edge. - */ - public int getCheckboxMaxX() { - return checkMaxX; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeManager.java b/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeManager.java deleted file mode 100644 index 84f83f6..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeManager.java +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Based on CheckTreeManager (rev 120, 2007-07-20) - * By Santhosh Kumar T - * https://java.net/projects/myswing - * - * https://java.net/projects/myswing/sources/svn/content/trunk/src/skt/swing/tree/check/CheckTreeManager.java?rev=120 - */ - -/** - * MySwing: Advanced Swing Utilites - * Copyright (C) 2005 Santhosh Kumar T - *

- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -package net.vhati.modmanager.ui.tree; - -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import javax.swing.JTree; -import javax.swing.event.TreeSelectionEvent; -import javax.swing.event.TreeSelectionListener; -import javax.swing.tree.TreePath; - -import net.vhati.modmanager.ui.tree.ChecklistTreePathFilter; -import net.vhati.modmanager.ui.tree.ChecklistTreeSelectionModel; - - -public class ChecklistTreeManager extends MouseAdapter implements TreeSelectionListener { - - private ChecklistTreeSelectionModel selectionModel; - private ChecklistTreePathFilter checklistFilter; - protected JTree tree = new JTree(); - protected int checkMaxX = 0; - - - /** - * Constructor. - * - * Modifies a given tree to add checkboxes. - * - The tree's existing cell renderer will be wrapped with a ChecklistTreeCellRenderer. - * - A MouseListener will be added to the tree to detect clicks, which will toggle checkboxes. - * - * A secondary ChecklistTreeSelectionModel will track checkboxes' states (independent of row - * highlighting). - * - * @param tree a tree to modify - * @param dig true show that a node is partially selected by scanning its descendents, false otherwise - * @checklistFilter a filter to decide which TreePaths need checkboxes, or null - */ - public ChecklistTreeManager( JTree tree, boolean dig, ChecklistTreePathFilter checklistFilter ) { - this.tree = tree; - this.checklistFilter = checklistFilter; - - // Note: If largemodel is not set then treenodes are getting truncated. - // Need to debug further to find the problem. - if ( checklistFilter != null ) tree.setLargeModel( true ); - - selectionModel = new ChecklistTreeSelectionModel( tree.getModel(), dig ); - - ChecklistTreeCellRenderer checklistRenderer = new ChecklistTreeCellRenderer( tree.getCellRenderer(), selectionModel, checklistFilter ); - setCheckboxMaxX( checklistRenderer.getCheckboxMaxX() ); - tree.setCellRenderer( checklistRenderer ); - - selectionModel.addTreeSelectionListener( this ); - tree.addMouseListener( this ); - } - - - /** - * Sets the checkbox's right edge (in the TreeCellRenderer component's coordinate space). - * - * Values less than that will be interpreted as within the checkbox's bounds. - * X=0 is the renderer component's left edge. - */ - public void setCheckboxMaxX( int x ) { - checkMaxX = x; - } - - - public ChecklistTreePathFilter getChecklistFilter() { - return checklistFilter; - } - - - public ChecklistTreeSelectionModel getSelectionModel() { - return selectionModel; - } - - - @Override - public void mouseClicked( MouseEvent e ) { - TreePath path = tree.getPathForLocation( e.getX(), e.getY() ); - if ( path == null ) return; - - if ( e.getX() > tree.getPathBounds(path).x + checkMaxX ) return; - - if ( checklistFilter != null && !checklistFilter.isSelectable(path) ) return; - - boolean selected = selectionModel.isPathSelected( path, selectionModel.isDigged() ); - selectionModel.removeTreeSelectionListener( this ); - - try { - if ( selected ) { - selectionModel.removeSelectionPath( path ); - } else { - selectionModel.addSelectionPath( path ); - } - } - finally { - selectionModel.addTreeSelectionListener( this ); - tree.treeDidChange(); - } - } - - - @Override - public void valueChanged( TreeSelectionEvent e ) { - tree.treeDidChange(); - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePanel.java b/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePanel.java deleted file mode 100644 index 103273d..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePanel.java +++ /dev/null @@ -1,196 +0,0 @@ -package net.vhati.modmanager.ui.tree; - -import java.awt.BorderLayout; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.List; -import javax.swing.DropMode; -import javax.swing.JFrame; -import javax.swing.JPanel; -import javax.swing.JScrollPane; -import javax.swing.JTree; -import javax.swing.SwingUtilities; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeCellRenderer; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; - -import net.vhati.modmanager.ui.tree.ChecklistTreeManager; -import net.vhati.modmanager.ui.tree.ChecklistTreeSelectionModel; -import net.vhati.modmanager.ui.tree.TreeTransferHandler; - - -public class ChecklistTreePanel extends JPanel { - - private DefaultTreeModel treeModel = null; - private JTree tree = null; - private ChecklistTreeManager checklistManager = null; - - - public ChecklistTreePanel() { - super( new BorderLayout() ); - - DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode( "Root", true ); - treeModel = new DefaultTreeModel( rootNode, true ); - tree = new JTree( treeModel ); - tree.setCellRenderer( new DefaultTreeCellRenderer() ); - tree.setRootVisible( false ); - tree.getSelectionModel().setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION ); - checklistManager = new ChecklistTreeManager( tree, true, null ); - - JScrollPane scrollPane = new JScrollPane( tree, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED ); - this.add( scrollPane, BorderLayout.CENTER ); - - tree.setTransferHandler( new TreeTransferHandler( tree ) ); - tree.setDropMode( DropMode.ON_OR_INSERT ); // Drop between rows, or onto groups. - tree.setDragEnabled( true ); - } - - - /** - * Returns all userObjects of nodes with ticked checkboxes (except root itself). - */ - public List getSelectedUserObjects() { - ChecklistTreeSelectionModel checklistSelectionModel = checklistManager.getSelectionModel(); - List results = new ArrayList(); - - for ( Enumeration enumer = checklistSelectionModel.getAllSelectedPaths(); enumer.hasMoreElements(); ) { - DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)enumer.nextElement(); - if ( !childNode.isRoot() && childNode.getUserObject() != null ) { - results.add( childNode.getUserObject() ); - } - } - - return results; - } - - /** - * Returns all userObjects of all nodes (except root itself). - */ - public List getAllUserObjects() { - List results = new ArrayList(); - - DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot(); - getAllUserObjects( rootNode, results ); - - return results; - } - - private void getAllUserObjects( DefaultMutableTreeNode currentNode, List results ) { - if ( !currentNode.isRoot() && currentNode.getUserObject() != null ) { - results.add( currentNode.getUserObject() ); - } - - for ( Enumeration enumer = currentNode.children(); enumer.hasMoreElements(); ) { - DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)enumer.nextElement(); - getAllUserObjects( currentNode, results ); - } - } - - - public void clear() { - DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot(); - rootNode.removeAllChildren(); - treeModel.reload(); - } - - - /** - * Adds a group to consolidate mods. - * - * TODO: Trigger a rename. - */ - public void addGroup() { - DefaultMutableTreeNode rootNode = (DefaultMutableTreeNode)treeModel.getRoot(); - DefaultMutableTreeNode groupNode = new DefaultMutableTreeNode( "New Group", true ); - rootNode.add( groupNode ); - treeModel.nodesWereInserted( rootNode, new int[]{rootNode.getIndex( groupNode )} ); - } - - /** - * Disband selected groups. - * - * TODO - */ - public void removeSelectedGroups() { - } - - /** - * Rename last selected group. - * - * TODO - */ - public void renameSelectedGroup() { - } - - - /** - * Cycles through ticking all checkboxes and clearing them. - */ - public void toggleAllNodeSelection() { - } - - /** - * Cycles through expanding all nodes and collapsing them. - */ - public void toggleAllNodeExpansion() { - boolean canExpand = false; - boolean canCollapse = false; - - for ( int i = tree.getRowCount()-1; i >= 0; i-- ) { - if ( tree.isCollapsed( i ) ) { - canExpand = true; - } - else if ( tree.isExpanded( i ) ) { - canCollapse = true; - } - } - - if ( canExpand ) { - expandAllNodes( tree.getRowCount() ); - } - else if ( canCollapse ) { - collapseAllNodes( new TreePath( treeModel.getRoot() ) ); - } - } - - /** - * Expands all nodes by repeatedly expanding until the row count stops - * growing. - */ - public void expandAllNodes( int prevRowCount ) { - for ( int i=0; i < prevRowCount; i++ ) { - tree.expandRow( i ); - } - if ( tree.getRowCount() != prevRowCount ) { - expandAllNodes( tree.getRowCount() ); - } - } - - /** - * Collapses all nodes by walking the TreeModel. - */ - public void collapseAllNodes( TreePath currentPath ) { - Object currentNode = currentPath.getLastPathComponent(); - for ( int i = treeModel.getChildCount( currentNode )-1; i >= 0; i-- ) { - Object childNode = treeModel.getChild( currentNode, i ); - TreePath childPath = currentPath.pathByAddingChild( childNode ); - collapseAllNodes( childPath ); - } - if ( currentNode != treeModel.getRoot() ) tree.collapsePath( currentPath ); - } - - - public JTree getTree() { - return tree; - } - - public DefaultTreeModel getTreeModel() { - return treeModel; - } - - public ChecklistTreeManager getChecklistManager() { - return checklistManager; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePathFilter.java b/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePathFilter.java deleted file mode 100644 index 5bb5969..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreePathFilter.java +++ /dev/null @@ -1,13 +0,0 @@ -package net.vhati.modmanager.ui.tree; - -import javax.swing.tree.TreePath; - - -/** - * Decides whether a given TreePath should have a checkbox. - */ -public interface ChecklistTreePathFilter { - - public boolean isSelectable( TreePath path ); - -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeSelectionModel.java b/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeSelectionModel.java deleted file mode 100644 index acffef9..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChecklistTreeSelectionModel.java +++ /dev/null @@ -1,265 +0,0 @@ -/* - * Based on CheckTreeSelectionModel (rev 120, 2007-07-20) - * By Santhosh Kumar T - * https://java.net/projects/myswing - * - * https://java.net/projects/myswing/sources/svn/content/trunk/src/skt/swing/tree/check/CheckTreeSelectionModel.java?rev=120 - */ - -/** - * MySwing: Advanced Swing Utilites - * Copyright (C) 2005 Santhosh Kumar T - *

- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -package net.vhati.modmanager.ui.tree; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Stack; -import javax.swing.tree.DefaultTreeSelectionModel; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; -import javax.swing.tree.TreeSelectionModel; - -import net.vhati.modmanager.ui.tree.PreorderEnumeration; - - -public class ChecklistTreeSelectionModel extends DefaultTreeSelectionModel { - - private TreeModel model; - private boolean dig = true; - - - public ChecklistTreeSelectionModel( TreeModel model, boolean dig ) { - this.model = model; - this.dig = dig; - this.setSelectionMode( TreeSelectionModel.DISCONTIGUOUS_TREE_SELECTION ); - } - - public boolean isDigged() { - return dig; - } - - - /** - * Returns true if path1 is a descendant of path2. - */ - private boolean isDescendant( TreePath path1, TreePath path2 ) { - Object obj1[] = path1.getPath(); - Object obj2[] = path2.getPath(); - for ( int i=0; i < obj2.length; i++ ) { - if ( obj1[i] != obj2[i] ) return false; - } - return true; - } - - - /** - * Returns true a selected node exists in the subtree of a given unselected path. - * Returns false if the given path is itself selected. - */ - public boolean isPartiallySelected( TreePath path ) { - if ( isPathSelected( path, true ) ) return false; - - TreePath[] selectionPaths = getSelectionPaths(); - if( selectionPaths == null ) return false; - - for ( int j=0; j < selectionPaths.length; j++ ) { - if ( isDescendant( selectionPaths[j], path ) ) { - return true; - } - } - return false; - } - - /** - * Returns true if a given path is selected. - * - * If dig is true, then the path is assumed to be selected, if - * one of its ancestors is selected. - */ - public boolean isPathSelected( TreePath path, boolean dig ) { - if ( !dig ) return super.isPathSelected( path ); - - while ( path != null && !super.isPathSelected( path ) ) { - path = path.getParentPath(); - } - return ( path != null ); - } - - - @Override - public void setSelectionPaths( TreePath[] paths ) { - if ( dig ) { - throw new UnsupportedOperationException(); - } else { - super.setSelectionPaths( paths ); - } - } - - @Override - public void addSelectionPaths( TreePath[] paths ) { - if ( !dig ) { - super.addSelectionPaths( paths ); - return; - } - - // Unselect all descendants of paths[]. - for( int i=0; i < paths.length; i++ ) { - TreePath path = paths[i]; - TreePath[] selectionPaths = getSelectionPaths(); - if ( selectionPaths == null ) break; - - List toBeRemoved = new ArrayList(); - for ( int j=0; j < selectionPaths.length; j++ ) { - if ( isDescendant( selectionPaths[j], path ) ) { - toBeRemoved.add( selectionPaths[j] ); - } - } - super.removeSelectionPaths( (TreePath[])toBeRemoved.toArray( new TreePath[0] ) ); - } - - // If all siblings are selected then unselect them and select parent recursively - // otherwize just select that path. - for ( int i=0; i < paths.length; i++ ) { - TreePath path = paths[i]; - TreePath temp = null; - while ( areSiblingsSelected(path) ) { - temp = path; - if ( path.getParentPath() == null ) break; - path = path.getParentPath(); - } - if ( temp != null ) { - if ( temp.getParentPath() != null ) { - addSelectionPath( temp.getParentPath() ); - } - else { - if ( !isSelectionEmpty() ) { - removeSelectionPaths( getSelectionPaths() ); - } - super.addSelectionPaths( new TreePath[]{temp} ); - } - } - else { - super.addSelectionPaths( new TreePath[]{path} ); - } - } - } - - @Override - public void removeSelectionPaths( TreePath[] paths ) { - if( !dig ) { - super.removeSelectionPaths( paths ); - return; - } - - for ( int i=0; i < paths.length; i++ ) { - TreePath path = paths[i]; - if ( path.getPathCount() == 1 ) { - super.removeSelectionPaths( new TreePath[]{path} ); - } else { - toggleRemoveSelection( path ); - } - } - } - - - /** - * Returns true if all siblings of given path are selected. - */ - private boolean areSiblingsSelected( TreePath path ) { - TreePath parent = path.getParentPath(); - if ( parent == null ) return true; - - Object node = path.getLastPathComponent(); - Object parentNode = parent.getLastPathComponent(); - - int childCount = model.getChildCount( parentNode ); - for ( int i=0; i < childCount; i++ ) { - Object childNode = model.getChild( parentNode, i ); - if ( childNode == node ) continue; - - if ( !isPathSelected( parent.pathByAddingChild( childNode ) ) ) { - return false; - } - } - return true; - } - - - /** - * Unselects a given path, toggling ancestors if they were entirely selected. - * - * If any ancestor node of the given path is selected, it will be unselected, - * and all its descendants - except any within the given path - will be selected. - * The ancestor will have gone from fully selected to partially selected. - * - * Otherwise, the given path will be unselected, and nothing else will change. - */ - private void toggleRemoveSelection( TreePath path ) { - Stack stack = new Stack(); - TreePath parent = path.getParentPath(); - - while ( parent != null && !isPathSelected( parent ) ) { - stack.push( parent ); - parent = parent.getParentPath(); - } - - if ( parent != null ) { - stack.push( parent ); - } - else { - super.removeSelectionPaths( new TreePath[]{path} ); - return; - } - - while ( !stack.isEmpty() ) { - TreePath temp = stack.pop(); - TreePath peekPath = ( stack.isEmpty() ? path : stack.peek() ); - Object node = temp.getLastPathComponent(); - Object peekNode = peekPath.getLastPathComponent(); - - int childCount = model.getChildCount( node ); - for ( int i=0; i < childCount; i++ ) { - Object childNode = model.getChild( node, i ); - if ( childNode != peekNode ) { - super.addSelectionPaths( new TreePath[]{temp.pathByAddingChild( childNode )} ); - } - } - } - super.removeSelectionPaths( new TreePath[]{parent} ); - } - - - public Enumeration getAllSelectedPaths() { - Enumeration result = null; - - TreePath[] treePaths = getSelectionPaths(); - if ( treePaths == null ) { - List pathsList = Collections.emptyList(); - result = Collections.enumeration( pathsList ); - } - else { - List pathsList = Arrays.asList( treePaths ); - result = Collections.enumeration( pathsList ); - if ( dig ) { - result = new PreorderEnumeration( result, model ); - } - } - - return result; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/ChildrenEnumeration.java b/src/main/java/net/vhati/modmanager/ui/tree/ChildrenEnumeration.java deleted file mode 100644 index 95f7d3b..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/ChildrenEnumeration.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Based on ChildrenEnumeration (rev 120, 2007-07-20) - * By Santhosh Kumar T - * https://java.net/projects/myswing - * - * https://java.net/projects/myswing/sources/svn/content/trunk/src/skt/swing/tree/ChildrenEnumeration.java?rev=120 - */ - -/** - * MySwing: Advanced Swing Utilites - * Copyright (C) 2005 Santhosh Kumar T - *

- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -package net.vhati.modmanager.ui.tree; - -import java.util.Enumeration; -import java.util.NoSuchElementException; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - - -public class ChildrenEnumeration implements Enumeration { - - private TreePath path; - private TreeModel model; - private int position = 0; - private int childCount; - - - public ChildrenEnumeration( TreePath path, TreeModel model ) { - this.path = path; - this.model = model; - childCount = model.getChildCount( path.getLastPathComponent() ); - } - - @Override - public boolean hasMoreElements() { - return position < childCount; - } - - @Override - public TreePath nextElement() { - if( !hasMoreElements() ) throw new NoSuchElementException(); - - return path.pathByAddingChild( model.getChild( path.getLastPathComponent(), position++ ) ); - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/PreorderEnumeration.java b/src/main/java/net/vhati/modmanager/ui/tree/PreorderEnumeration.java deleted file mode 100644 index 3e8fe9e..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/PreorderEnumeration.java +++ /dev/null @@ -1,68 +0,0 @@ -/** - * Based on PreorderEnumeration (rev 120, 2007-07-20) - * By Santhosh Kumar T - * https://java.net/projects/myswing - * - * https://java.net/projects/myswing/sources/svn/content/trunk/src/skt/swing/tree/PreorderEnumeration.java?rev=120 - */ - -/** - * MySwing: Advanced Swing Utilites - * Copyright (C) 2005 Santhosh Kumar T - *

- * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - *

- * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -package net.vhati.modmanager.ui.tree; - -import java.util.Collections; -import java.util.Enumeration; -import java.util.List; -import java.util.Stack; -import javax.swing.tree.TreeModel; -import javax.swing.tree.TreePath; - - -public class PreorderEnumeration implements Enumeration { - - private TreeModel model; - protected Stack> stack = new Stack>(); - - - public PreorderEnumeration( TreePath path, TreeModel model ) { - this( Collections.enumeration( Collections.singletonList( path ) ), model ); - } - - public PreorderEnumeration( Enumeration enumer, TreeModel model ){ - this.model = model; - stack.push( enumer ); - } - - - @Override - public boolean hasMoreElements() { - return ( !stack.empty() && stack.peek().hasMoreElements() ); - } - - @Override - public TreePath nextElement() { - Enumeration enumer = stack.peek(); - TreePath path = enumer.nextElement(); - - if ( !enumer.hasMoreElements() ) stack.pop(); - - if ( model.getChildCount( path.getLastPathComponent() ) > 0 ) { - stack.push( new ChildrenEnumeration( path, model ) ); - } - - return path; - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/TreeState.java b/src/main/java/net/vhati/modmanager/ui/tree/TreeState.java deleted file mode 100644 index 580d1c9..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/TreeState.java +++ /dev/null @@ -1,190 +0,0 @@ -package net.vhati.modmanager.ui.tree; - -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - - -/** - * An implementation-agnostic model to pass between the GUI thread and the - * (de)serializer. - */ -public class TreeState { - - protected TreeNodeState rootNodeState = null; - - - public TreeState() { - } - - - public void setRootNodeState( TreeNodeState rootNodeState ) { - this.rootNodeState = rootNodeState; - } - - public TreeNodeState getRootNodeState() { - return rootNodeState; - } - - - public List findNodeStates( TreeNodeStateFilter filter ) { - return findNodeStates( getRootNodeState(), filter ); - } - - /** - * Returns a list of descendant node states which match a given filter. - */ - public List findNodeStates( TreeNodeState currentNodeState, TreeNodeStateFilter filter ) { - List results = new ArrayList( 1 ); - collectNodeStates( currentNodeState, filter, results ); - return results; - } - - public boolean collectNodeStates( TreeNodeState currentNodeState, TreeNodeStateFilter filter, List results ) { - int maxResultCount = filter.getMaxResultCount(); - boolean found = false; - - if ( filter.accept( currentNodeState ) ) { - results.add( currentNodeState ); - if ( maxResultCount > 0 && maxResultCount >= results.size() ) return true; - } - - if ( currentNodeState.getAllowsChildren() ) { - for ( Iterator it = currentNodeState.children(); it.hasNext(); ) { - TreeNodeState childNodeState = it.next(); - found = collectNodeStates( childNodeState, filter, results ); - if ( found && maxResultCount > 0 && maxResultCount >= results.size() ) return true; - } - } - - return found; - } - - - public boolean containsUserObject( Object o ) { - UserObjectTreeNodeStateFilter filter = new UserObjectTreeNodeStateFilter( o ); - filter.setMaxResultCount( 1 ); - List results = findNodeStates( filter ); - - return ( !results.isEmpty() ); - } - - - - public static interface TreeNodeStateFilter { - public int getMaxResultCount(); - public boolean accept( TreeNodeState nodeState ); - } - - - - public static class UserObjectTreeNodeStateFilter implements TreeNodeStateFilter { - - private Class objectClass = null; - private Object o = null; - private int maxResultCount = 0; - - - /** - * Constructs a filter matching objects of a given class (or subclass). - */ - public UserObjectTreeNodeStateFilter( Class objectClass ) { - this.objectClass = objectClass; - } - - /** - * Constructs a filter matching objects equal to a given object. - */ - public UserObjectTreeNodeStateFilter( Object o ) { - this.o = o; - } - - - public void setMaxResultCount( int n ) { maxResultCount = n; } - - @Override - public int getMaxResultCount() { return maxResultCount; } - - @Override - public boolean accept( TreeNodeState nodeState ) { - Object nodeObject = nodeState.getUserObject(); - if ( objectClass != null && nodeObject != null ) { - @SuppressWarnings("unchecked") - boolean result = objectClass.isAssignableFrom( nodeObject.getClass() ); - return result; - } - else if ( o != null ) { - return ( o.equals( nodeState.getUserObject() ) ); - } - return false; - } - } - - - public static class TreeNodeState { - - protected Object userObject = null; - protected boolean expand = false; - protected List children = null; - private TreeNodeState parentNodeState = null; - - - public TreeNodeState() { - this( false, false ); - } - - public TreeNodeState( boolean allowsChildren, boolean expand ) { - if ( allowsChildren ) { - this.expand = expand; - children = new ArrayList(); - } - } - - - /** - * Sets this node's parent to newParent but does not change the - * parent's child array. - */ - public void setParent( TreeNodeState nodeState ) { - parentNodeState = nodeState; - } - - public TreeNodeState getParent() { - return parentNodeState; - } - - - public boolean getAllowsChildren() { - return ( children != null ); - } - - public void addChild( TreeNodeState childNodeState ) { - TreeNodeState oldParent = childNodeState.getParent(); - if ( oldParent != null ) oldParent.removeChild( childNodeState ); - - childNodeState.setParent( this ); - children.add( childNodeState ); - } - - public void removeChild( TreeNodeState childNodeState ) { - children.remove( childNodeState ); - childNodeState.setParent( null ); - } - - /** - * Returns an iterator over this node state's children. - */ - public Iterator children() { - return children.iterator(); - } - - - public void setUserObject( Object userObject ) { - this.userObject = userObject; - } - - public Object getUserObject() { - return userObject; - } - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/TreeTransferHandler.java b/src/main/java/net/vhati/modmanager/ui/tree/TreeTransferHandler.java deleted file mode 100644 index 66c9bd3..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/TreeTransferHandler.java +++ /dev/null @@ -1,268 +0,0 @@ -package net.vhati.modmanager.ui.tree; - -import java.awt.Cursor; -import java.awt.datatransfer.DataFlavor; -import java.awt.datatransfer.Transferable; -import java.awt.dnd.DragSource; -import java.util.ArrayList; -import java.util.Enumeration; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.TreeMap; -import javax.swing.JComponent; -import javax.swing.JTable; -import javax.swing.JTree; -import javax.swing.TransferHandler; -import javax.swing.tree.DefaultMutableTreeNode; -import javax.swing.tree.DefaultTreeModel; -import javax.swing.tree.MutableTreeNode; -import javax.swing.tree.TreeNode; -import javax.swing.tree.TreePath; - - -/** - * A handler to enable drag-and-drop within a JTree. - * - * When dropped, copies of highlighted nodes will be made via clone() and - * inserted at the drop location, then the originals will be removed. - * - * Dragging onto a space between nodes will insert at that location. - * Dragging onto a node that allows children will insert into it. - * Dragging onto a node that doesn't allow children will insert after it. - * - * The TreeModel must be DefaultTreeModel (or a subclass). - * All nodes must be DefaultMutableTreeNode (or a subclass) and properly - * implement Cloneable. - * Set the Jtree's DropMode to ON_OR_INSERT. - * The root node must be hidden, to prevent it from being dragged. - * The tree's selection model may be set to single or multiple. - */ -public class TreeTransferHandler extends TransferHandler { - - private DataFlavor localTreePathFlavor = null; - private JTree tree = null; - - - public TreeTransferHandler( JTree tree ) { - super(); - this.tree = tree; - - try { - localTreePathFlavor = new DataFlavor( DataFlavor.javaJVMLocalObjectMimeType + ";class=\""+ TreePath[].class.getName() +"\"" ); - } - catch ( ClassNotFoundException e ) { - //log.error( e ); - } - } - - @Override - protected Transferable createTransferable( JComponent c ) { - assert ( c == tree ); - TreePath[] highlightedPaths = tree.getSelectionPaths(); - - Map> pathsByLengthMap = new TreeMap>(); - for ( TreePath path : highlightedPaths ) { - if ( path.getPath().length == 1 ) continue; // Omit root node (shouldn't drag it anyway). - - Integer pathLength = new Integer( path.getPath().length ); - if ( !pathsByLengthMap.containsKey( pathLength ) ) { - pathsByLengthMap.put( pathLength, new ArrayList() ); - } - pathsByLengthMap.get( pathLength ).add( path ); - } - // For each length (shortest-first), iterate its paths. - // For each of those paths, search longer lengths' lists, - // removing any paths that are descendants of those short ancestor nodes. - List lengthsList = new ArrayList( pathsByLengthMap.keySet() ); - for ( int i=0; i < lengthsList.size(); i++ ) { - for ( TreePath ancestorPath : pathsByLengthMap.get( lengthsList.get( i ) ) ) { - for ( int j=i+1; j < lengthsList.size(); j++ ) { - - List childPaths = pathsByLengthMap.get( lengthsList.get( j ) ); - for ( Iterator childIt = childPaths.iterator(); childIt.hasNext(); ) { - TreePath childPath = childIt.next(); - if ( ancestorPath.isDescendant( childPath ) ) { - childIt.remove(); - } - } - - } - } - } - List uniquePathList = new ArrayList(); - for ( List paths : pathsByLengthMap.values() ) { - uniquePathList.addAll( paths ); - } - TreePath[] uniquePathsArray = uniquePathList.toArray( new TreePath[uniquePathList.size()] ); - - return new TreePathTransferrable( uniquePathsArray ); - } - - @Override - public boolean canImport( TransferHandler.TransferSupport ts ) { - boolean b = ( ts.getComponent() == tree && ts.isDrop() && ts.isDataFlavorSupported( localTreePathFlavor ) ); - tree.setCursor( b ? DragSource.DefaultMoveDrop : DragSource.DefaultMoveNoDrop ); - return b; - } - - @Override - public int getSourceActions( JComponent comp ) { - return TransferHandler.MOVE; - } - - @Override - @SuppressWarnings("Unchecked") - public boolean importData( TransferHandler.TransferSupport ts ) { - if ( !canImport(ts) ) return false; - - JTree dstTree = (JTree)ts.getComponent(); - DefaultTreeModel dstTreeModel = (DefaultTreeModel)dstTree.getModel(); - JTree.DropLocation dl = (JTree.DropLocation)ts.getDropLocation(); - TreePath dropPath = dl.getPath(); // Dest parent node, or null. - int dropIndex = dl.getChildIndex(); // Insertion child index in the dest parent node, - // or -1 if dropped onto a group. - - dstTree.setCursor( Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR) ); - if ( dropPath == null ) return false; - MutableTreeNode dropParentNode = (MutableTreeNode)dropPath.getLastPathComponent(); - - // When dropping onto a non-group node, insert into the position after it instead. - if ( !dropParentNode.getAllowsChildren() ) { - MutableTreeNode prevParentNode = dropParentNode; - dropPath = dropPath.getParentPath(); - dropParentNode = (MutableTreeNode)dropPath.getLastPathComponent(); - dropIndex = dropParentNode.getIndex( prevParentNode ) + 1; - } - - try { - TreePath[] draggedPaths = (TreePath[])ts.getTransferable().getTransferData( localTreePathFlavor ); - - // Bail if the dropPath was among those dragged. - boolean badDrop = false; - for ( TreePath path : draggedPaths ) { - if ( path.equals( dropPath ) ) { - badDrop = true; - break; - } - } - - if ( !badDrop && dropParentNode.getAllowsChildren() ) { - for ( TreePath path : draggedPaths ) { - // Copy the dragged node and any children. - DefaultMutableTreeNode srcNode = (DefaultMutableTreeNode)path.getLastPathComponent(); - MutableTreeNode newNode = (MutableTreeNode)cloneNodes( srcNode ); - - if ( dropIndex != -1 ) { - // Insert. - dropParentNode.insert( newNode, dropIndex ); - dstTreeModel.nodesWereInserted( dropParentNode, new int[]{dropIndex} ); - dropIndex++; // Next insertion will be after this node. - } - else { - // Add to the end. - int addIndex = dropParentNode.getChildCount(); - dropParentNode.insert( newNode, addIndex ); - dstTreeModel.nodesWereInserted( dropParentNode, new int[]{addIndex} ); - if ( !dstTree.isExpanded( dropPath ) ) dstTree.expandPath( dropPath ); - } - } - return true; - } - } - catch ( Exception e ) { - // UnsupportedFlavorException: if Transferable.getTransferData() fails. - // IOException: if Transferable.getTransferData() fails. - // IllegalStateException: if insert/add fails because dropPath's node doesn't allow children. - //log.error( e ); - } - return false; - } - - @Override - protected void exportDone( JComponent source, Transferable data, int action ) { - if ( action == TransferHandler.MOVE || action == TransferHandler.NONE ) { - tree.setCursor( Cursor.getPredefinedCursor( Cursor.DEFAULT_CURSOR ) ); - } - - JTree srcTree = (JTree)source; - DefaultTreeModel srcTreeModel = (DefaultTreeModel)srcTree.getModel(); - - if ( action == TransferHandler.MOVE ) { - // Remove original dragged rows now that the move completed. - - try { - TreePath[] draggedPaths = (TreePath[])data.getTransferData( localTreePathFlavor ); - for ( TreePath path : draggedPaths ) { - MutableTreeNode doomedNode = (MutableTreeNode)path.getLastPathComponent(); - TreeNode parentNode = doomedNode.getParent(); - int doomedIndex = parentNode.getIndex( doomedNode ); - doomedNode.removeFromParent(); - srcTreeModel.nodesWereRemoved( parentNode, new int[]{doomedIndex}, new Object[]{doomedNode} ); - } - } - catch ( Exception e ) { - //log.error( e ); - } - } - } - - - /** - * Recursively clones a node and its descendants. - * - * The clone() methods will generally do a shallow copy, sharing - * userObjects. - * - * Sidenote: The parameter couldn't just be MutableTreeNode, because that - * doesn't offer the clone() method. And blindly using reflection to - * invoke it wouldn't be pretty. Conceivably, a settable factory could be - * designed to copy specific custom classes (using constructors instead - * of clone(). But that'd be overkill. - */ - @SuppressWarnings("Unchecked") - protected MutableTreeNode cloneNodes( DefaultMutableTreeNode srcNode ) { - MutableTreeNode resultNode = (MutableTreeNode)srcNode.clone(); - - Enumeration enumer = srcNode.children(); - while ( enumer.hasMoreElements() ) { - DefaultMutableTreeNode childNode = (DefaultMutableTreeNode)enumer.nextElement(); - int addIndex = resultNode.getChildCount(); - resultNode.insert( cloneNodes( (DefaultMutableTreeNode)childNode ), addIndex ); - } - - return resultNode; - } - - - /** - * Drag and drop TreePath data, constructed with a raw object - * from a drag source, to be transformed into a flavor - * suitable for the drop target. - */ - private class TreePathTransferrable implements Transferable { - private TreePath[] data; - - public TreePathTransferrable( TreePath[] data ) { - this.data = data; - } - - @Override - public Object getTransferData( DataFlavor flavor ) { - if ( flavor.equals( localTreePathFlavor ) ) { - return data; - } - return null; - } - - @Override - public DataFlavor[] getTransferDataFlavors() { - return new DataFlavor[] {localTreePathFlavor}; - } - - @Override - public boolean isDataFlavorSupported( DataFlavor flavor ) { - return flavor.equals( localTreePathFlavor ); - } - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/TristateButtonModel.java b/src/main/java/net/vhati/modmanager/ui/tree/TristateButtonModel.java deleted file mode 100644 index 4817243..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/TristateButtonModel.java +++ /dev/null @@ -1,105 +0,0 @@ -/** - * Copied from "TristateCheckBox Revisited" (2007-05-25) - * By Dr. Heinz M. Kabutz - * http://www.javaspecialists.co.za/archive/Issue145.html - */ - -package net.vhati.modmanager.ui.tree; - -import java.awt.event.ItemEvent; -import javax.swing.JToggleButton.ToggleButtonModel; - - -public class TristateButtonModel extends ToggleButtonModel { - - private TristateState state = TristateState.DESELECTED; - - - public TristateButtonModel( TristateState state ) { - setState( state ); - } - - public TristateButtonModel() { - this( TristateState.DESELECTED ); - } - - - public void setIndeterminate() { - setState( TristateState.INDETERMINATE ); - } - - public boolean isIndeterminate() { - return ( state == TristateState.INDETERMINATE ); - } - - - @Override - public void setEnabled( boolean enabled ) { - super.setEnabled(enabled); - // Restore state display. - displayState(); - } - - @Override - public void setSelected( boolean selected ) { - setState( selected ? TristateState.SELECTED : TristateState.DESELECTED ); - } - - @Override - public void setArmed( boolean b ) { - } - - @Override - public void setPressed( boolean b ) { - } - - - public void iterateState() { - setState( state.next() ); - } - - public void setState( TristateState state ) { - this.state = state; - displayState(); - if ( state == TristateState.INDETERMINATE && isEnabled() ) { - // Send ChangeEvent. - fireStateChanged(); - - // Send ItemEvent. - int indeterminate = 3; - fireItemStateChanged(new ItemEvent( this, ItemEvent.ITEM_STATE_CHANGED, this, indeterminate )); - } - } - - private void displayState() { - super.setSelected( state != TristateState.DESELECTED ); - super.setArmed( state == TristateState.INDETERMINATE ); - super.setPressed( state == TristateState.INDETERMINATE ); - } - - public TristateState getState() { - return state; - } - - - - public static enum TristateState { - SELECTED { - public TristateState next() { - return INDETERMINATE; - } - }, - INDETERMINATE { - public TristateState next() { - return DESELECTED; - } - }, - DESELECTED { - public TristateState next() { - return SELECTED; - } - }; - - public abstract TristateState next(); - } -} diff --git a/src/main/java/net/vhati/modmanager/ui/tree/TristateCheckBox.java b/src/main/java/net/vhati/modmanager/ui/tree/TristateCheckBox.java deleted file mode 100644 index 95a4395..0000000 --- a/src/main/java/net/vhati/modmanager/ui/tree/TristateCheckBox.java +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Based on "TristateCheckBox Revisited" (2007-05-25) - * By Dr. Heinz M. Kabutz - * http://www.javaspecialists.co.za/archive/Issue145.html - */ - -package net.vhati.modmanager.ui.tree; - -import java.awt.AWTEvent; -import java.awt.EventQueue; -import java.awt.event.ActionEvent; -import java.awt.event.InputEvent; -import java.awt.event.MouseAdapter; -import java.awt.event.MouseEvent; -import java.awt.event.MouseListener; -import javax.swing.AbstractAction; -import javax.swing.ActionMap; -import javax.swing.ButtonModel; -import javax.swing.Icon; -import javax.swing.JCheckBox; -import javax.swing.SwingUtilities; -import javax.swing.event.ChangeEvent; -import javax.swing.event.ChangeListener; -import javax.swing.plaf.ActionMapUIResource; - -import net.vhati.modmanager.ui.tree.TristateButtonModel; -import net.vhati.modmanager.ui.tree.TristateButtonModel.TristateState; - - -public class TristateCheckBox extends JCheckBox { - - private final ChangeListener enableListener; - - - public TristateCheckBox( String text, Icon icon, TristateState initial ) { - super( text, icon ); - - setModel( new TristateButtonModel( initial ) ); - - enableListener = new ChangeListener() { - @Override - public void stateChanged( ChangeEvent e ) { - TristateCheckBox.this.setFocusable( TristateCheckBox.this.getModel().isEnabled() ); - } - }; - - // Add a listener for when the mouse is pressed. - super.addMouseListener(new MouseAdapter() { - @Override - public void mousePressed( MouseEvent e ) { - TristateCheckBox.this.iterateState(); - } - }); - - // Reset the keyboard action map. - ActionMap map = new ActionMapUIResource(); - map.put( "pressed", new AbstractAction() { - @Override - public void actionPerformed( ActionEvent e ) { - TristateCheckBox.this.iterateState(); - } - }); - map.put( "released", null ); - SwingUtilities.replaceUIActionMap( this, map ); - } - - public TristateCheckBox( String text, TristateState initial ) { - this( text, null, initial ); - } - - public TristateCheckBox( String text ) { - this( text, null ); - } - - public TristateCheckBox() { - this( null ); - } - - - public void setIndeterminate() { - getTristateModel().setIndeterminate(); - } - - public boolean isIndeterminate() { - return getTristateModel().isIndeterminate(); - } - - - public void setState( TristateState state ) { - getTristateModel().setState( state ); - } - - public TristateState getState() { - return getTristateModel().getState(); - } - - - @Override - public void setModel( ButtonModel newModel ) { - super.setModel( newModel ); - - // Listen for enable changes. - if ( model instanceof TristateButtonModel ) { - model.addChangeListener( enableListener ); - } - } - - @SuppressWarnings("unchecked") - public TristateButtonModel getTristateModel() { - return (TristateButtonModel)super.getModel(); - } - - - /** - * No one may add mouse listeners, not even Swing! - */ - @Override - public void addMouseListener( MouseListener l ) { - } - - - private void iterateState() { - // Maybe do nothing at all? - if ( !super.getModel().isEnabled() ) return; - - this.grabFocus(); - - // Iterate state. - getTristateModel().iterateState(); - - // Fire ActionEvent. - int modifiers = 0; - AWTEvent currentEvent = EventQueue.getCurrentEvent(); - if ( currentEvent instanceof InputEvent ) { - modifiers = ((InputEvent)currentEvent).getModifiers(); - } - else if ( currentEvent instanceof ActionEvent ) { - modifiers = ((ActionEvent)currentEvent).getModifiers(); - } - fireActionPerformed(new ActionEvent( this, ActionEvent.ACTION_PERFORMED, this.getText(), System.currentTimeMillis(), modifiers )); - } -}