diff --git a/src/main/java/net/vhati/modmanager/FTLModManager.java b/src/main/java/net/vhati/modmanager/FTLModManager.java index 68690c1..665e887 100644 --- a/src/main/java/net/vhati/modmanager/FTLModManager.java +++ b/src/main/java/net/vhati/modmanager/FTLModManager.java @@ -26,7 +26,7 @@ public class FTLModManager { private static final Logger log = LogManager.getLogger(FTLModManager.class); private static final String APP_NAME = "Slipstream Mod Manager"; - private static final ComparableVersion APP_VERSION = new ComparableVersion( "1.1" ); + private static final ComparableVersion APP_VERSION = new ComparableVersion( "???" ); private static final String APP_URL = "http://www.ftlgame.com/forum/viewtopic.php?f=12&t=17102"; private static final String APP_AUTHOR = "Vhati"; diff --git a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java b/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java index 25337f3..7c3521d 100644 --- a/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java +++ b/src/main/java/net/vhati/modmanager/ui/DatExtractDialog.java @@ -1,21 +1,11 @@ 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 java.io.InputStream; import java.io.IOException; import java.util.List; -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.JTextArea; import javax.swing.SwingUtilities; import net.vhati.ftldat.FTLDat; @@ -24,60 +14,23 @@ import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; -public class DatExtractDialog extends JDialog implements ActionListener { +public class DatExtractDialog extends ProgressDialog { private static final Logger log = LogManager.getLogger(DatExtractDialog.class); - private JProgressBar progressBar; - private JTextArea statusArea; - private JButton continueBtn; - - private boolean extracting = false; - private boolean done = false; + private boolean started = false; private File extractDir; private File[] datFiles; - DatExtractDialog( Frame owner, File extractDir, File[] datFiles ) { - super( owner, "Extracting...", true ); - this.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE ); + public DatExtractDialog( Frame owner, File extractDir, File[] datFiles ) { + super( owner, false ); + this.setTitle( "Extracting..." ); this.extractDir = extractDir; this.datFiles = datFiles; - progressBar = new JProgressBar(); - progressBar.setBorderPainted( true ); - progressBar.setStringPainted( false ); - - 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(); - statusArea.setBorder( BorderFactory.createEtchedBorder() ); - statusArea.setLineWrap( true ); - statusArea.setWrapStyleWord( true ); - statusArea.setEditable( false ); - - JPanel statusHolder = new JPanel( new BorderLayout() ); - statusHolder.setBorder( BorderFactory.createEmptyBorder( 15, 15, 15, 15 ) ); - statusHolder.add( statusArea ); - 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.setSize( 400, 160 ); this.setMinimumSize( this.getPreferredSize() ); this.setLocationRelativeTo( owner ); @@ -85,85 +38,25 @@ public class DatExtractDialog extends JDialog implements ActionListener { /** * Starts the background extraction thread. + * Call this immediately before setVisible(). */ public void extract() { - if ( extracting ) return; + if ( started ) return; DatExtractThread t = new DatExtractThread( extractDir, datFiles ); t.start(); + started = true; } - @Override - public void actionPerformed( ActionEvent e ) { - Object source = e.getSource(); + protected void setTaskOutcome( boolean outcome, Exception e ) { + super.setTaskOutcome( outcome, e ); + if ( !this.isShowing() ) return; - if ( source == continueBtn ) { - this.setVisible( false ); - this.dispose(); - } - } - - - private void setStatusText( String message ) { - statusArea.setText( message != null ? message : "..." ); - } - - private void setProgress( final int n ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - progressBar.setValue( n ); - } - }); - } - - private void setMaximum( final int n ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - progressBar.setMaximum( n ); - } - }); - } - - public void extractingInnerPath( final String innerPath ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setStatusText( innerPath ); - } - }); - } - - public void extractingEnded( final boolean success, final Exception e ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if ( success ) - setStatusText( "All resources extracted successfully." ); - else - setStatusText( String.format( "Error extracting dats: %s", e ) ); - - done = true; - continueBtn.setEnabled( true ); - - if ( !DatExtractDialog.this.isShowing() ) { - // The window's not visible, no continueBtn to click. - DatExtractDialog.this.dispose(); - } - } - }); - } - - - /** - * Shows or hides this component depending on the value of parameter b. - * - * If extracting has already completed, this method will do nothing. - */ - public void setVisible( boolean b ) { - if ( !done ) super.setVisible( b ); + if ( succeeded ) + setStatusText( "All resources extracted successfully." ); + else + setStatusText( String.format( "Error extracting dats: %s", e ) ); } @@ -193,27 +86,26 @@ public class DatExtractDialog extends JDialog implements ActionListener { for ( File datFile : datFiles ) { srcP = new FTLDat.FTLPack( datFile, false ); progress = 0; - setProgress( progress ); List innerPaths = srcP.list(); - setMaximum( innerPaths.size() ); + setProgressLater( progress, innerPaths.size() ); for ( String innerPath : innerPaths ) { - extractingInnerPath( innerPath ); + setStatusTextLater( innerPath ); if ( dstP.contains( innerPath ) ) { log.info( "While extracting resources, this file was overwritten: "+ innerPath ); dstP.remove( innerPath ); } is = srcP.getInputStream( innerPath ); dstP.add( innerPath, is ); - setProgress( progress++ ); + setProgressLater( progress++ ); } srcP.close(); } - extractingEnded( true, null ); + setTaskOutcomeLater( true, null ); } catch ( Exception ex ) { log.error( "Error extracting dats.", ex ); - extractingEnded( false, ex ); + setTaskOutcomeLater( false, ex ); } finally { try {if ( is != null ) is.close();} diff --git a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java index b70d94c..18119f0 100644 --- a/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java +++ b/src/main/java/net/vhati/modmanager/ui/ManagerFrame.java @@ -582,7 +582,7 @@ public class ManagerFrame extends JFrame implements ActionListener, HashObserver resDat.datFile = new File( datsDir, "resource.dat" ); resDat.bakFile = new File( backupDir, "resource.dat.bak" ); - ModPatchDialog patchDlg = new ModPatchDialog( this ); + ModPatchDialog patchDlg = new ModPatchDialog( this, true ); patchDlg.setSuccessTask( new SpawnGameTask() ); log.info( "" ); diff --git a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java index 76e0e91..a5239ed 100644 --- a/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java +++ b/src/main/java/net/vhati/modmanager/ui/ModPatchDialog.java @@ -1,68 +1,19 @@ 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.JTextArea; import javax.swing.SwingUtilities; import net.vhati.modmanager.core.ModPatchObserver; -public class ModPatchDialog extends JDialog implements ActionListener, ModPatchObserver { - - private JProgressBar progressBar; - private JTextArea statusArea; - private JButton continueBtn; - - private boolean done = false; - private boolean patchingSucceeded = false; - private Runnable successTask = null; +public class ModPatchDialog extends ProgressDialog implements ModPatchObserver { - public ModPatchDialog( Frame owner ) { - super( owner, "Patching...", true ); - this.setDefaultCloseOperation( JDialog.DO_NOTHING_ON_CLOSE ); - - 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(); - statusArea.setBorder( BorderFactory.createEtchedBorder() ); - statusArea.setLineWrap( true ); - statusArea.setWrapStyleWord( true ); - statusArea.setEditable( false ); - - JPanel statusHolder = new JPanel( new BorderLayout() ); - statusHolder.setBorder( BorderFactory.createEmptyBorder( 15, 15, 15, 15 ) ); - statusHolder.add( statusArea ); - 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 ); + public ModPatchDialog( Frame owner, boolean continueOnSuccess ) { + super( owner, true ); + this.setTitle( "Patching..." ); this.setSize( 400, 160 ); this.setMinimumSize( this.getPreferredSize() ); @@ -70,26 +21,6 @@ public class ModPatchDialog extends JDialog implements ActionListener, ModPatchO } - @Override - public void actionPerformed( ActionEvent e ) { - Object source = e.getSource(); - - if ( source == continueBtn ) { - this.setVisible( false ); - this.dispose(); - - if ( done && patchingSucceeded && successTask != null ) { - successTask.run(); - } - } - } - - - private void setStatusText( String message ) { - statusArea.setText( message != null ? message : "..." ); - } - - /** * Updates the progress bar. * @@ -100,26 +31,7 @@ public class ModPatchDialog extends JDialog implements ActionListener, ModPatchO */ @Override public void patchingProgress( 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 ); - } - } - }); + this.setProgressLater( value, max ); } /** @@ -129,12 +41,7 @@ public class ModPatchDialog extends JDialog implements ActionListener, ModPatchO */ @Override public void patchingStatus( final String message ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setStatusText( message != null ? message : "..." ); - } - }); + setStatusTextLater( message != null ? message : "..." ); } /** @@ -142,12 +49,7 @@ public class ModPatchDialog extends JDialog implements ActionListener, ModPatchO */ @Override public void patchingMod( final File modFile ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - setStatusText( String.format( "Installing mod \"%s\"...", modFile.getName() ) ); - } - }); + setStatusTextLater( String.format( "Installing mod \"%s\"...", modFile.getName() ) ); } /** @@ -156,46 +58,19 @@ public class ModPatchDialog extends JDialog implements ActionListener, ModPatchO * If anything went wrong, e may be non-null. */ @Override - public void patchingEnded( final boolean success, final Exception e ) { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - if ( success ) - setStatusText( "Patching completed." ); - else - setStatusText( String.format( "Patching failed: %s", e ) ); - - done = true; - patchingSucceeded = success; - - continueBtn.setEnabled( true ); - - if ( !ModPatchDialog.this.isShowing() ) { - // The window's not visible, no continueBtn to click. - ModPatchDialog.this.dispose(); - - if ( patchingSucceeded && successTask != null ) { - successTask.run(); - } - } - } - }); + public void patchingEnded( boolean outcome, Exception e ) { + setTaskOutcomeLater( outcome, e ); } - /** - * Sets a runnable to trigger after patching successfully. - */ - public void setSuccessTask( Runnable r ) { - successTask = r; - } + @Override + protected void setTaskOutcome( boolean outcome, Exception e ) { + super.setTaskOutcome( outcome, e ); + if ( !this.isShowing() ) return; - /** - * Shows or hides this component depending on the value of parameter b. - * - * If patching has already completed, this method will do nothing. - */ - public void setVisible( boolean b ) { - if ( !done ) super.setVisible( b ); + if ( succeeded == true ) + setStatusText( "Patching completed." ); + else + setStatusText( String.format( "Patching failed: %s", e ) ); } } diff --git a/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java b/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java new file mode 100644 index 0000000..69a4bc5 --- /dev/null +++ b/src/main/java/net/vhati/modmanager/ui/ProgressDialog.java @@ -0,0 +1,221 @@ +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.JTextArea; +import javax.swing.SwingUtilities; + + +public class ProgressDialog extends JDialog implements ActionListener { + + 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(); + statusArea.setBorder( BorderFactory.createEtchedBorder() ); + statusArea.setLineWrap( true ); + statusArea.setWrapStyleWord( true ); + statusArea.setEditable( false ); + + JPanel statusHolder = new JPanel( new BorderLayout() ); + statusHolder.setBorder( BorderFactory.createEmptyBorder( 15, 15, 15, 15 ) ); + statusHolder.add( statusArea ); + 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.setSize( 400, 160 ); + 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 responce 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 ); + } +}