From: megacz Date: Fri, 30 Jan 2004 07:42:48 +0000 (+0000) Subject: 2003/12/13 08:13:32 X-Git-Tag: RC3~271 X-Git-Url: http://git.megacz.com/?p=org.ibex.core.git;a=commitdiff_plain;h=2ccad2219888c9942f62ae8b4d4207f655690948 2003/12/13 08:13:32 darcs-hash:20040130074248-2ba56-3e13b6d44b78a8625d8e8b125101e3c6c279addd.gz --- diff --git a/src/org/xwt/Box.java b/src/org/xwt/Box.java index 04fd791..5187dc0 100644 --- a/src/org/xwt/Box.java +++ b/src/org/xwt/Box.java @@ -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); diff --git a/src/org/xwt/Font.java b/src/org/xwt/Font.java index e14727f..6081253 100644 --- a/src/org/xwt/Font.java +++ b/src/org/xwt/Font.java @@ -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 text are not yet loaded, spawn a - * Task to load them and invoke callback. + * Rasterize the glyphs of text. + * + * If all the glyphs of text are not yet loaded, + * spawn a Task to load them and then invoke callback. If all + * the glyphs are 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 (width<<32)|height 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>> 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; } }; } diff --git a/src/org/xwt/HTTP.java b/src/org/xwt/HTTP.java index 5f89ce3..8d7ffda 100644 --- a/src/org/xwt/HTTP.java +++ b/src/org/xwt/HTTP.java @@ -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= 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= 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= 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: diff --git a/src/org/xwt/Main.java b/src/org/xwt/Main.java index 939922c..50bb76d 100644 --- a/src/org/xwt/Main.java +++ b/src/org/xwt/Main.java @@ -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; } diff --git a/src/org/xwt/Picture.java b/src/org/xwt/Picture.java index 426266c..eb32928 100644 --- a/src/org/xwt/Picture.java +++ b/src/org/xwt/Picture.java @@ -6,67 +6,46 @@ import org.xwt.util.*; import org.xwt.translators.*; /** - *

* The in-memory representation of a PNG or GIF image. It is * read-only. It is usually passed to PixelBuffer.drawPicture() - *

* - *

* 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). - *

*/ -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; diff --git a/src/org/xwt/PixelBuffer.java b/src/org/xwt/PixelBuffer.java index 740f224..aa69b11 100644 --- a/src/org/xwt/PixelBuffer.java +++ b/src/org/xwt/PixelBuffer.java @@ -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 w; note that the coordinates here are post-transform */ @@ -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; } diff --git a/src/org/xwt/Platform.java b/src/org/xwt/Platform.java index aada999..45e303c 100644 --- a/src/org/xwt/Platform.java +++ b/src/org/xwt/Platform.java @@ -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 <clinit> to run */ - public static void forceLoad() { } + public static void forceLoad() { } ///< do-nothing method that forces <clinit> 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 owner; 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;iactual cursor for this surface to the cursor referenced by cursor + 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 b, make window invisible; otherwise, make it non-invisible. protected abstract void _setMaximized(boolean b); ///< If b, maximize the surface; otherwise, un-maximize it. protected abstract void _setMinimized(boolean b); ///< If b, 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 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); } diff --git a/src/org/xwt/XMLRPC.java b/src/org/xwt/XMLRPC.java index 0bdca37..b38fdfd 100644 --- a/src/org/xwt/XMLRPC.java +++ b/src/org/xwt/XMLRPC.java @@ -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)); }}); } } } diff --git a/src/org/xwt/XWT.java b/src/org/xwt/XWT.java index 5fa9a8d..c5f56c3 100644 --- a/src/org/xwt/XWT.java +++ b/src/org/xwt/XWT.java @@ -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"); } diff --git a/src/org/xwt/builtin/splash.xwt b/src/org/xwt/builtin/splash.xwt index c4f3d8b..128ff26 100644 --- a/src/org/xwt/builtin/splash.xwt +++ b/src/org/xwt/builtin/splash.xwt @@ -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;