2003/12/13 08:13:32
authormegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:42:48 +0000 (07:42 +0000)
committermegacz <megacz@xwt.org>
Fri, 30 Jan 2004 07:42:48 +0000 (07:42 +0000)
darcs-hash:20040130074248-2ba56-3e13b6d44b78a8625d8e8b125101e3c6c279addd.gz

15 files changed:
src/org/xwt/Box.java
src/org/xwt/Font.java
src/org/xwt/HTTP.java
src/org/xwt/Main.java
src/org/xwt/Picture.java
src/org/xwt/PixelBuffer.java
src/org/xwt/Platform.java
src/org/xwt/Res.java
src/org/xwt/SOAP.java
src/org/xwt/Scheduler.java
src/org/xwt/Surface.java
src/org/xwt/Template.java
src/org/xwt/XMLRPC.java
src/org/xwt/XWT.java
src/org/xwt/builtin/splash.xwt

index 04fd791..5187dc0 100644 (file)
@@ -39,7 +39,7 @@ import org.xwt.translators.*;
  *  SizeChanges trigger an Surface.abort; if rendering were done in the same
  *  pass, rendering work done prior to the Surface.abort would be wasted.
  */
-public final class Box extends JSScope {
+public final class Box extends JSScope implements Scheduler.Task {
 
     // Macros //////////////////////////////////////////////////////////////////////
 
@@ -123,7 +123,7 @@ public final class Box extends JSScope {
 
     private String text = null;
     private Font font = DEFAULT_FONT; 
-    private Picture.Holder texture;
+    private Picture texture;
     private short strokewidth = 1;
     private int fillcolor = 0x00000000;
     private int strokecolor = 0xFF000000;
@@ -157,6 +157,15 @@ public final class Box extends JSScope {
 
     // Instance Methods /////////////////////////////////////////////////////////////////////
 
+
+    /** invoked when a resource needed to render ourselves finishes loading */
+    public void perform() {
+        MARK_REPACK;
+        MARK_REFLOW;
+        MARK_RESIZE;
+        dirty();
+    }
+
     public Box getRoot() { return parent == null ? this : parent.getRoot(); }
     public Surface getSurface() { return Surface.fromBox(getRoot()); }
 
@@ -356,15 +365,14 @@ public final class Box extends JSScope {
         if ((fillcolor & 0xFF000000) != 0x00000000)
             buf.fillTrapezoid(globalx, globalx + width, globaly, globalx, globalx + width, globaly + height, fillcolor);
 
-        if (texture != null && texture.picture != null)
-            for(int x = globalx; x < cx2; x += texture.picture.getWidth())
-                for(int y = globaly; y < cy2; y += texture.picture.getHeight())
-                    buf.drawPicture(texture.picture, x, y, cx1, cy1, cx2, cy2);
+        if (texture != null && texture.isLoaded)
+            for(int x = globalx; x < cx2; x += texture.width)
+                for(int y = globaly; y < cy2; y += texture.height)
+                    buf.drawPicture(texture, x, y, cx1, cy1, cx2, cy2);
 
         if (text != null && !text.equals("") && font != null)
             if (font.rasterizeGlyphs(text, buf, strokecolor, globalx, globaly, cx1, cy1, cx2, cy2, null) == -1)
-                font.rasterizeGlyphs(text, buf, strokecolor, globalx, globaly, cx1, cy1, cx2, cy2,
-                                    new Scheduler.Task() { public void perform() { Box b = Box.this; MARK_REFLOW_b; dirty(); }});
+                font.rasterizeGlyphs(text, buf, strokecolor, globalx, globaly, cx1, cy1, cx2, cy2, this);
 
         for(Box b = getChild(0); b != null; b = b.nextSibling())
             b.render(globalx, globaly, cx1, cy1, cx2, cy2, buf, null);
@@ -507,12 +515,7 @@ public final class Box extends JSScope {
         case "SizeChange":   return;  // prevent stuff from hitting the Hash
         case "childadded":   return;  // prevent stuff from hitting the Hash
         case "childremoved": return;  // prevent stuff from hitting the Hash
-        case "thisbox": {
-            if (value != null) break;
-            if (parent != null) { parent.removeChild(parent.indexNode(this)); return; }
-            Surface surface = Surface.fromBox(this); 
-            if (surface != null) surface.dispose(true);
-        }
+        case "thisbox": if (value == null) removeSelf();
         default: super.put(name, value);
         //#end
     }
@@ -569,17 +572,21 @@ public final class Box extends JSScope {
             return;
         }
         if (!(value instanceof Res)) return;
-        texture = Picture.fromRes((Res)value, null);
-        if (texture != null) {
-            minwidth = texture.picture.getWidth();
-            minheight = texture.picture.getHeight();
+
+
+        // FIXME
+        texture = Picture.load((Res)value, this);
+        if (texture.isLoaded) {
+            minwidth = texture.width;
+            minheight = texture.height;
             MARK_REFLOW;
             dirty();
             return;
         }
-        texture = Picture.fromRes((Res)value, new Scheduler.Task() { public void perform() {
-            minwidth = texture.picture.getWidth();
-            minheight = texture.picture.getHeight();
+        texture = Picture.load((Res)value, new Scheduler.Task() { public void perform() {
+            // FIXME pass this instead of a new Task?
+            minwidth = texture.width;
+            minheight = texture.height;
             Box b = Box.this; MARK_REFLOW_b;
             dirty();
         } });
@@ -684,6 +691,12 @@ public final class Box extends JSScope {
 
     // Tree Manipulation /////////////////////////////////////////////////////////////////////
 
+    void removeSelf() {
+        if (parent != null) { parent.removeChild(parent.indexNode(this)); return; }
+        Surface surface = Surface.fromBox(this); 
+        if (surface != null) surface.dispose(true);
+    }
+
     /** remove the i^th child */
     public void removeChild(int i) {
         Box b = getChild(i);
index e14727f..6081253 100644 (file)
@@ -6,31 +6,37 @@ import org.xwt.js.*;
 import java.util.*;
 import java.io.*;
 
+/** encapsulates a single font (a set of Glyphs) */
 public class Font {
 
     private Font(Res res, int pointsize) { this.res = res; this.pointsize = pointsize; }
 
-    public final int pointsize;
-    public final Res res;
-    public int max_ascent;
-    public int max_descent;
+    private static boolean glyphRenderingTaskIsScheduled = false;
+
+    public final int pointsize;                 ///< the size of the font
+    public final Res res;                       ///< the resource from which this font was loaded
+    public int max_ascent;                      ///< the maximum ascent, in pixels
+    public int max_descent;                     ///< the maximum descent, in pixels
     boolean latinCharsPreloaded = false;        ///< true if a request to preload ASCII 32-127 has begun
-    Glyph[] glyphs = new Glyph[65535];
-
-    public static class Glyph {
-        public Glyph(char c, Font f) { this.c = c; font = f; }
-        public char c;
-        public int baseline;       // within the picture, this is the y-coordinate of the baseline
-        public int advance;        // amount to increment the x-coordinate
-        public Picture p;
+    Glyph[] glyphs = new Glyph[65535];          ///< the glyphs that comprise this font
+
+    public abstract static class Glyph {
+        protected Glyph(Font font, char c) { this.font = font; this.c = c; }
         public final Font font;
+        public final char c;
+        public int baseline;                    ///< within the alphamask, this is the y-coordinate of the baseline
+        public int advance;                     ///< amount to increment the x-coordinate
+        public boolean isLoaded = false;        ///< true iff the glyph is loaded
+        public byte[] alphaChannel = null;
+        public int width = -1;                  ///< the width of the glyph
+        public int height = -1;                 ///< the height of the glyph
+        public byte[] data = null;
     }
 
 
     // Statics //////////////////////////////////////////////////////////////////////
 
     private static final Freetype freetype = new Freetype();
-    private static Cache sizeCache = new Cache(100);
     static final Queue glyphsToBeRendered = new Queue(255);
     private static Cache fontCache = new Cache(100);
     public static Font getFont(Res res, int pointsize) {
@@ -43,53 +49,63 @@ public class Font {
     // Methods //////////////////////////////////////////////////////////////////////
 
     /**
-     *  If the glyphs of <code>text</code> are not yet loaded, spawn a
-     *  Task to load them and invoke callback.
+     *  Rasterize the glyphs of <code>text</code>.
+     *
+     *  If all the glyphs of <code>text</code> are not yet loaded,
+     *  spawn a Task to load them and then invoke callback.  If all
+     *  the glyphs <i>are</i> loaded, rasterize them to the
+     *  PixelBuffer (if non-null).
      *
-     *  returns the width (in the high-order int) and height (in the
-     *  low-order int) of the string's rasterization, or -1 if some
-     *  glyphs are not loaded.
+     *  @returns <code>(width&lt;&lt;32)|height</code> if all glyphs are loaded; else -1
      */
     public long rasterizeGlyphs(final String text, PixelBuffer pb, int textcolor,
                                 int x, int y, int cx1, int cy1, int cx2, int cy2,
                                 final Scheduler.Task callback) {
         boolean encounteredUnrenderedGlyph = false;
-        int width = 0;
-        int height = 0;
+        int width = 0, height = 0;
         for(int i=0; i<text.length(); i++) {
             final char c = text.charAt(i);
             Glyph g = glyphs[c];
-            if (g == null) glyphsToBeRendered.prepend(g = glyphs[c] = new Glyph(c, this));    // prepend so they are high priority
-            if (g.p == null) {
-                glyphsToBeRendered.prepend(g);
+            if (g == null) glyphs[c] = g = Platform.createGlyph(this, c);    // prepend so they are high priority
+            if (!g.isLoaded) {
+                glyphsToBeRendered.prepend(g);              // even if it's already in the queue, boost its priority
                 encounteredUnrenderedGlyph = true;
             } else if (!encounteredUnrenderedGlyph) {
-                if (pb != null && g.p != null)
-                    pb.drawPictureAlphaOnly(g.p, x + width, y + g.font.max_ascent - g.baseline, cx1, cy1, cx2, cy2, textcolor);
+                if (pb != null)
+                    pb.drawGlyph(g, x + width, y + g.font.max_ascent - g.baseline, cx1, cy1, cx2, cy2, textcolor);
                 width += g.advance;
                 height = java.lang.Math.max(height, max_ascent + max_descent);
             }
         }
 
-        if (encounteredUnrenderedGlyph && callback != null) Scheduler.add(new Scheduler.Task() {
-                public void perform() throws Exception{
+        if (!encounteredUnrenderedGlyph) return ((((long)width) << 32) | (long)(height & 0xffffffffL));
+
+        if (callback != null) Scheduler.add(new Scheduler.Task() {
+                public void perform() throws Exception {
+                    // FEATURE this isn't terribly efficient... perhaps the task should go on the last glyph?
                     for(int i=0; i<text.length(); i++) {
                         Glyph g = glyphs[text.charAt(i)];
-                        if (g == null || g.p == null) { Scheduler.add(this); return; }
+                        if (g == null || !g.isLoaded) { Scheduler.add(this); return; }
                     }
                     callback.perform();
                 }});
+
+        // preload the Latin-1 charset with low priority (we'll probably want it)
         if (!latinCharsPreloaded) {
-            for(int i=48; i<57; i++) glyphsToBeRendered.append(glyphs[i] = new Glyph((char)i, this));
-            for(int i=32; i<47; i++) glyphsToBeRendered.append(glyphs[i] = new Glyph((char)i, this));
-            for(int i=57; i<128; i++) glyphsToBeRendered.append(glyphs[i] = new Glyph((char)i, this));
+            for(int i=48; i<57; i++) glyphsToBeRendered.append(glyphs[i] = Platform.createGlyph(this, (char)i));
+            for(int i=32; i<47; i++) glyphsToBeRendered.append(glyphs[i] = Platform.createGlyph(this, (char)i));
+            for(int i=57; i<128; i++) glyphsToBeRendered.append(glyphs[i] = Platform.createGlyph(this, (char)i));
+            latinCharsPreloaded = true;
+        }
+        if (!glyphRenderingTaskIsScheduled) {
+            Scheduler.add(glyphRenderingTask);
+            glyphRenderingTaskIsScheduled = true;
         }
-        if (!latinCharsPreloaded || encounteredUnrenderedGlyph) Scheduler.add(glyphRenderingTask);
-        latinCharsPreloaded = true;
-        if (encounteredUnrenderedGlyph) return -1;
-        return ((((long)width) << 32) | (long)(height & 0xffffffffL));
+        return -1;
     }
 
+    // FEATURE do we really need to be caching sizes?
+    private static Cache sizeCache = new Cache(1000);
     public int textwidth(String s) { return (int)((textsize(s) >>> 32) & 0xffffffff); }
     public int textheight(String s) { return (int)(textsize(s) & 0xffffffffL); }
     public long textsize(String s) {
@@ -102,10 +118,12 @@ public class Font {
 
     static final Scheduler.Task glyphRenderingTask = new Scheduler.Task() { public void perform() {
         Glyph g = (Glyph)glyphsToBeRendered.remove(false);
-        if (g == null) return;
-        if (g.p != null) { perform(); return; }
+        if (g == null) { glyphRenderingTaskIsScheduled = false; return; }
+        if (g.isLoaded) { perform(); /* tailcall to the next glyph */ return; }
         Log.log(Glyph.class, "rendering glyph " + g.c);
         try { freetype.renderGlyph(g); } catch (IOException e) { Log.log(Freetype.class, e); }
-        Scheduler.add(this);
+        g.isLoaded = true;
+        Scheduler.add(this);          // keep ourselves in the queue until there are no glyphs to render
+        glyphRenderingTaskIsScheduled = true;
     } };
 }
index 5f89ce3..8d7ffda 100644 (file)
@@ -15,35 +15,41 @@ import org.bouncycastle.crypto.digests.*;
  */
 public class HTTP {
 
-    /** the URL as passed to the original constructor; this is never changed */
-    final String originalUrl;
 
-    /** the URL to connect to; this is munged when the url is parsed */
-    URL url = null;
+    // Public Methods ////////////////////////////////////////////////////////////////////////////////////////
+
+    public HTTP(String url) { this(url, false); }
+    public HTTP(String url, boolean skipResolveCheck) { originalUrl = url; this.skipResolveCheck = skipResolveCheck; }
 
-    /** the host to connect to */
-    String host = null;
+    /** Performs an HTTP GET request */
+    public InputStream GET() throws IOException { return makeRequest(null, null); }
+
+    /** Performs an HTTP POST request; content is additional headers, blank line, and body */
+    public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
 
-    /** the port to connect on */
-    int port = -1;
+    public static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
 
-    /** true if SSL (HTTPS) should be used */
-    boolean ssl = false;
 
-    /** the path (URI) to retrieve on the server */
-    String path = null;
+    // Statics ///////////////////////////////////////////////////////////////////////////////////////////////
 
-    /** the socket */
-    Socket sock = null;
+    static Hash resolvedHosts = new Hash();            ///< cache for resolveAndCheckIfFirewalled()
+    private static Hash authCache = new Hash();        ///< cache of userInfo strings, keyed on originalUrl
 
-    /** the socket's inputstream */
-    InputStream in = null;
 
-    /** the username and password portions of the URL */
-    String userInfo = null;
+    // Instance Data ///////////////////////////////////////////////////////////////////////////////////////////////
 
-    /** cache of userInfo strings, keyed on originalUrl */
-    private static Hashtable authCache = new Hashtable();
+    final String originalUrl;              ///< the URL as passed to the original constructor; this is never changed
+    URL url = null;                        ///< the URL to connect to; this is munged when the url is parsed */
+    String host = null;                    ///< the host to connect to
+    int port = -1;                         ///< the port to connect on
+    boolean ssl = false;                   ///< true if SSL (HTTPS) should be used
+    String path = null;                    ///< the path (URI) to retrieve on the server
+    Socket sock = null;                    ///< the socket
+    InputStream in = null;                 ///< the socket's inputstream
+    String userInfo = null;                ///< the username and password portions of the URL
+    boolean firstRequest = true;           ///< true iff this is the first request to be made on this socket
+    boolean skipResolveCheck = false;      ///< allowed to skip the resolve check when downloading PAC script
+    boolean proxied = false;               ///< true iff we're using a proxy
 
     /** this is null if the current request is the first request on
      *  this HTTP connection; otherwise it is a Semaphore which will be
@@ -51,33 +57,6 @@ public class HTTP {
      */
     Semaphore okToRecieve = null;
 
-    /** true iff this is the first request to be made on this socket */
-    boolean firstRequest = true;
-
-    /** cache for resolveAndCheckIfFirewalled() */
-    static Hashtable resolvedHosts = new Hashtable();
-
-    /** true iff we are allowed to skip the resolve check (only allowed when we're downloading the PAC script) */
-    boolean skipResolveCheck = false;
-
-    /** true iff we're using a proxy */
-    boolean proxied = false;
-
-
-    // Public Methods ////////////////////////////////////////////////////////////////////////////////////////
-
-    public HTTP(String url) { this(url, false); }
-    public HTTP(String url, boolean skipResolveCheck) {
-        originalUrl = url;
-        this.skipResolveCheck = skipResolveCheck;
-    }
-
-    /** Performs an HTTP GET request */
-    public InputStream GET() throws IOException { return makeRequest(null, null); }
-
-    /** Performs an HTTP POST request; content is appended to the headers (so it should include a blank line to delimit the beginning of the body) */
-    public InputStream POST(String contentType, String content) throws IOException { return makeRequest(contentType, content); }
-
     /**
      *  This method isn't synchronized; however, only one thread can be in the inner synchronized block at a time, and the rest of
      *  the method is protected by in-order one-at-a-time semaphore lock-steps
@@ -177,7 +156,8 @@ public class HTTP {
             if ((quadbyte[0] == 10 ||
                  (quadbyte[0] == 192 && quadbyte[1] == 168) ||
                  (quadbyte[0] == 172 && (quadbyte[1] & 0xF0) == 16)) && !addr.equals(Main.originAddr))
-                throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() + "] is in a firewalled netblock");
+                throw new HTTPException("security violation: " + host + " [" + addr.getHostAddress() +
+                                        "] is in a firewalled netblock");
             return;
         } catch (UnknownHostException uhe) { }
 
@@ -194,11 +174,11 @@ public class HTTP {
         return ret;
     }
 
-
     /** Attempts a direct connection */
-    public Socket attemptDirect() {
+    private Socket attemptDirect() {
         try {
-            if (Log.verbose) Log.log(this, "attempting to create unproxied socket to " + host + ":" + port + (ssl ? " [ssl]" : ""));
+            if (Log.verbose) Log.log(this, "attempting to create unproxied socket to " +
+                                     host + ":" + port + (ssl ? " [ssl]" : ""));
             return getSocket(host, port, ssl, true);
         } catch (IOException e) {
             if (Log.on) Log.log(this, "exception in attemptDirect(): " + e);
@@ -207,23 +187,24 @@ public class HTTP {
     }
 
     /** Attempts to use an HTTP proxy, employing the CONNECT method if HTTPS is requested */
-    public Socket attemptHttpProxy(String proxyHost, int proxyPort) {
+    private Socket attemptHttpProxy(String proxyHost, int proxyPort) {
         try {
             if (Log.verbose) Log.log(this, "attempting to create HTTP proxied socket using proxy " + proxyHost + ":" + proxyPort);
-
             Socket sock = getSocket(proxyHost, proxyPort, ssl, false);
+
             if (!ssl) {
                 if (!path.startsWith("http://")) path = "http://" + host + ":" + port + path;
-            } else {
-                PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
-                BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
-                pw.print("CONNECT " + host + ":" + port + " HTTP/1.1\r\n\r\n");
-                pw.flush();
-                String s = br.readLine();
-                if (s.charAt(9) != '2') throw new HTTPException("proxy refused CONNECT method: \"" + s + "\"");
-                while (br.readLine().length() > 0) { };
-                ((SSL)sock).negotiate();
+                return sock;
             }
+
+            PrintWriter pw = new PrintWriter(new OutputStreamWriter(sock.getOutputStream()));
+            BufferedReader br = new BufferedReader(new InputStreamReader(sock.getInputStream()));
+            pw.print("CONNECT " + host + ":" + port + " HTTP/1.1\r\n\r\n");
+            pw.flush();
+            String s = br.readLine();
+            if (s.charAt(9) != '2') throw new HTTPException("proxy refused CONNECT method: \"" + s + "\"");
+            while (br.readLine().length() > 0) { };
+            ((SSL)sock).negotiate();
             return sock;
 
         } catch (IOException e) {
@@ -237,7 +218,7 @@ public class HTTP {
      *  @see http://www.socks.nec.com/protocol/socks4.protocol
      *  @see http://www.socks.nec.com/protocol/socks4a.protocol
      */
-    public Socket attemptSocksProxy(String proxyHost, int proxyPort) {
+    private Socket attemptSocksProxy(String proxyHost, int proxyPort) {
 
         // even if host is already a "x.y.z.w" string, we use this to parse it into bytes
         InetAddress addr = null;
@@ -283,7 +264,7 @@ public class HTTP {
     }
 
     /** executes the PAC script and dispatches a call to one of the other attempt methods based on the result */
-    public Socket attemptPAC(org.xwt.js.JS pacFunc) {
+    private Socket attemptPAC(org.xwt.js.JS pacFunc) {
         if (Log.verbose) Log.log(this, "evaluating PAC script");
         String pac = null;
         try {
@@ -323,10 +304,7 @@ public class HTTP {
     // Everything Else ////////////////////////////////////////////////////////////////////////////
 
     private synchronized void connect() throws IOException {
-        if (originalUrl.equals("stdio:")) {
-            in = new BufferedInputStream(System.in);
-            return;
-        }
+        if (originalUrl.equals("stdio:")) { in = new BufferedInputStream(System.in); return; }
         if (sock != null) {
             if (in == null) in = new BufferedInputStream(sock.getInputStream());
             return;
@@ -362,7 +340,7 @@ public class HTTP {
             if (pi != null) {
                 for(int i=0; i<pi.excluded.length; i++) if (host.equals(pi.excluded[i])) break OUTER;
                 if (sock == null && pi.proxyAutoConfigJSFunction != null) sock = attemptPAC(pi.proxyAutoConfigJSFunction);
-                if (sock == null && ssl && pi.httpsProxyHost != null) sock = attemptHttpProxy(pi.httpsProxyHost, pi.httpsProxyPort);
+                if (sock == null && ssl && pi.httpsProxyHost != null) sock = attemptHttpProxy(pi.httpsProxyHost,pi.httpsProxyPort);
                 if (sock == null && pi.httpProxyHost != null) sock = attemptHttpProxy(pi.httpProxyHost, pi.httpProxyPort);
                 if (sock == null && pi.socksProxyHost != null) sock = attemptSocksProxy(pi.socksProxyHost, pi.socksProxyPort);
             }
@@ -373,9 +351,9 @@ public class HTTP {
         if (in == null) in = new BufferedInputStream(sock.getInputStream());
     }
 
-    public void sendRequest(String contentType, String content) throws IOException {
-
-        PrintWriter pw = new PrintWriter(new OutputStreamWriter(originalUrl.equals("stdio:") ? System.out : sock.getOutputStream()));
+    private void sendRequest(String contentType, String content) throws IOException {
+        PrintWriter pw = new PrintWriter(new OutputStreamWriter(originalUrl.equals("stdio:") ?
+                                                                System.out : sock.getOutputStream()));
         if (content != null) {
             pw.print("POST " + path + " HTTP/1.1\r\n");
             int contentLength = content.substring(0, 2).equals("\r\n") ?
@@ -392,7 +370,7 @@ public class HTTP {
         pw.print("Host: " + (host + (port == 80 ? "" : (":" + port))) + "\r\n");
         if (proxied) pw.print("X-RequestOrigin: " + Main.originHost + "\r\n");
 
-        if (Proxy.Authorization.authorization != null) pw.print("Proxy-Authorization: " + Proxy.Authorization.authorization2 + "\r\n");
+        if (Proxy.Authorization.authorization != null) pw.print("Proxy-Authorization: "+Proxy.Authorization.authorization2+"\r\n");
         if (authCache.get(originalUrl) != null) pw.print("Authorization: " + authCache.get(originalUrl) + "\r\n");
 
         pw.print(content == null ? "\r\n" : content);
@@ -409,13 +387,15 @@ public class HTTP {
             authCache.put(originalUrl, "Basic " + new String(Base64.encode(userInfo.getBytes("US-ASCII"))));
             
         } else if (h.get("AUTHTYPE").equals("Digest")) {
-            if (authCache.get(originalUrl) != null && !"true".equals(h.get("stale"))) throw new HTTPException("username/password rejected");
+            if (authCache.get(originalUrl) != null && !"true".equals(h.get("stale")))
+                throw new HTTPException("username/password rejected");
             String path2 = path;
             if (path2.startsWith("http://") || path2.startsWith("https://")) {
                 path2 = path2.substring(path2.indexOf("://") + 3);
                 path2 = path2.substring(path2.indexOf('/'));
             }
-            String A1 = userInfo.substring(0, userInfo.indexOf(':')) + ":" + h.get("realm") + ":" + userInfo.substring(userInfo.indexOf(':') + 1);
+            String A1 = userInfo.substring(0, userInfo.indexOf(':')) + ":" + h.get("realm") + ":" +
+                userInfo.substring(userInfo.indexOf(':') + 1);
             String A2 = method + ":" + path2;
             authCache.put(originalUrl,
                           "Digest " +
@@ -446,7 +426,8 @@ public class HTTP {
         }
 
         if (!realm.equals("Digest") || Proxy.Authorization.authorization2 == null || !"true".equals(h.get("stale")))
-            Proxy.Authorization.getPassword(realm, style, sock.getInetAddress().getHostAddress(), Proxy.Authorization.authorization);
+            Proxy.Authorization.getPassword(realm, style, sock.getInetAddress().getHostAddress(),
+                                            Proxy.Authorization.authorization);
 
         if (style.equals("Basic")) {
             Proxy.Authorization.authorization2 =
@@ -458,7 +439,8 @@ public class HTTP {
             String A2 = method + ":" + path;
             Proxy.Authorization.authorization2 = 
                 "Digest " +
-                "username=\"" + Proxy.Authorization.authorization.substring(0, Proxy.Authorization.authorization.indexOf(':')) + "\", " +
+                "username=\"" + Proxy.Authorization.authorization.substring(0, Proxy.Authorization.authorization.indexOf(':')) +
+                "\", " +
                 "realm=\"" + h.get("realm") + "\", " +
                 "nonce=\"" + h.get("nonce") + "\", " +
                 "uri=\"" + path + "\", " +
@@ -485,31 +467,16 @@ public class HTTP {
     }
 
 
-    // HTTPException ///////////////////////////////////////////////////////////////////////////////////
-
-    static class HTTPException extends IOException { public HTTPException(String s) { super(s); } }
-
-
     // HTTPInputStream ///////////////////////////////////////////////////////////////////////////////////
 
     /** An input stream that represents a subset of a longer input stream. Supports HTTP chunking as well */
     public class HTTPInputStream extends FilterInputStream implements KnownLength {
 
-        /** if chunking, the number of bytes remaining in this subset; otherwise the remainder of the chunk */
-        private int length = 0;
-
-        /** this semaphore will be released when the stream is closed */
-        private Semaphore releaseMe = null;
-
-        /** indicates that we have encountered the zero-length terminator chunk */
-        boolean chunkedDone = false;
-
-        /** if we're on the first chunk, we don't pre-read a CRLF */
-        boolean firstChunk = true;
-
-        /** the length of the entire content body; -1 if chunked */
-        private int contentLength = 0;
-        public int getContentLength() { return contentLength; }
+        private int length = 0;              ///< if chunking, numbytes left in this subset; else the remainder of the chunk
+        private Semaphore releaseMe = null;  ///< this semaphore will be released when the stream is closed
+        boolean chunkedDone = false;         ///< indicates that we have encountered the zero-length terminator chunk
+        boolean firstChunk = true;           ///< if we're on the first chunk, we don't pre-read a CRLF
+        private int contentLength = 0;       ///< the length of the entire content body; -1 if chunked
 
         HTTPInputStream(InputStream in, int length, Semaphore releaseMe) throws IOException {
             super(in);
@@ -610,7 +577,9 @@ public class HTTP {
             if (read == -1 && buflen == 0) return null;
             if (read == -1) throw new HTTPException("stream closed while reading headers");
             buf[buflen++] = (byte)read;
-            if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' && buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n') break;
+            if (buflen >= 4 && buf[buflen - 4] == '\r' && buf[buflen - 3] == '\n' &&
+                buf[buflen - 2] == '\r' && buf[buflen - 1] == '\n')
+                break;
             if (buflen == buf.length) {
                 byte[] newbuf = new byte[buf.length * 2];
                 System.arraycopy(buf, 0, newbuf, 0, buflen);
@@ -682,29 +651,14 @@ public class HTTP {
         
         public Proxy() { }
         
-        /** the HTTP Proxy host to use */
-        public String httpProxyHost = null;
-        
-        /** the HTTP Proxy port to use */
-        public int httpProxyPort = -1;
-        
-        /** if a seperate proxy should be used for HTTPS, this is the hostname; otherwise, httpProxyHost is used */
-        public String httpsProxyHost = null;
-    
-        /** if a seperate proxy should be used for HTTPS, this is the port */
+        public String httpProxyHost = null;                  ///< the HTTP Proxy host to use
+        public int httpProxyPort = -1;                       ///< the HTTP Proxy port to use
+        public String httpsProxyHost = null;                 ///< seperate proxy for HTTPS
         public int httpsProxyPort = -1;
-    
-        /** the SOCKS Proxy Host to use */
-        public String socksProxyHost = null;
-    
-        /** the SOCKS Proxy Port to use */
-        public int socksProxyPort = -1;
-    
-        /** hosts to be excluded from proxy use; wildcards permitted */
-        public String[] excluded = null;
-    
-        /** the PAC script */
-        public JS proxyAutoConfigJSFunction = null;
+        public String socksProxyHost = null;                 ///< the SOCKS Proxy Host to use
+        public int socksProxyPort = -1;                      ///< the SOCKS Proxy Port to use
+        public String[] excluded = null;                     ///< hosts to be excluded from proxy use; wildcards permitted
+        public JSFunction proxyAutoConfigJSFunction = null;  ///< the PAC script
     
         public static Proxy detectProxyViaManual() {
             Proxy ret = new Proxy();
@@ -712,7 +666,8 @@ public class HTTP {
             ret.httpProxyHost = Platform.getEnv("http_proxy");
             if (ret.httpProxyHost != null) {
                 if (ret.httpProxyHost.startsWith("http://")) ret.httpProxyHost = ret.httpProxyHost.substring(7);
-                if (ret.httpProxyHost.endsWith("/")) ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.length() - 1);
+                if (ret.httpProxyHost.endsWith("/"))
+                    ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.length() - 1);
                 if (ret.httpProxyHost.indexOf(':') != -1) {
                     ret.httpProxyPort = Integer.parseInt(ret.httpProxyHost.substring(ret.httpProxyHost.indexOf(':') + 1));
                     ret.httpProxyHost = ret.httpProxyHost.substring(0, ret.httpProxyHost.indexOf(':'));
@@ -724,7 +679,8 @@ public class HTTP {
             ret.httpsProxyHost = Platform.getEnv("https_proxy");
             if (ret.httpsProxyHost != null) {
                 if (ret.httpsProxyHost.startsWith("https://")) ret.httpsProxyHost = ret.httpsProxyHost.substring(7);
-                if (ret.httpsProxyHost.endsWith("/")) ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.length() - 1);
+                if (ret.httpsProxyHost.endsWith("/"))
+                    ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.length() - 1);
                 if (ret.httpsProxyHost.indexOf(':') != -1) {
                     ret.httpsProxyPort = Integer.parseInt(ret.httpsProxyHost.substring(ret.httpsProxyHost.indexOf(':') + 1));
                     ret.httpsProxyHost = ret.httpsProxyHost.substring(0, ret.httpsProxyHost.indexOf(':'));
@@ -736,7 +692,8 @@ public class HTTP {
             ret.socksProxyHost = Platform.getEnv("socks_proxy");
             if (ret.socksProxyHost != null) {
                 if (ret.socksProxyHost.startsWith("socks://")) ret.socksProxyHost = ret.socksProxyHost.substring(7);
-                if (ret.socksProxyHost.endsWith("/")) ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.length() - 1);
+                if (ret.socksProxyHost.endsWith("/"))
+                    ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.length() - 1);
                 if (ret.socksProxyHost.indexOf(':') != -1) {
                     ret.socksProxyPort = Integer.parseInt(ret.socksProxyHost.substring(ret.socksProxyHost.indexOf(':') + 1));
                     ret.socksProxyHost = ret.socksProxyHost.substring(0, ret.socksProxyHost.indexOf(':'));
@@ -757,7 +714,7 @@ public class HTTP {
         }
     
         public static JSScope proxyAutoConfigRootJSScope = new ProxyAutoConfigRootJSScope();
-        public static JS getProxyAutoConfigJSFunction(String url) {
+        public static JSFunction getProxyAutoConfigJSFunction(String url) {
             try { 
                 BufferedReader br = new BufferedReader(new InputStreamReader(new HTTP(url, true).GET()));
                 String s = null;
@@ -785,7 +742,7 @@ public class HTTP {
 
                 JSFunction scr = JSFunction.fromReader("PAC script at " + url, 0, new StringReader(script));
                 scr.cloneWithNewParentScope(proxyAutoConfigRootJSScope).call(null, null, null, null, 0);
-                return (JS)proxyAutoConfigRootJSScope.get("FindProxyForURL");
+                return (JSFunction)proxyAutoConfigRootJSScope.get("FindProxyForURL");
             } catch (Exception e) {
                 if (Log.on) {
                     Log.log(Platform.class, "WPAD detection failed due to:");
@@ -812,7 +769,8 @@ public class HTTP {
             static public String authorization2 = null;
             static public Semaphore waitingForUser = new Semaphore();
 
-            public static synchronized void getPassword(final String realm, final String style, final String proxyIP, String oldAuth) {
+            public static synchronized void getPassword(final String realm, final String style,
+                                                        final String proxyIP, String oldAuth) {
 
                 // this handles cases where multiple threads hit the proxy auth at the same time -- all but one will block on the
                 // synchronized keyword. If 'authorization' changed while the thread was blocked, it means that the user entered
@@ -820,9 +778,8 @@ public class HTTP {
 
                 if (authorization != oldAuth) return;
                 if (Log.on) Log.log(Authorization.class, "displaying proxy authorization dialog");
-                /*
-                Message.Q.add(new Message() {
-                        public void perform() {
+                Scheduler.add(new Scheduler.Task() {
+                        public void perform() throws Exception {
                             Box b = new Box();
                             Template t = Template.getTemplate((Res)Main.builtin.get("org/xwt/builtin/proxy_authorization.xwt"));
                             t.apply(b, null, null);
@@ -830,10 +787,9 @@ public class HTTP {
                             b.put("proxyIP", proxyIP);
                         }
                     });
-                */
+
                 waitingForUser.block();
                 if (Log.on) Log.log(Authorization.class, "got proxy authorization info; re-attempting connection");
-            
             }
         }
 
@@ -845,20 +801,22 @@ public class HTTP {
             public ProxyAutoConfigRootJSScope() { super(); }
         
             public Object get(Object name) throws JSExn {
-                if (name.equals("isPlainHostName")) return isPlainHostName;
-                else if (name.equals("dnsDomainIs")) return dnsDomainIs;
-                else if (name.equals("localHostOrDomainIs")) return localHostOrDomainIs;
-                else if (name.equals("isResolvable")) return isResolvable;
-                else if (name.equals("isInNet")) return isInNet;
-                else if (name.equals("dnsResolve")) return dnsResolve;
-                else if (name.equals("myIpAddress")) return myIpAddress;
-                else if (name.equals("dnsDomainLevels")) return dnsDomainLevels;
-                else if (name.equals("shExpMatch")) return shExpMatch;
-                else if (name.equals("weekdayRange")) return weekdayRange;
-                else if (name.equals("dateRange")) return dateRange;
-                else if (name.equals("timeRange")) return timeRange;
-                else if (name.equals("ProxyConfig")) return ProxyConfig;
-                else return super.get(name);
+                //#switch(name)
+                case "isPlainHostName": return METHOD;
+                case "dnsDomainIs": return METHOD;
+                case "localHostOrDomainIs": return METHOD;
+                case "isResolvable": return METHOD;
+                case "isInNet": return METHOD;
+                case "dnsResolve": return METHOD;
+                case "myIpAddress": return METHOD;
+                case "dnsDomainLevels": return METHOD;
+                case "shExpMatch": return METHOD;
+                case "weekdayRange": return METHOD;
+                case "dateRange": return METHOD;
+                case "timeRange": return METHOD;
+                case "ProxyConfig": return ProxyConfig;
+                //#end
+                return super.get(name);
             }
         
             private static final JS proxyConfigBindings = new JS();
@@ -868,85 +826,79 @@ public class HTTP {
                         return null;
                     }
                 };
-        
-            private static final JS isPlainHostName = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        return (args.elementAt(0).toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
-                    }
-                };
-        
-            private static final JS dnsDomainIs = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        return (args.elementAt(0).toString().endsWith(args.elementAt(1).toString())) ? Boolean.TRUE : Boolean.FALSE;
-                    }
-                };
-        
-            private static final JS localHostOrDomainIs = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        return (args.elementAt(0).toString().equals(args.elementAt(1).toString()) || 
-                                (args.elementAt(0).toString().indexOf('.') == -1 && args.elementAt(1).toString().startsWith(args.elementAt(0).toString()))) ?
+
+            public Object callMethod(Object method, Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
+                //#switch(method)
+                case "isPlainHostName": return (a0.toString().indexOf('.') == -1) ? Boolean.TRUE : Boolean.FALSE;
+                case "dnsDomainIs": return (a0.toString().endsWith(a1.toString())) ? Boolean.TRUE : Boolean.FALSE;
+                case "localHostOrDomainIs":
+                    return (a0.equals(a1) || (a0.toString().indexOf('.') == -1 && a1.toString().startsWith(a0.toString()))) ? T:F;
+                case "isResolvable": try {
+                    return (InetAddress.getByName(a0.toString()) != null) ? Boolean.TRUE : Boolean.FALSE;
+                } catch (UnknownHostException e) { return F; }
+                case "isInNet":
+                    if (nargs != 3) return Boolean.FALSE;
+                    try {
+                        byte[] host = InetAddress.getByName(a0.toString()).getAddress();
+                        byte[] net = InetAddress.getByName(a1.toString()).getAddress();
+                        byte[] mask = InetAddress.getByName(a2.toString()).getAddress();
+                        return ((host[0] & mask[0]) == net[0] &&
+                                (host[1] & mask[1]) == net[1] &&
+                                (host[2] & mask[2]) == net[2] &&
+                                (host[3] & mask[3]) == net[3]) ?
                             Boolean.TRUE : Boolean.FALSE;
+                    } catch (Exception e) {
+                        throw new JSExn("exception in isInNet(): " + e);
                     }
-                };
-        
-            private static final JS isResolvable = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        try {
-                            return (InetAddress.getByName(args.elementAt(0).toString()) != null) ? Boolean.TRUE : Boolean.FALSE;
-                        } catch (UnknownHostException e) {
-                            return Boolean.FALSE;
-                        }
-                    }
-                };
-        
-            private static final JS isInNet = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        if (args.length() != 3) return Boolean.FALSE;
-                        try {
-                            byte[] host = InetAddress.getByName(args.elementAt(0).toString()).getAddress();
-                            byte[] net = InetAddress.getByName(args.elementAt(1).toString()).getAddress();
-                            byte[] mask = InetAddress.getByName(args.elementAt(2).toString()).getAddress();
-                            return ((host[0] & mask[0]) == net[0] &&
-                                    (host[1] & mask[1]) == net[1] &&
-                                    (host[2] & mask[2]) == net[2] &&
-                                    (host[3] & mask[3]) == net[3]) ?
-                                Boolean.TRUE : Boolean.FALSE;
-                        } catch (Exception e) {
-                            throw new JSExn("exception in isInNet(): " + e);
-                        }
-                    }
-                };
-        
-            private static final JS dnsResolve = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        try {
-                            return InetAddress.getByName(args.elementAt(0).toString()).getHostAddress();
-                        } catch (UnknownHostException e) {
-                            return null;
-                        }
-                    }
-                };
-        
-            private static final JS myIpAddress = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        try {
-                            return InetAddress.getLocalHost().getHostAddress();
-                        } catch (UnknownHostException e) {
-                            if (Log.on) Log.log(this, "strange... host does not know its own address");
-                            return null;
-                        }
+                case "dnsResolve":
+                    try {
+                        return InetAddress.getByName(a0.toString()).getHostAddress();
+                    } catch (UnknownHostException e) {
+                        return null;
                     }
-                };
-        
-            private static final JS dnsDomainLevels = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        String s = args.elementAt(0).toString();
-                        int i = 0;
-                        while((i = s.indexOf('.', i)) != -1) i++;
-                        return new Integer(i);
+                case "myIpAddress":
+                    try {
+                        return InetAddress.getLocalHost().getHostAddress();
+                    } catch (UnknownHostException e) {
+                        if (Log.on) Log.log(this, "strange... host does not know its own address");
+                        return null;
                     }
-                };
-        
+                case "dnsDomainLevels":
+                    String s = a0.toString();
+                    int i = 0;
+                    while((i = s.indexOf('.', i)) != -1) i++;
+                    return new Integer(i);
+                case "shExpMatch":
+                    StringTokenizer st = new StringTokenizer(a1.toString(), "*", false);
+                    String[] arr = new String[st.countTokens()];
+                    String s = a0.toString();
+                    for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken();
+                    return match(arr, s, 0) ? Boolean.TRUE : Boolean.FALSE;
+                case "weekdayRange":
+                    TimeZone tz = (nargs < 3 || a2 == null || !a2.equals("GMT")) ?
+                        TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
+                    Calendar c = new GregorianCalendar();
+                    c.setTimeZone(tz);
+                    c.setTime(new java.util.Date());
+                    java.util.Date d = c.getTime();
+                    int day = d.getDay();
+                    String d1s = a0.toString().toUpperCase();
+                    int d1 = 0, d2 = 0;
+                    for(int i=0; i<days.length; i++) if (days[i].equals(d1s)) d1 = i;
+                    
+                    if (nargs == 1)
+                        return d1 == day ? Boolean.TRUE : Boolean.FALSE;
+                    
+                    String d2s = a1.toString().toUpperCase();
+                    for(int i=0; i<days.length; i++) if (days[i].equals(d2s)) d2 = i;
+                    
+                    return ((d1 <= d2 && day >= d1 && day <= d2) || (d1 > d2 && (day >= d1 || day <= d2))) ? T : F;
+                    
+                case "dateRange": throw new JSExn("XWT does not support dateRange() in PAC scripts");
+                case "timeRange": throw new JSExn("XWT does not support timeRange() in PAC scripts");
+                //#end
+                return super.callMethod(method, a0, a1, a2, rest, nargs);
+            }       
             private static boolean match(String[] arr, String s, int index) {
                 if (index >= arr.length) return true;
                 for(int i=0; i<s.length(); i++) {
@@ -955,59 +907,10 @@ public class HTTP {
                 }
                 return false;
             }
-        
-            private static final JS shExpMatch = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        StringTokenizer st = new StringTokenizer(args.elementAt(1).toString(), "*", false);
-                        String[] arr = new String[st.countTokens()];
-                        String s = args.elementAt(0).toString();
-                        for (int i=0; st.hasMoreTokens(); i++) arr[i] = st.nextToken();
-                        return match(arr, s, 0) ? Boolean.TRUE : Boolean.FALSE;
-                    }
-                };
-        
             public static String[] days = { "SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT" };
-        
-            private static final JS weekdayRange = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        TimeZone tz = (args.length() < 3 || args.elementAt(2) == null || !args.elementAt(2).equals("GMT")) ? TimeZone.getTimeZone("UTC") : TimeZone.getDefault();
-                        Calendar c = new GregorianCalendar();
-                        c.setTimeZone(tz);
-                        c.setTime(new java.util.Date());
-                        java.util.Date d = c.getTime();
-                        int day = d.getDay();
-                    
-                        String d1s = args.elementAt(0).toString().toUpperCase();
-                        int d1 = 0, d2 = 0;
-                        for(int i=0; i<days.length; i++) if (days[i].equals(d1s)) d1 = i;
-                    
-                        if (args.length() == 1)
-                            return d1 == day ? Boolean.TRUE : Boolean.FALSE;
-                    
-                        String d2s = args.elementAt(1).toString().toUpperCase();
-                        for(int i=0; i<days.length; i++) if (days[i].equals(d2s)) d2 = i;
-                    
-                        return
-                            ((d1 <= d2 && day >= d1 && day <= d2) ||
-                             (d1 > d2 && (day >= d1 || day <= d2))) ?
-                            Boolean.TRUE : Boolean.FALSE;
-                    }
-                };
-        
-            private static final JS dateRange = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        throw new JSExn("XWT does not support dateRange() in PAC scripts");
-                    }
-                };
-        
-            private static final JS timeRange = new JS() {
-                    public Object call(org.xwt.js.JSArray args) throws JSExn {
-                        throw new JSExn("XWT does not support timeRange() in PAC scripts");
-                    }
-                };
-        
         }
 
+
         /**
          *  An implementation of Microsoft's proprietary NTLM authentication protocol.  This code was derived from Eric
          *  Glass's work, and is copyright as follows:
index 939922c..50bb76d 100644 (file)
@@ -43,7 +43,6 @@ public class Main {
         Runtime.getRuntime().exit(-1);
     }
 
-    /** common entry point */
     public static void main(String[] args) throws Exception {
         int startargs = 0;
         while (true) {
@@ -80,15 +79,12 @@ public class Main {
         final XWT xwt = new XWT(rr);
         final Res final_rr = rr;
 
-        scarHolder =
-            Picture.fromRes((Res)Main.builtin.get("org/xwt/builtin/scar.png"),
-                        new Scheduler.Task() { public void perform() throws JSExn {
-                            scarImage = scarHolder.picture;
-                            Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), xwt);
-                        } });
+        scarImage =
+            Picture.load((Res)Main.builtin.get("org/xwt/builtin/scar.png"),
+                         new Scheduler.Task() { public void perform() throws JSExn {
+                             Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), xwt);
+                         } });
 
-        new Thread() { public void run() { Scheduler.init(); } }.start();
-        Platform.running();
+        Scheduler.init();
     }
-    static Picture.Holder scarHolder = null;
 }
index 426266c..eb32928 100644 (file)
@@ -6,67 +6,46 @@ import org.xwt.util.*;
 import org.xwt.translators.*;
 
 /** 
- *    <p>
  *    The in-memory representation of a PNG or GIF image. It is
  *    read-only. It is usually passed to PixelBuffer.drawPicture()
- *    </p>
  *
- *    <p>
  *    Implementations of the Platform class should return objects
  *    supporting this interface from the createPicture() method. These
  *    implementations may choose to implement caching strategies (for
  *    example, using a Pixmap on X11).
- *    </p>
  */
-public abstract class Picture {
+public class Picture {
 
-    /** the resource that created this Picture */
-    public Res res = null;
+    public Picture() { this.res = null; }
+    public Picture(Res r) { this.res = r; }
+    private static Cache cache = new Cache(100);   ///< Picture, keyed by the Res that loaded them
 
-    /** the height of the picture */
-    public abstract int getHeight();
+    public Res res = null;                         ///< the resource we were loaded from
+    public int width = -1;                         ///< the width of the image
+    public int height = -1;                        ///< the height of the image
+    public int[] data = null;                      ///< argb samples
+    public boolean isLoaded = false;               ///< true iff the image is fully loaded
     
-    /** the width of the picture */
-    public abstract int getWidth();
-
-    /** Pictures, cache keyed by Res instance */
-    private static Cache cache = new Cache(100);
-    private static GIF gif = new GIF();
-    
-    public static class Holder {
-        public Picture picture = null;
-    }
-
     /** turns a resource into a Picture.Source and passes it to the callback */
-    public static Holder fromRes(final Res r, final Scheduler.Task callback) {
-        Holder ret = (Holder)cache.get(r);
-        if (ret == null) {
-            ret = new Holder();
-            cache.put(r, ret);
-            if (callback == null) return null;
-        }
-        final Holder holder = ret;
-        if (callback != null)
+    public static Picture load(final Res r, final Scheduler.Task callback) {
+        Picture ret = (Picture)cache.get(r);
+        if (ret == null) cache.put(r, ret = Platform.createPicture(r));
+        if (!ret.isLoaded && callback != null)
             new java.lang.Thread() { public void run() {
                 try {
-                    final byte[] b = InputStreamToByteArray.convert(r.getInputStream());
-                    Scheduler.add(new Scheduler.Task() { public void perform() {
-                        try {
-                            Picture p = null;
-                            InputStream pbis = new ByteArrayInputStream(b);
-                            if ((b[0] & 0xff) == 'G') p = gif.fromInputStream(pbis, "some picture");
-                            else if ((b[0] & 0xff) == 137) p = new PNG().fromInputStream(pbis, "some picture");
-                            else if ((b[0] & 0xff) == 0xff) p = Platform.decodeJPEG(pbis, "some picture");
-                            else throw new JSExn("couldn't figure out image type from first byte");
-                            p.res = r;
-                            holder.picture = p;
-                            Scheduler.add(callback);
-                        } catch (Exception e) {
-                            Log.log(Picture.class, e);
-                        } } });
-                } catch (IOException e) {
-                    Log.log(Picture.class, e);
-                    return;
+                    PushbackInputStream pbis = new PushbackInputStream(r.getInputStream());
+                    Picture p = null;
+                    int firstByte = pbis.read();
+                    if (firstByte == -1) throw new JSExn("empty stream reading image");
+                    pbis.unread(firstByte);
+                    if ((firstByte & 0xff) == 'G') GIF.load(pbis, p);
+                    else if ((firstByte & 0xff) == 137)  PNG.load(pbis, p);
+                    else if ((firstByte & 0xff) == 0xff) Platform.decodeJPEG(pbis, p);
+                    else throw new JSExn("couldn't figure out image type from first byte");
+                    Scheduler.add(callback);
+                } catch (Exception e) {
+                    Log.log(this, "exception while loading image");
+                    Log.log(this, e);
                 }
             } }.start();
         return ret;
index 740f224..aa69b11 100644 (file)
@@ -22,7 +22,7 @@ package org.xwt;
 public abstract class PixelBuffer {
 
     /** draw the picture at (dx1, dy1), cropping to (cx1, cy1, cx2, cy2) */
-    public abstract void drawPicture(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2);
+    protected abstract void drawPicture(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2);
 
     /** fill a trapezoid whose top and bottom edges are horizontal */
     public abstract void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color);
@@ -32,7 +32,7 @@ public abstract class PixelBuffer {
      *  channels of the Picture in the process.  This method may assume that the RGB channels of the image are all zero IFF it
      *  restores this invariant before returning.
      */
-    public abstract void drawPictureAlphaOnly(Picture source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2, int rgb);
+    public abstract void drawGlyph(Font.Glyph source, int dx1, int dy1, int cx1, int cy1, int cx2, int cy2, int rgb);
 
     // FEATURE: we want floats (inter-pixel spacing) for antialiasing, but this hoses the fastpath line drawing... argh!
     /** draws a line of width <tt>w</tt>; note that the coordinates here are <i>post-transform</i> */
@@ -41,7 +41,8 @@ public abstract class PixelBuffer {
        if (y1 > y2) { int t = x1; x1 = x2; x2 = t; t = y1; y1 = y2; y2 = t; }
 
        if (x1 == x2) {
-            fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0), x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
+            fillTrapezoid(x1 - w / 2, x2 + w / 2, y1 - (capped ? w / 2 : 0),
+                          x1 - w / 2, x2 + w / 2, y2 + (capped ? w / 2 : 0), color);
             return;
         }
 
index aada999..45e303c 100644 (file)
@@ -17,32 +17,21 @@ import org.xwt.util.*;
  *  it in the org.xwt.plat package, and add code to this file's static
  *  block to detect the new platform.
  */
-public class Platform {
+public abstract class Platform {
 
-    // Static Data /////////////////////////////////////////////////////////////////////////////////////
-
-    /**
-     *  set to true during the delivery of a KeyPressed:C-v/A-v or Press3; it is safe to use a
-     *  'global' here, since message delivery is single-threaded and non-preemptable
-     */
-    static boolean clipboardReadEnabled = false;
-
-    /** The appropriate Platform object for this JVM */
-    static Platform platform = null;
+    public Platform() { platform = this; }
 
-    /** true if proxy autodetection has already been run */
-    static boolean alreadyDetectedProxy = false;
-
-    /** the result of proxy autodetection */
-    static org.xwt.HTTP.Proxy cachedProxyInfo = null;
+    // Static Data /////////////////////////////////////////////////////////////////////////////////////
 
-    /** the current build */
-    public static String build = "unknown";
+    static boolean clipboardReadEnabled = false;       ///< true iff inside a C-v/A-v/Press3 trap handler
+    static Platform platform = null;                   ///< The appropriate Platform object for this JVM
+    static boolean alreadyDetectedProxy = false;       ///< true if proxy autodetection has already been run
+    static org.xwt.HTTP.Proxy cachedProxyInfo = null;  ///< the result of proxy autodetection
+    public static String build = "unknown";            ///< the current build
 
     // VM Detection Logic /////////////////////////////////////////////////////////////////////
 
-    /** do-nothing method that forces &lt;clinit&gt; to run */
-    public static void forceLoad() { }
+    public static void forceLoad() { }                 ///< do-nothing method that forces &lt;clinit&gt; to run
 
     // If you create a new subclass of Platform, you should add logic
     // here to detect it. Do not reference your class directly -- use
@@ -65,16 +54,12 @@ public class Platform {
             else if (version.startsWith("1.1") && vendor.startsWith("Microsoft")) platform_class = "Microsoft";
             else if (!version.startsWith("1.0") && !version.startsWith("1.1")) platform_class = "Java2";
 
-            /*
-            // Disable 2d hardware acceleration on Jaguar
-            if (os_name.equals("Mac OS X") && os_version.startsWith("10.2")) System.setProperty("com.apple.hwaccel", "false");
-            */
-            System.setProperty("com.apple.hwaccel", "true");
+            // Disable 2d hardware acceleration on Jaguar (do we need this?)
+            // if (os_name.equals("Mac OS X") && os_version.startsWith("10.2")) System.setProperty("com.apple.hwaccel", "false");
+            // System.setProperty("com.apple.hwaccel", "true");
 
-            if (platform_class != null) {
-                platform = (Platform)Class.forName("org.xwt.plat." + platform_class).newInstance();
-                platform.init();
-            }
+            if (platform_class != null)
+                Class.forName("org.xwt.plat." + platform_class).newInstance();
 
             try {
                 build = (String)Class.forName("org.xwt.Build").getField("build").get(null);
@@ -91,7 +76,7 @@ public class Platform {
 
             if (platform_class == null) {
                 if (Log.on) Log.log(Platform.class, "Unable to detect JVM");
-                new Platform().criticalAbort("Unable to detect JVM");
+                criticalAbort("Unable to detect JVM");
             }
 
             if (Log.on) Log.log(Platform.class, "                  platform = " + platform.getDescriptiveName());
@@ -101,41 +86,39 @@ public class Platform {
         } catch (Exception e) {
             if (Log.on) Log.log(Platform.class, "Exception while trying to detect JVM");
             if (Log.on) Log.log(Platform.class, e);
-            new Platform().criticalAbort("Unable to detect JVM");
+            criticalAbort("Unable to detect JVM");
         }
 
     }
 
 
-    // Methods to be Overridden ////////////////////////////////////////////////////////////////////
+    // Methods to be Overridden ///////////////////////////////////////////////////////////////////
 
     /** a string describing the VM */
     protected String getDescriptiveName() { return "Generic Java 1.1 VM"; }
 
-    /** this initializes the platform; code in here can invoke methods on Platform since Platform.platform has already been set */
-    protected void init() { }
+    /** invoked after initialization messages have been printed; useful for additional platform detection log messages */
     protected void postInit() { }
 
-    /** creates and returns a doublebuffer 'belonging' to <tt>owner</tt>; we need to associate PixelBuffers to surfaces
-     *  due to AWT 1.1 requirements (definately for Navigator, possibly also for MSJVM).
-     */
-    public static PixelBuffer createPixelBuffer(int w, int h, Surface s) { return platform._createPixelBuffer(w, h, s); }
+    protected Surface _createSurface(Box b, boolean framed) { return null; }
+    protected Picture _createPicture(Res r) { return null; }
     protected PixelBuffer _createPixelBuffer(int w, int h, Surface owner) { return null; }
-    
-    /** creates and returns a picture */
-    public static Picture createPicture(int[] data, int w, int h) { return platform._createPicture(data, w, h); }
-    public static Picture createAlphaOnlyPicture(byte[] data, int w, int h) { return platform._createAlphaOnlyPicture(data, w, h); }
-    
-    protected Picture _createPicture(int[] b, int w, int h) { return null; }
-    protected Picture _createAlphaOnlyPicture(byte[] b, int w, int h) {
-        int[] b2 = new int[b.length];
-        for(int i=0;i<b.length;i++) b2[i] = (b[i]&0xff) << 24;
-        return _createPicture(b2,w,h);
-    }
+    protected Font.Glyph _createGlyph(org.xwt.Font f, char c) { return new DefaultGlyph(f, c); }
 
-    /** should return true if it is safe to supress full-surface dirties immediately after a window resize */
-    public static boolean supressDirtyOnResize() { return platform._supressDirtyOnResize(); }
-    protected boolean _supressDirtyOnResize() { return false; }
+    public static PixelBuffer createPixelBuffer(int w, int h, Surface s) { return platform._createPixelBuffer(w, h, s); }
+    public static Picture createPicture(Res r) { return platform._createPicture(r); }
+    public static Font.Glyph createGlyph(org.xwt.Font f, char c) { return platform._createGlyph(f, c); }
+    public static Surface createSurface(Box b, boolean framed, boolean refreshable) {
+        Surface ret = platform._createSurface(b, framed);
+        ret.setInvisible(false);
+        ret.setLimits(b.minwidth, b.minheight, b.maxwidth, b.maxheight);
+        if (refreshable) {
+            Surface.allSurfaces.addElement(ret);
+            ret.dirty(0, 0, b.width, b.height);
+            ret.Refresh();
+        }
+        return ret;
+    }
 
     /** the human-readable name of the key mapped to XWT's 'alt' key */
     public static String altKeyName() { return platform._altKeyName(); }
@@ -208,8 +191,8 @@ public class Platform {
     }
 
     /** convert a JPEG into an Image */
-    public static synchronized Picture decodeJPEG(InputStream is, String name) { return platform._decodeJPEG(is, name); }
-    protected Picture _decodeJPEG(InputStream is, String name) { return null; }
+    public static synchronized void decodeJPEG(InputStream is, Picture p) { platform._decodeJPEG(is, p); }
+    protected abstract void _decodeJPEG(InputStream is, Picture p);
 
     /** displays a platform-specific "open file" dialog and returns the chosen filename, or null if the user hit cancel */
     protected String _fileDialog(String suggestedFileName, boolean write) { return null; }
@@ -256,22 +239,6 @@ public class Platform {
         platform._criticalAbort(message);
     }
 
-    /** this method invokes the platform _createSurface() method and then enforces a few post-call invariants */
-    protected Surface _createSurface(Box b, boolean framed) { return null; }
-    public static Surface createSurface(Box b, boolean framed, boolean refreshable) {
-        Surface ret = platform._createSurface(b, framed);
-        ret.setInvisible(false);
-
-        ret.setLimits(b.minwidth, b.minheight, b.maxwidth, b.maxheight);
-
-        if (refreshable) {
-            Surface.allSurfaces.addElement(ret);
-            ret.dirty(0, 0, b.width, b.height);
-            ret.Refresh();
-        }
-        return ret;
-    }
-
     /** detects proxy settings */
     protected synchronized org.xwt.HTTP.Proxy _detectProxy() { return null; }
     public static synchronized org.xwt.HTTP.Proxy detectProxy() {
@@ -289,14 +256,30 @@ public class Platform {
         if (cachedProxyInfo != null) return cachedProxyInfo;
 
         return cachedProxyInfo;
-    }
+   } 
 
     /** returns a Scheduler instance; used to implement platform-specific schedulers */
     protected Scheduler _getScheduler() { return new Scheduler(); }
     public static Scheduler getScheduler() { return platform._getScheduler(); }
-    
-    public static void running() { platform._running(); }
-    public void _running() { new Semaphore().block(); }
+
+    // FEATURE: be more efficient for many of the subclasses
+    public static class DefaultGlyph extends Font.Glyph {
+        private Picture p = null;
+        public DefaultGlyph(org.xwt.Font f, char c) { super(f, c); }
+        public Picture getPicture() {
+            if (p == null && isLoaded) {
+                p = createPicture(null);
+                p.data = new int[data.length];
+                for(int i=0; i<data.length; i++) p.data[i] = (data[i] & 0xff) << 24;
+                data = null;
+                p.width = this.width;
+                p.height = this.height;
+            }
+            return p;
+        }
+    }
+
+
 }
 
 
index f37d758..c55f1ac 100644 (file)
@@ -1,3 +1,4 @@
+// FIXME
 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
 package org.xwt;
 
@@ -28,8 +29,13 @@ public abstract class Res extends JS {
 
     public Object get(Object key) {
         if ("".equals(key)) {
-            Template t = Template.getTemplate(addExtension(".xwt"));
-            return t == null ? null : t.getStatic();
+            try {
+                Template t = Template.getTemplate(addExtension(".xwt"));
+                return t == null ? null : t.getStatic();
+            } catch (Exception e) {
+                Log.log(this, e);
+                return null;
+            }
         }
         Object ret = refCache == null ? null : refCache.get(key);
         if (ret != null) return ret;
index 104e11d..5e6d463 100644 (file)
@@ -1,3 +1,4 @@
+// FIXME
 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
 package org.xwt;
 
index 1f57fe7..8a5cd32 100644 (file)
@@ -11,20 +11,17 @@ public class Scheduler {
     // Public API Exposed to org.xwt /////////////////////////////////////////////////
 
     private static Scheduler singleton;
-    public static abstract class Task { public abstract void perform() throws Exception; }
+    public static interface Task { public abstract void perform() throws Exception; }
     public static void add(Task t) { singleton.runnable.append(t); }
-    public static void init() {
-        if (singleton != null) return;
-        singleton = Platform.getScheduler();
-        singleton.run();
-    }
-
+    public static void init() { if (singleton == null) (singleton = Platform.getScheduler()).run(); }
 
     // API which must be supported by subclasses /////////////////////////////////////
 
     /**
-     *  INVARIANT: all scheduler implementations MUST invoke Surface.renderAll() after performing a Task if no tasks remain
-     *  in the queue.  A scheduler may choose to invoke Surface.renderAll() more often than that if it so chooses.
+     *  SCHEDULER INVARIANT: all scheduler implementations MUST invoke
+     *  Surface.renderAll() after performing a Task if no tasks remain
+     *  in the queue.  A scheduler may choose to invoke
+     *  Surface.renderAll() more often than that if it so chooses.
      */
     public void run() { defaultRun(); }
     protected Scheduler() { }
@@ -39,12 +36,15 @@ public class Scheduler {
             try {
                 t.perform();
                 // FEATURE: be smarter about this
-                Surface.renderAll();
+                if (t != Surface.renderAll) add(Surface.renderAll);
             } catch (JSExn e) {
+                Log.log(Scheduler.class, "a JavaScript thread spawned with xwt.thread() threw an exception:");
                 Log.log(Scheduler.class, e.toString());
             } catch (Exception e) {
+                Log.log(Scheduler.class, "a Task threw an exception which was caught by the scheduler:");
                 Log.log(Scheduler.class, e);
             }
+            // if an Error is thrown it will cause the engine to quit
         }
     }
 }
index 0977ada..ae85745 100644 (file)
@@ -18,12 +18,12 @@ import java.util.*;
  *  Scheduler-time (the size/position/state at the time that the
  *  now-executing message was enqueued). This distinction is important.
  */
-public abstract class Surface extends PixelBuffer {
+public abstract class Surface extends PixelBuffer implements Scheduler.Task {
 
     // Static Data ////////////////////////////////////////////////////////////////////////////////
 
-    /**< the most recently enqueued Move message; used to throttle the message rate */
-    private static Scheduler.Task lastMoveMessage = null;
+    private static Boolean T = Boolean.TRUE;
+    private static Boolean F = Boolean.FALSE;
 
     /** all instances of Surface which need to be refreshed by the Scheduler */
     public static Vec allSurfaces = new Vec();
@@ -42,16 +42,21 @@ public abstract class Surface extends PixelBuffer {
 
     // Instance Data ///////////////////////////////////////////////////////////////////////
 
-    public Box root;      /**< The Box at the root of this surface */
-    public String cursor = "default";
-
-    public int mousex;                    ///< the x position of the mouse, relative to this Surface, in Scheduler-time
-    public int mousey;                    ///< the y position of the mouse, relative to this Surface, in Scheduler-time
-    public boolean minimized = false;     ///< True iff this surface is minimized, in real time
-    public boolean maximized = false;     ///< True iff this surface is maximized, in real time
+    Vec keywatchers = new Vec();
 
-    /** Dirty regions on the backbuffer which need to be rebuilt using Box.render() */
-    private DirtyList dirtyRegions = new DirtyList();
+    public Box root;                                   ///< The Box at the root of this surface
+    public String cursor = "default";                  ///< The active cursor to switch to when syncCursor() is called
+    public int mousex;                                 ///< x position of the mouse, in Scheduler-time
+    public int mousey;                                 ///< y position of the mouse, in Scheduler-time
+    public int newmousex = -1;                         ///< x position of the mouse, in realtime
+    public int newmousey = -1;                         ///< y position of the mouse, in realtime
+    public boolean minimized = false;                  ///< True iff this surface is minimized, in real time
+    public boolean maximized = false;                  ///< True iff this surface is maximized, in real time
+    private DirtyList dirtyRegions = new DirtyList();  ///< Dirty regions on the *screen*
+    private int width = 0;                             ///< The actual width of the surface
+    private int height = 0;                            ///< The actual height of the surface
+    public int getWidth() { return width; }
+    public int getHeight() { return height; }
 
 
     // Used For Simulating Clicks and DoubleClicks /////////////////////////////////////////////////
@@ -64,37 +69,29 @@ public abstract class Surface extends PixelBuffer {
     
     // Methods to be overridden by subclasses ///////////////////////////////////////////////////////
 
-    public abstract void toBack();      ///< when invoked, the surface should push itself to the back of the stacking order
-    public abstract void toFront();     ///< when invoked, the surface should pull itself to the front of the stacking order
-    public abstract void syncCursor();  ///< the <i>actual</i> cursor for this surface to the cursor referenced by <tt>cursor</tt>
+    public abstract void toBack();                     ///< should push surface to the back of the stacking order
+    public abstract void toFront();                    ///< should pull surface to the front of the stacking order
+    public abstract void syncCursor();                 ///< set the actual cursor to this.cursor if they do not match
     public abstract void setInvisible(boolean b);      ///< If <tt>b</tt>, make window invisible; otherwise, make it non-invisible.
     protected abstract void _setMaximized(boolean b);  ///< If <tt>b</tt>, maximize the surface; otherwise, un-maximize it.
     protected abstract void _setMinimized(boolean b);  ///< If <tt>b</tt>, minimize the surface; otherwise, un-minimize it.
-    public abstract void setLocation();                      ///< Set the surface's x/y position to that of the root box
-    public abstract void setTitleBarText(String s);      ///< Sets the surface's title bar text, if applicable
-    public abstract void setIcon(Picture i);      ///< Sets the surface's title bar text, if applicable
-    public abstract void _dispose();      ///< Destroy the surface
-
+    public abstract void setLocation();                ///< Set the surface's x/y position to that of the root box
+    protected abstract void _setSize(int w, int h);    ///< set the actual size of the surface
+    public abstract void setTitleBarText(String s);    ///< Sets the surface's title bar text, if applicable
+    public abstract void setIcon(Picture i);           ///< Sets the surface's title bar text, if applicable
+    public abstract void _dispose();                   ///< Destroy the surface
 
 
     // Sizing /////////////////////////////////////////////////////////////////////////////////
 
-    private int width = 0;
-    private int height = 0;
-    public int getWidth() { return width; }
-    public int getHeight() { return height; }
-
     public void setLimits(int min_width, int min_height, int max_width, int max_height) { }
 
-    /// should be overriden by subclasses; programmatically sets the height
-    protected abstract void _setSize(int width, int height); 
-
     public final void setWidth(int width) { setSize(width, this.height); }
     public final void setHeight(int height) { setSize(this.width, height); }
-    protected final void setSize(int width, int height) {
+    public final void setSize(int width, int height) {
         if (this.width == width && this.height == height) return;
-        this.width = Math.max(Main.scarImage.getWidth(), width);
-        this.height = Math.max(Main.scarImage.getHeight(), height);
+        this.width = Math.max(Main.scarImage.width, width);
+        this.height = Math.max(Main.scarImage.height, height);
         _setSize(width, height);
     }
 
@@ -109,13 +106,13 @@ public abstract class Surface extends PixelBuffer {
         else if (button == 2) button2 = true;
         else if (button == 3) button3 = true;
 
-        if (button == 1) new SimpleMessage("Press1", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 2) new SimpleMessage("Press2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
+        if (button == 1) new SimpleMessage("Press1", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 2) new SimpleMessage("Press2", T, Box.whoIs(root, mousex, mousey));
         else if (button == 3) {
             final Box who = Box.whoIs(root, mousex, mousey);
             Scheduler.add(new Scheduler.Task() { public void perform() {
                 Platform.clipboardReadEnabled = true;
-                root.putAndTriggerTraps("Press3", Boolean.TRUE);
+                root.putAndTriggerTraps("Press3", T);
                 Platform.clipboardReadEnabled = false;
             }});
         }
@@ -126,9 +123,9 @@ public abstract class Surface extends PixelBuffer {
         else if (button == 2) button2 = false;
         else if (button == 3) button3 = false;
 
-        if (button == 1) new SimpleMessage("Release1", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 2) new SimpleMessage("Release2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 3) new SimpleMessage("Release3", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
+        if (button == 1) new SimpleMessage("Release1", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 2) new SimpleMessage("Release2", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 3) new SimpleMessage("Release3", T, Box.whoIs(root, mousex, mousey));
 
         if (Platform.needsAutoClick() && Math.abs(last_press_x - mousex) < 5 && Math.abs(last_press_y - mousey) < 5) Click(button);
         last_press_x = Integer.MAX_VALUE;
@@ -136,9 +133,9 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void Click(int button) {
-        if (button == 1) new SimpleMessage("Click1", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 2) new SimpleMessage("Click2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 3) new SimpleMessage("Click3", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
+        if (button == 1) new SimpleMessage("Click1", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 2) new SimpleMessage("Click2", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 3) new SimpleMessage("Click3", T, Box.whoIs(root, mousex, mousey));
         if (Platform.needsAutoDoubleClick()) {
             long now = System.currentTimeMillis();
             if (lastClickButton == button && now - lastClickTime < 350) DoubleClick(button);
@@ -148,32 +145,22 @@ public abstract class Surface extends PixelBuffer {
     }
 
     protected final void DoubleClick(int button) {
-        if (button == 1) new SimpleMessage("DoubleClick1", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 2) new SimpleMessage("DoubleClick2", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
-        else if (button == 3) new SimpleMessage("DoubleClick3", Boolean.TRUE, Box.whoIs(root, mousex, mousey));
+        if (button == 1) new SimpleMessage("DoubleClick1", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 2) new SimpleMessage("DoubleClick2", T, Box.whoIs(root, mousex, mousey));
+        else if (button == 3) new SimpleMessage("DoubleClick3", T, Box.whoIs(root, mousex, mousey));
     }
 
-    /** Sends a KeyPressed message; subclasses should not add the C- or A- prefixes,
-     *  nor should they capitalize alphabet characters
-     */
+    /** Send a KeyPressed message; subclasses should not add the C- or A- prefixes or should they capitalize alphabet chars */
     protected final void KeyPressed(String key) {
         if (key == null) return;
-
-        if (key.toLowerCase().endsWith("shift")) shift = true;
-        else if (shift) key = key.toUpperCase();
-
-        if (key.toLowerCase().equals("alt")) alt = true;
-        else if (alt) key = "A-" + key;
-
-        if (key.toLowerCase().endsWith("control")) control = true;
-        else if (control) key = "C-" + key;
-
-        final String fkey = key;
+        if (key.toLowerCase().endsWith("shift")) shift = true;     else if (shift) key = key.toUpperCase();
+        if (key.toLowerCase().equals("alt")) alt = true;           else if (alt) key = "A-" + key;
+        if (key.toLowerCase().endsWith("control")) control = true; else if (control) key = "C-" + key;
         Scheduler.add(new KMessage(key));
     }
 
     // This is implemented as a private static class instead of an anonymous class to work around a GCJ bug
-    private class KMessage extends Scheduler.Task {
+    private class KMessage implements Scheduler.Task {
         String key = null;
         public KMessage(String k) { key = k; }
         public void perform() {
@@ -188,10 +175,7 @@ public abstract class Surface extends PixelBuffer {
         }
     }
 
-    Vec keywatchers = new Vec();
-
-    /** sends a KeyReleased message; subclasses should not add the C- or A- prefixes,
-     *  nor should they capitalize alphabet characters */
+    /** sends a KeyReleased message; subclasses should not add the C- or A- prefixes or capitalize alpha chars */
     protected final void KeyReleased(final String key) {
         if (key == null) return;
         if (key.toLowerCase().equals("alt")) alt = false;
@@ -207,6 +191,19 @@ public abstract class Surface extends PixelBuffer {
         }});
     }
 
+    /** we enqueue ourselves in the Scheduler when we have a Move message to deal with */
+    public void perform() {
+        if (mousex == newmousex && mousey == newmousey) return;
+        int oldmousex = mousex;     mousex = newmousex;
+        int oldmousey = mousey;     mousey = newmousey;
+        String oldcursor = cursor;  cursor = "default";
+        // Root gets motion events outside itself (if trapped)
+        if (!root.inside(oldmousex, oldmousey) && !root.inside(mousex, mousey) && (button1 || button2 || button3))
+            root.putAndTriggerTraps("Move", T);
+        root.Move(oldmousex, oldmousey, mousex, mousey);
+        if (!cursor.equals(oldcursor)) syncCursor();
+    }
+
     /**
      *  Notify XWT that the mouse has moved. If the mouse leaves the
      *  surface, but the host windowing system does not provide its new
@@ -214,30 +211,12 @@ public abstract class Surface extends PixelBuffer {
      *  message), the subclass should use (-1,-1).
      */
     protected final void Move(final int newmousex, final int newmousey) {
-        Scheduler.add(lastMoveMessage = new Scheduler.Task() { public void perform() {
-            synchronized(Surface.this) {
-
-                // if move messages are arriving faster than we can process them, we just start ignoring them
-                if (lastMoveMessage != this) return;
-
-                int oldmousex = mousex;
-                int oldmousey = mousey;
-                mousex = newmousex;
-                mousey = newmousey;
-
-                String oldcursor = cursor;
-                cursor = "default";
-
-                // Root gets motion events outside itself (if trapped, of course)
-                if (!root.inside(oldmousex, oldmousey) && !root.inside(mousex, mousey) && (button1 || button2 || button3))
-                    root.putAndTriggerTraps("Move", Boolean.TRUE);
-
-                root.Move(oldmousex, oldmousey, mousex, mousey);
-                if (!cursor.equals(oldcursor)) syncCursor();
-            }
-        }});
+        this.newmousex = newmousex;
+        this.newmousey = newmousey;
+        Scheduler.add(this);
     }
 
+    // FEATURE: can we avoid creating objects here?
     protected final void SizeChange(final int width, final int height) {
         Scheduler.add(new Scheduler.Task() { public void perform() {
             root.set(root.REFLOW);
@@ -247,27 +226,25 @@ public abstract class Surface extends PixelBuffer {
         abort = true;
     }
 
+    // FEATURE: can we avoid creating objects here?
     protected final void PosChange(final int x, final int y) {
         Scheduler.add(new Scheduler.Task() { public void perform() {
             root.x = x;
             root.y = y;
-            root.putAndTriggerTraps("PosChange", Boolean.TRUE);
+            root.putAndTriggerTraps("PosChange", T);
         }});
     }
 
-    protected final void Close() { new SimpleMessage("Close", Boolean.TRUE, root); }
-    protected final void Minimized(boolean b) { minimized = b; new SimpleMessage("Minimized", b ? Boolean.TRUE : Boolean.FALSE, root); }
-    protected final void Maximized(boolean b) { maximized = b; new SimpleMessage("Maximized", b ? Boolean.TRUE : Boolean.FALSE, root); }
-    protected final void Focused(boolean b) { new SimpleMessage("Focused", b ? Boolean.TRUE : Boolean.FALSE, root); }
-    public static void Refresh() {
-        Scheduler.add(new Scheduler.Task() { public void perform() {
-            renderAll();
-        }}); }
+    protected final void Close() { new SimpleMessage("Close", T, root); }
+    protected final void Minimized(boolean b) { minimized = b; new SimpleMessage("Minimized", b ? T : F, root); }
+    protected final void Maximized(boolean b) { maximized = b; new SimpleMessage("Maximized", b ? T : F, root); }
+    protected final void Focused(boolean b) { new SimpleMessage("Focused", b ? T : F, root); }
+    public static void Refresh() { Scheduler.add(renderAll); }
 
-    public static void renderAll() {
+    public static final Scheduler.Task renderAll = new Scheduler.Task() { public void perform() {
         for(int i=0; i<allSurfaces.size(); i++)
             ((Surface)allSurfaces.elementAt(i)).render();
-    }
+    } };
 
     public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); }
     public final void setMinimized(boolean b) { if (b != minimized) _setMinimized(minimized = b); }
@@ -303,11 +280,7 @@ public abstract class Surface extends PixelBuffer {
         this.root = root;
         Surface old = fromBox(root);
         if (old != null) old.dispose(false);
-        else try {
-            root.put("thisbox", null);
-        } catch (JSExn e) {
-            throw new Error("this should never happen");
-        }
+        else root.removeSelf();
         Refresh();
     }
 
@@ -316,23 +289,21 @@ public abstract class Surface extends PixelBuffer {
     /** runs the prerender() and render() pipelines in the root Box to regenerate the backbuffer, then blits it to the screen */
     public synchronized void render() {
 
-        // FIXME ugly
+        // dirty the place where the scar used to be
         if (root.width != width || root.height != height)
-            root.dirty(0, root.height - Main.scarImage.getHeight(), Main.scarImage.getWidth(), Main.scarImage.getHeight());
+            root.dirty(0, root.height - Main.scarImage.height, Main.scarImage.width, Main.scarImage.height);
 
         // make sure the root is properly sized
         do {
             abort = false;
             root.reflow(width, height);
             setSize(width, height);
-            // update mouseinside and trigger Enter/Leave as a result of box size/position changes
             String oldcursor = cursor;
             cursor = "default";
             root.Move(mousex, mousey, mousex, mousey);
             if (!cursor.equals(oldcursor)) syncCursor();
         } while(abort);
 
-        //Box.sizePosChangesSinceLastRender = 0;
         int[][] dirt = dirtyRegions.flush();
         for(int i = 0; dirt != null && i < dirt.length; i++) {
             if (dirt[i] == null) continue;
@@ -344,9 +315,7 @@ public abstract class Surface extends PixelBuffer {
             if (w <= 0 || h <= 0) continue;
 
             root.render(0, 0, x, y, x + w, y + h, this, identity);
-            drawPicture(Main.scarImage,
-                        0, root.height - Main.scarImage.getHeight(), 
-                        x, y, w, h);
+            drawPicture(Main.scarImage, 0, root.height - Main.scarImage.height, x, y, w, h);
             
             if (abort) {
 
@@ -366,7 +335,7 @@ public abstract class Surface extends PixelBuffer {
     }
 
     // FEATURE: reinstate recycler
-    public class SimpleMessage extends Scheduler.Task {
+    public class SimpleMessage implements Scheduler.Task {
         
         private Box boxContainingMouse;
         private Object value;
@@ -398,9 +367,9 @@ public abstract class Surface extends PixelBuffer {
             backbuffer.drawPicture(source, dx, dy, cx1, cy1, cx2, cy2);
         }
 
-        public void drawPictureAlphaOnly(Picture source, int dx, int dy, int cx1, int cy1, int cx2, int cy2, int argb) {
+        public void drawGlyph(Font.Glyph source, int dx, int dy, int cx1, int cy1, int cx2, int cy2, int argb) {
             screenDirtyRegions.dirty(cx1, cy1, cx2 - cx1, cy2 - cy1);
-            backbuffer.drawPictureAlphaOnly(source, dx, dy, cx1, cy1, cx2, cy2, argb);
+            backbuffer.drawGlyph(source, dx, dy, cx1, cy1, cx2, cy2, argb);
         }
 
         public void fillTrapezoid(int x1, int x2, int y1, int x3, int x4, int y2, int color) {
index 4cbf5e4..a95ec38 100644 (file)
@@ -53,30 +53,22 @@ public class Template {
 
     // Static data/methods ///////////////////////////////////////////////////////////////////
 
-    public static Template getTemplate(Res r) {
+    public static Template getTemplate(Res r) throws JSExn {
         try {
             r = r.addExtension(".xwt");
             if (r.t != null) return r.t;
             r.t = new Template(r);
-            try { new TemplateHelper().parseit(r.getInputStream(), r.t); }
-            catch (FileNotFoundException e) { Log.log(Template.class, "template not found: "+r); }
+            new TemplateHelper().parseit(r.getInputStream(), r.t);
             return r.t;
-        } catch (Exception e) {
-            if (Log.on) Log.log(r.t == null ? "null" : r.t.fileName, e);
-            return null;
+        } catch (Exception e) { throw new JSExn(e.toString());
         }
     }
 
-    public static Res resolveStringToResource(String str, XWT xwt, boolean permitAbsolute) {
+    public static Res resolveStringToResource(String str, XWT xwt, boolean permitAbsolute) throws JSExn {
         // URL
         if (str.indexOf("://") != -1) {
-            try {
-                if (permitAbsolute) return (Res)xwt.callMethod("res.url", str, null, null, null, 1);
-            } catch (JSExn jse) {
-                throw new Error("this should never happen");
-            }
-            Log.log(Template.class, "absolute URL " + str + " not permitted here");
-            return null;
+            if (permitAbsolute) return (Res)xwt.url2res(str);
+            throw new JSExn("absolute URL " + str + " not permitted here");
         }
 
         // root-relative
@@ -101,16 +93,12 @@ public class Template {
     }
 
     /** called before this template is applied or its static object can be externally referenced */
-    JSScope getStatic() {
+    JSScope getStatic() throws JSExn {
         if (staticJSScope == null) staticJSScope = new JSScope(null);
         if (staticscript == null) return staticJSScope;
         JSFunction temp = staticscript;
         staticscript = null;
-        try {
-            temp.cloneWithNewParentScope(staticJSScope).call(null, null, null, null, 0);
-        } catch (JSExn jse) {
-            throw new Error("this should never happen");
-        }
+        temp.cloneWithNewParentScope(staticJSScope).call(null, null, null, null, 0);
         return staticJSScope;
     }
     
@@ -142,7 +130,7 @@ public class Template {
         for(int i=0; keys != null && i<keys.length; i++)
             if (vals[i] instanceof String && ((String)vals[i]).charAt(0) == '$') {
                 Object rbox = pis.get(vals[i]);
-                if (rbox == null) Log.log(this, "unknown box id '"+vals[i]+"' referenced");
+                if (rbox == null) Log.log(this, "unknown box id '"+vals[i]+"' referenced in XML attribute");
                 else b.putAndTriggerTraps(keys[i], rbox);
             }
             else if ("image".equals(keys[i])) b.putAndTriggerTraps("image", resolveStringToResource((String)vals[i], xwt, true));
@@ -299,24 +287,19 @@ public class Template {
             }
         }
 
-        private JSFunction parseScript(boolean isstatic) {
+        private JSFunction parseScript(boolean isstatic) throws IOException {
             JSFunction thisscript = null;
-            try {
-                String contentString = t.content.toString();
-                if (contentString.trim().length() > 0)
-                    thisscript = JSFunction.fromReader(t.fileName + (isstatic ? "._" : ""),
-                                                       t.content_start,
-                                                       new StringReader(contentString));
-            } catch (IOException ioe) {
-                if (Log.on) Log.log(this, "  ERROR: " + ioe.getMessage());
-                thisscript = null;
-            }
+            String contentString = t.content.toString();
+            if (contentString.trim().length() > 0)
+                thisscript = JSFunction.fromReader(t.fileName + (isstatic ? "._" : ""),
+                                                   t.content_start,
+                                                   new StringReader(contentString));
             t.content = null;
             t.content_start = 0;
             return thisscript;
         }
 
-        public void endElement(XML.Element c) throws XML.SchemaException {
+        public void endElement(XML.Element c) throws XML.SchemaException, IOException {
             if (state == STATE_IN_XWT_NODE) {
                 if ("static".equals(nameOfHeaderNodeBeingProcessed) && t.content != null) t.staticscript = parseScript(true);
                 nameOfHeaderNodeBeingProcessed = null;
@@ -379,6 +362,7 @@ public class Template {
         public Object get(Object key) throws JSExn {
             if (super.has(key)) return super.get(key);
             if (key.equals("xwt")) return xwt;
+            if (key.equals("_")) return xwt.rr;
             if (key.equals("static")) return myStatic;
             return super.get(key);
         }
index 0bdca37..b38fdfd 100644 (file)
@@ -324,21 +324,17 @@ class XMLRPC extends JS {
             try {
                 new Helper().parse(br);
                 final Object result = fault ? new JSExn(objects.elementAt(0)) : objects.size() == 0 ? null : objects.elementAt(0);
-                Scheduler.add(new Scheduler.Task() { public void perform() {
-                    try {
-                        callback.unpause(result);
-                    } catch (Exception e) {
-                        // FIXME
-                        Log.log(this, e);
-                    }
-                }});
+                Scheduler.add(new Scheduler.Task() { public void perform() throws Exception { callback.unpause(result); }});
             } finally {
                 tracker.clear();
                 objects.setSize(0);
             }
-        } catch (Exception e) {
-            // FIXME
-            Log.log(this, e);
+        } catch (final JSExn e) {
+            Scheduler.add(new Scheduler.Task() { public void perform() throws Exception { callback.unpause(e); }});
+        } catch (final IOException e) {
+            Scheduler.add(new Scheduler.Task() { public void perform() throws Exception { callback.unpause(new JSExn(e)); }});
+        } catch (final XML.XMLException e) {
+            Scheduler.add(new Scheduler.Task() { public void perform() throws Exception { callback.unpause(new JSExn(e)); }});
         }
     }
 }
index 5fa9a8d..c5f56c3 100644 (file)
@@ -1,4 +1,3 @@
-// FIXME
 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
 package org.xwt;
 
@@ -17,13 +16,20 @@ public final class XWT extends JS {
     public final Res rr;
     public XWT(Res rr) { this.rr = rr; }
 
+    private Cache subCache = new Cache(20);
+    private Sub getSub(String s) {
+        Sub ret = (Sub)subCache.get(s);
+        if (ret == null) subCache.put(s, ret = new Sub(s));
+        return ret;
+    }
+
     /** lets us put multi-level get/put/call keys all in the same method */
     private class Sub extends JS {
         String key;
         Sub(String key) { this.key = key; }
         public String toString() { return "XWTSUB " + key; }
         public void put(Object key, Object val) { XWT.this.put(this.key + "." + key, val); }
-        public Object get(Object key) { return XWT.this.get(this.key + "." + key); }
+        public Object get(Object key) throws JSExn { return XWT.this.get(this.key + "." + key); }
         public Object call(Object a0, Object a1, Object a2, Object[] rest, int nargs) throws JSExn {
             return XWT.this.callMethod(this.key, a0, a1, a2, rest, nargs);
         }
@@ -32,42 +38,43 @@ public final class XWT extends JS {
         }
     }
 
-    public Object get(Object name) {
+    public Object get(Object name) throws JSExn {
         //#switch(name)
+        case "_": return rr;
         case "math": return xwtMath;
         case "string": return xwtString;
         case "date": return METHOD;
         case "origin": return Main.origin;
         case "box": return new Box();
-        case "log": return new Sub("log");
-        case "ui": return new Sub("ui");
-        case "thread": return new Sub("thread");
-        case "font": return new Sub("font");
+        case "log": return getSub("log");
+        case "ui": return getSub("ui");
+        case "thread": return getSub("thread");
+        case "font": return getSub("font");
         case "font.sansserif": return Main.builtin.get("fonts/vera/Vera.ttf");
         case "font.monospace": return Main.builtin.get("fonts/vera/VeraMono.ttf");
         case "font.serif": return Main.builtin.get("fonts/vera/VeraSe.ttf");
-        case "ui.key": return new Sub("ui.key");
-        case "ui.key.alt": return Surface.alt ? Boolean.TRUE : Boolean.FALSE;
-        case "ui.key.control": return Surface.control ? Boolean.TRUE : Boolean.FALSE;
-        case "ui.key.shift": return Surface.shift ? Boolean.TRUE : Boolean.FALSE;
+        case "ui.key": return getSub("ui.key");
+        case "ui.key.alt": return Surface.alt ? T : F;
+        case "ui.key.control": return Surface.control ? T : F;
+        case "ui.key.shift": return Surface.shift ? T : F;
         case "ui.clipboard": return Platform.getClipBoard();
         case "ui.maxdim": return new Integer(Short.MAX_VALUE);
-        case "ui.key.name": return new Sub("ui.key.name");
+        case "ui.key.name": return getSub("ui.key.name");
         case "ui.key.name.alt": return Platform.altKeyName();
-        case "ui.screen": return new Sub("ui.screen");
+        case "ui.screen": return getSub("ui.screen");
         case "ui.screen.width": return new Integer(Platform.getScreenWidth());
         case "ui.screen.height": return new Integer(Platform.getScreenHeight());
         case "fs.home": return System.getProperty("user.home");
         case "fs.temp": return System.getProperty("java.io.tempdir");
-        case "ui.mouse": return new Sub("ui.mouse");
-        case "res": return new Sub("res");
+        case "ui.mouse": return getSub("ui.mouse");
+        case "res": return getSub("res");
         case "ui.mouse.button":
-            if (Surface.button1 && !Surface.button2 && !Surface.button3) return new Integer(1);
-            else if (!Surface.button1 && Surface.button2 && !Surface.button3) return new Integer(2);
-            else if (!Surface.button1 && !Surface.button2 && Surface.button3) return new Integer(3);
+            if (Surface.button1 && !Surface.button2 && !Surface.button3) return N(1);
+            else if (!Surface.button1 && Surface.button2 && !Surface.button3) return N(2);
+            else if (!Surface.button1 && !Surface.button2 && Surface.button3) return N(3);
             else return ZERO;
-        case "undocumented": return new Sub("undocumented");
-        case "undocumented.internal": return new Sub("undocumented.internal");
+        case "undocumented": return getSub("undocumented");
+        case "undocumented.internal": return getSub("undocumented.internal");
         case "thread.yield": return METHOD;
         case "thread.sleep": return METHOD;
         case "res.watch": return METHOD;
@@ -93,21 +100,12 @@ public final class XWT extends JS {
         case "stream.parse.xml": return METHOD;
         case "stream.parse.utf8": return METHOD;
         //#end
-        return rr.get(name);
+        return super.get(name);
     }
 
     public void put(Object name, final Object value) {
         //#switch(name)
-        case "thread":
-            Scheduler.add(new Scheduler.Task() {
-                    public void perform() throws JSExn {
-                        try {
-                            JS.invokePauseable((JSFunction)value);
-                        } catch (JS.PausedException pe) {
-                            // okay; wait for ourselves to be re-enqueued
-                        }
-                    }
-                });
+        case "thread": Scheduler.add((JSFunction)value);
         case "ui.clipboard": Platform.setClipBoard((String)value);
         case "ui.frame": Platform.createSurface((Box)value, true, true);
         case "ui.window": Platform.createSurface((Box)value, false, true);
@@ -124,12 +122,11 @@ public final class XWT extends JS {
             //#switch(name)
             case "res.watch": return new Res.ProgressWatcher((Res)a, (JSFunction)b);
             case "soap": /* return new SOAP((String)a, "", (String)b, null); */
-            case "apply":
-                Template.getTemplate((Res)b).apply((Box)a, XWT.this);
-                return a;
+            case "apply": Template.getTemplate((Res)b).apply((Box)a, XWT.this); return a;
             //#end
-        } else if (nargs == 3 && name.equals("soap")) {
+
             /*
+        } else if (nargs == 3 && name.equals("soap")) {
             if (name.equals("soap"))
                 return new SOAP((String)a, "", (String)b, (String)c);
             */
@@ -142,13 +139,7 @@ public final class XWT extends JS {
             case "res.uncab": return new Res.Cab((Res)a);
             case "res.cache": try { return new Res.CachedRes((Res)a, "resources", true); }
                               catch (Res.NotCacheableException e) { throw new JSExn("this resource cannot be cached"); }
-            case "res.url":
-                String url = (String)a;
-                if (url.startsWith("http://")) return new Res.HTTP(url);
-                else if (url.startsWith("https://")) return new Res.HTTP(url);
-                else if (url.startsWith("data:")) return new Res.ByteArray(Base64.decode(url.substring(5)), null);
-                else if (url.startsWith("utf8:")) return new Res.ByteArray(url.substring(5).getBytes(), null);
-                throw new JSExn("invalid resource specifier " + url);
+            case "res.url": return url2res((String)a);
             case "thread.sleep": sleep(JS.toInt(a)); return null;
             case "log.println": Log.logJS(this, a== null ? "**null**" : a.toString()); return null;
             case "log.dump": Log.recursiveLog("","",a); return null;
@@ -167,20 +158,22 @@ public final class XWT extends JS {
         return null;
     }
 
+    public Res url2res(String url) throws JSExn {
+        if (url.startsWith("http://")) return new Res.HTTP(url);
+        else if (url.startsWith("https://")) return new Res.HTTP(url);
+        else if (url.startsWith("data:")) return new Res.ByteArray(Base64.decode(url.substring(5)), null);
+        else if (url.startsWith("utf8:")) return new Res.ByteArray(url.substring(5).getBytes(), null);
+        else throw new JSExn("invalid resource specifier " + url);
+    }
+
     public static void sleep(final int i) throws JSExn {
         try {
             final JS.UnpauseCallback callback = JS.pause();
             final long currentTime = System.currentTimeMillis();
-            new Thread() {
-                public void run() {
-                    try { Thread.sleep(i); } catch (InterruptedException e) { }
-                    Scheduler.add(new Scheduler.Task() {
-                            public void perform() throws JSExn {
-                                callback.unpause(null);
-                            }
-                        });
-                }
-            }.start();
+            new Thread() { public void run() {
+                try { Thread.sleep(i); } catch (InterruptedException e) { }
+                Scheduler.add(callback);
+            } }.start();
         } catch (JS.NotPauseableException npe) {
             throw new JSExn("you cannot sleep or yield in the foreground thread");
         }
index c4f3d8b..128ff26 100644 (file)
@@ -31,7 +31,7 @@
             $text4.font = xwt.font.sansserif;
             $text4.fontsize = 14;
             settext("downloading...");
-            fill = xwt.org.xwt.builtin["splash.png"];
+            fill = _.org.xwt.builtin["splash.png"];
             xwt.ui.window = thisbox;
             thisbox.width=394;
             thisbox.height=276;