diff --git a/src/java/com/threerings/getdown/data/Application.java b/src/java/com/threerings/getdown/data/Application.java
index 7e4b97f..7e1a1b3 100644
--- a/src/java/com/threerings/getdown/data/Application.java
+++ b/src/java/com/threerings/getdown/data/Application.java
@@ -1,8 +1,10 @@
//
-// $Id: Application.java,v 1.5 2004/07/06 09:46:35 mdb Exp $
+// $Id: Application.java,v 1.6 2004/07/07 08:42:40 mdb Exp $
package com.threerings.getdown.data;
+import java.awt.Rectangle;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
@@ -42,6 +44,23 @@ public class Application
/** The name of our target version file. */
public static final String VERSION_FILE = "version.txt";
+ /** Used to communicate information about the UI displayed when
+ * updating the application. */
+ public static class UpdateInterface
+ {
+ /** The human readable name of this application. */
+ public String name;
+
+ /** The dimensions of the progress bar. */
+ public Rectangle progress;
+
+ /** The dimensions of the status display. */
+ public Rectangle status;
+
+ /** The path (relative to the appdir) to the background image. */
+ public String background;
+ }
+
/**
* Creates an application instance which records the location of the
* getdown.txt configuration file from the supplied
@@ -91,10 +110,13 @@ public class Application
* discovered later, the caller can use the application base to
* download a new config.txt file and try again.
*
+ * @return a configured UpdateInterface instance that will be used to
+ * configure the update UI.
+ *
* @exception IOException thrown if there is an error reading the file
* or an error encountered during its parsing.
*/
- public void init ()
+ public UpdateInterface init ()
throws IOException
{
// parse our configuration file
@@ -194,6 +216,15 @@ public class Application
_appargs.add(appargs[ii]);
}
}
+
+ // parse and return our application config
+ UpdateInterface ui = new UpdateInterface();
+ ui.name = (String)cdata.get("ui.name");
+ ui.progress = parseRect(
+ "ui.progress", (String)cdata.get("ui.progress"));
+ ui.status = parseRect("ui.progress", (String)cdata.get("ui.status"));
+ ui.background = (String)cdata.get("ui.background");
+ return ui;
}
/**
@@ -362,6 +393,8 @@ public class Application
StreamUtil.close(fin);
}
+ // next parse any custom user interface information
+
// finally let the caller know if we need an update
return _version != _targetVersion;
}
@@ -469,6 +502,21 @@ public class Application
return new Resource(path, getRemoteURL(path), getLocalPath(path));
}
+ /** Used to parse rectangle specifications from the config file. */
+ protected Rectangle parseRect (String name, String value)
+ {
+ if (!StringUtil.blank(value)) {
+ int[] v = StringUtil.parseIntArray(value);
+ if (v != null && v.length == 4) {
+ return new Rectangle(v[0], v[1], v[2], v[3]);
+ } else {
+ Log.warning("Ignoring invalid '" + name + "' config '" +
+ value + "'.");
+ }
+ }
+ return null;
+ }
+
protected File _appdir;
protected File _config;
protected Digest _digest;
diff --git a/src/java/com/threerings/getdown/launcher/Downloader.java b/src/java/com/threerings/getdown/launcher/Downloader.java
index fab5f82..2b23f35 100644
--- a/src/java/com/threerings/getdown/launcher/Downloader.java
+++ b/src/java/com/threerings/getdown/launcher/Downloader.java
@@ -1,5 +1,5 @@
//
-// $Id: Downloader.java,v 1.1 2004/07/06 05:13:36 mdb Exp $
+// $Id: Downloader.java,v 1.2 2004/07/07 08:42:40 mdb Exp $
package com.threerings.getdown.launcher;
@@ -260,5 +260,5 @@ public class Downloader extends Thread
/** The delay in milliseconds between notifying progress observers of
* file download progress. */
- protected static final long UPDATE_DELAY = 2500L;
+ protected static final long UPDATE_DELAY = 500L;
}
diff --git a/src/java/com/threerings/getdown/launcher/Getdown.java b/src/java/com/threerings/getdown/launcher/Getdown.java
index 276c8c8..696c69b 100644
--- a/src/java/com/threerings/getdown/launcher/Getdown.java
+++ b/src/java/com/threerings/getdown/launcher/Getdown.java
@@ -1,8 +1,15 @@
//
-// $Id: Getdown.java,v 1.4 2004/07/06 05:13:36 mdb Exp $
+// $Id: Getdown.java,v 1.5 2004/07/07 08:42:40 mdb Exp $
package com.threerings.getdown.launcher;
+import java.awt.BorderLayout;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import javax.imageio.ImageIO;
+import javax.swing.JFrame;
+
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -12,6 +19,9 @@ import java.util.List;
import org.apache.commons.io.TeeOutputStream;
+import com.samskivert.swing.util.SwingUtil;
+import com.samskivert.util.StringUtil;
+
import com.threerings.getdown.Log;
import com.threerings.getdown.data.Application;
import com.threerings.getdown.data.Resource;
@@ -31,10 +41,11 @@ public class Getdown
{
try {
for (int ii = 0; ii < MAX_LOOPS; ii++) {
- _app.init();
+ _ifc = _app.init();
if (_app.verifyMetadata()) {
Log.info("Application requires update.");
+ update();
} else {
Log.info("Metadata verified.");
@@ -63,7 +74,6 @@ public class Getdown
*/
protected void update ()
{
- Log.info("We're needin' an update Cap'n!");
}
/**
@@ -74,15 +84,20 @@ public class Getdown
{
final Object lock = new Object();
+ // create our user interface
+ createInterface();
+
// create a downloader to download our resources
Downloader.Observer obs = new Downloader.Observer() {
public void resolvingDownloads () {
Log.info("Resolving downloads...");
+ _status.setStatus("Resolving downloads...");
}
public void downloadProgress (int percent, long remaining) {
- Log.info("Download progress " + percent + "% " +
- remaining + "s remaining.");
+ _status.setStatus("Download progress " + percent + "%, " +
+ remaining + " seconds remaining.");
+ _status.setProgress(percent);
if (percent == 100) {
synchronized (lock) {
lock.notify();
@@ -91,6 +106,7 @@ public class Getdown
}
public void downloadFailed (Resource rsrc, Exception e) {
+ _status.setStatus("Download failed: " + e.getMessage());
Log.warning("Download failed [rsrc=" + rsrc + "].");
Log.logStackTrace(e);
synchronized (lock) {
@@ -117,14 +133,55 @@ public class Getdown
*/
protected void launch ()
{
- Log.info("All systems go!");
+ if (_status != null) {
+ _status.setStatus("Launching...");
+ }
+
try {
Process proc = _app.createProcess();
+ System.exit(0);
} catch (IOException ioe) {
Log.logStackTrace(ioe);
}
}
+ /**
+ * Creates our user interface, which we avoid doing unless we actually
+ * have to update something.
+ */
+ protected void createInterface ()
+ {
+ if (_frame != null) {
+ return;
+ }
+
+ Rectangle ppos = (_ifc.progress == null) ? DEFAULT_PPOS : _ifc.progress;
+ Rectangle spos = (_ifc.status == null) ? DEFAULT_STATUS : _ifc.status;
+ Rectangle bounds = ppos.union(spos);
+ bounds.grow(5, 5);
+
+ // if we have a background image, load it up
+ BufferedImage bgimg = null;
+ if (!StringUtil.blank(_ifc.background)) {
+ File bgpath = _app.getLocalPath(_ifc.background);
+ try {
+ bgimg = ImageIO.read(bgpath);
+ bounds.setRect(0, 0, bgimg.getWidth(), bgimg.getHeight());
+ } catch (IOException ioe) {
+ Log.warning("Failed to read UI background [path=" + bgpath +
+ ", error=" + ioe + "].");
+ }
+ }
+
+ // create our user interface, and display it
+ _frame = new JFrame(StringUtil.blank(_ifc.name) ? "" : _ifc.name);
+ _status = new StatusPanel(bounds, bgimg, ppos, spos);
+ _frame.getContentPane().add(_status, BorderLayout.CENTER);
+ _frame.pack();
+ SwingUtil.centerWindow(_frame);
+ _frame.show();
+ }
+
public static void main (String[] args)
{
// ensure the proper parameters are passed
@@ -161,6 +218,15 @@ public class Getdown
}
protected Application _app;
+ protected Application.UpdateInterface _ifc;
+
+ protected JFrame _frame;
+ protected StatusPanel _status;
protected static final int MAX_LOOPS = 5;
+
+ protected static final Rectangle DEFAULT_PPOS =
+ new Rectangle(0, 0, 300, 15);
+ protected static final Rectangle DEFAULT_STATUS =
+ new Rectangle(0, 20, 300, 200);
}
diff --git a/src/java/com/threerings/getdown/launcher/StatusPanel.java b/src/java/com/threerings/getdown/launcher/StatusPanel.java
new file mode 100644
index 0000000..2e89398
--- /dev/null
+++ b/src/java/com/threerings/getdown/launcher/StatusPanel.java
@@ -0,0 +1,93 @@
+//
+// $Id: StatusPanel.java,v 1.1 2004/07/07 08:42:40 mdb Exp $
+
+package com.threerings.getdown.launcher;
+
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Graphics2D;
+import java.awt.Graphics;
+import java.awt.Rectangle;
+import java.awt.image.BufferedImage;
+
+import javax.swing.JComponent;
+
+import com.samskivert.swing.Label;
+
+/**
+ * Displays download and patching status.
+ */
+public class StatusPanel extends JComponent
+{
+ public StatusPanel (Rectangle bounds, BufferedImage bgimg,
+ Rectangle ppos, Rectangle spos)
+ {
+ _bgimg = bgimg;
+ _psize = new Dimension(bounds.width, bounds.height);
+ _ppos = ppos;
+ _spos = spos;
+ }
+
+ /**
+ * Adjusts the progress display to the specified percentage.
+ */
+ public void setProgress (int percent)
+ {
+ _progress = percent;
+ repaint();
+ }
+
+ /**
+ * Displays the specified status string.
+ */
+ public void setStatus (String status)
+ {
+ _newlab = new Label(status, Color.white, null);
+ _newlab.setTargetWidth(_spos.width);
+ repaint();
+ }
+
+ // documentation inherited
+ public void paintComponent (Graphics g)
+ {
+ super.paintComponent(g);
+ Graphics2D gfx = (Graphics2D)g;
+
+ // if we have a new label; lay it out
+ if (_newlab != null) {
+ _newlab.layout(gfx);
+ _label = _newlab;
+ _newlab = null;
+ }
+
+ if (_bgimg != null) {
+ gfx.drawImage(_bgimg, 0, 0, null);
+ } else {
+ gfx.fillRect(0, 0, getWidth(), getHeight());
+ }
+
+ gfx.setColor(Color.blue);
+ gfx.fillRect(_ppos.x, _ppos.y, _progress * _ppos.width / 100,
+ _ppos.height);
+
+ gfx.setColor(Color.white);
+ gfx.draw(_ppos);
+
+ if (_label != null) {
+ _label.render(gfx, _spos.x, _spos.y);
+ }
+ }
+
+ // documentation inherited
+ public Dimension getPreferredSize ()
+ {
+ return _psize;
+ }
+
+ protected BufferedImage _bgimg;
+ protected Dimension _psize;
+ protected Rectangle _ppos, _spos;
+
+ protected int _progress = 0;
+ protected Label _label, _newlab;
+}