public LENGTH maxwidth = MAX_LENGTH;
public LENGTH maxheight = MAX_LENGTH;
private String text = null;
- private Res font = null;
- private int fontsize = 10;
+ private Font font = null;
private LENGTH textwidth = 0;
private LENGTH textheight = 0;
for(int y = globaly; y < clipy + cliph; y += image.getHeight())
buf.drawPicture(image, x, y, clipx, clipy, clipx + clipw, clipy + cliph);
- if (text != null && !text.equals(""))
- Glyph.rasterizeGlyphs(font, fontsize, text, buf, textcolor, globalx, globaly, clipx, clipy, clipw + clipx, clipy + cliph,
- new Callback() { public Object call(Object arg) {
- Box.this.dirty();
- Box b = Box.this;
- MARK_FOR_REFLOW_b;
- b.dirty();
- return null;
- }});
+ if (text != null && !text.equals("") && font != null) // FIXME
+ font.rasterizeGlyphs(text, buf, textcolor,
+ globalx, globaly, clipx, clipy, clipw + clipx, clipy + cliph,
+ new Scheduler.Task() { public void perform() {
+ Box.this.dirty();
+ Box b = Box.this;
+ MARK_FOR_REFLOW_b;
+ b.dirty();
+ }});
if (path != null) {
if (rtransform == null) rpath = null;
* INVARIANT: after completion, getChild(min(i, numChildren())) == newnode
* WARNING: O(n) runtime, unless i == numChildren()
*/
- public void put(int i, Object value) {
+ public void put(int i, Object value, TailCall tail) {
if (i < 0) return;
Trap t = value == null ? (Trap)get("childremoved", Trap.class) : (Trap)get("childadded", Trap.class);
}
if (redirect == null) {
- if (t != null) t.perform(value);
+ if (t != null) t.perform(value, tail);
else if (Log.on) Log.logJS(this, "attempt to add/remove children to/from a node with a null redirect");
} else if (redirect != this) {
Box b = value == null ? (Box)redirect.get(i) : (Box)value;
- redirect.put(i, value);
- if (t != null) t.perform(value);
+ // FIXME: what if both of them want a tailcall?
+ redirect.put(i, value, tail);
+ if (t != null) t.perform(value, tail);
} else if (value == null) {
if (i >= 0 && i < numChildren()) {
Box b = getChild(i);
b.remove();
- if (t != null) t.perform(b);
+ if (t != null) t.perform(b, tail);
}
} else {
Box newnode = (Box)value;
MARK_FOR_REFLOW_this;
newnode.dirty();
- if (t != null) t.perform(b);
+ if (t != null) t.perform(b, tail);
}
}
public Object get(Object key, Object key2) { return super.get(key, key2); }
- public void put(Object key, Object key2, Object val) { super.put(key, key2, val); }
+ public void put2(Object key, Object key2, Object val) { super.put2(key, key2, val); }
public Object get_(Object name) { return super.get(name); }
public Object get(Object name) { return get(name, false); }
// See if we're triggering a trap
Trap t = ignoretraps ? (Trap)null : (Trap)get(name, Trap.class);
- if (t != null) return t.perform();
+ if (t != null) { t.perform(); return null; }
// Check for a special handler
SpecialBoxProperty gph = (SpecialBoxProperty)SpecialBoxProperty.specialBoxProperties.get(name);
* @param rp if this put is being performed via a root proxy, rp is the root proxy.
*/
public void put_(Object name, Object value) { super.put(name, value); }
- public void put(Object name, Object value) { put(name, value, false); }
- public void put(Object name_, Object value, boolean ignoretraps) {
- if (name_ instanceof Number) { put(((Number)name_).intValue(), value); return; }
+ public void put(Object name, Object value) { put(name, value, null, false); } // FIXME: correct?
+ public void put(Object name, Object value, TailCall tail) { put(name, value, tail, false); }
+ public void put(Object name_, Object value, TailCall tail, boolean ignoretraps) {
+ if (name_ instanceof Number) { put(((Number)name_).intValue(), value, tail); }
if (!(name_ instanceof String)) { super.put(name_,value); return; }
String name = name_.toString();
if (!ignoretraps) {
Trap t = (Trap)get(name, Trap.class);
- if (t != null) {
- t.perform(value);
- return;
- }
+ if (t != null) { t.perform(value, tail); return; }
}
SpecialBoxProperty gph = (SpecialBoxProperty)SpecialBoxProperty.specialBoxProperties.get(name);
// note that JavaScript box[0] will invoke put(int i), not put(String s)
if (oldparent != null) {
Trap t = (Trap)oldparent.get("childremoved", Trap.class);
- if (t != null) t.perform(this);
+
+ // FIXME!!
+ //if (t != null) t.perform(this, tail);
}
}
public void recompute_font() {
if (text == null) { textwidth = textheight = 0; return; }
- if (font == null) { /* FIXME */ }
- long widthheight = Glyph.rasterizeGlyphs(font, fontsize, text, null, textcolor, 0, 0, 0, 0, 0, 0,
- new Callback() { public Object call(Object arg) {
- Box b = Box.this;
- recompute_font();
- MARK_FOR_REFLOW_b;
- b.dirty();
- return null;
- } });
- if (widthheight == -1) return;
+ if (font == null) { /* FIXME */ return; }
+ long widthheight = font.rasterizeGlyphs(text, null, textcolor, 0, 0, 0, 0, 0, 0,
+ new Scheduler.Task() { public void perform() {
+ Box b = Box.this;
+ recompute_font();
+ MARK_FOR_REFLOW_b;
+ b.dirty();
+ } });
textwidth = (int)((widthheight & 0xffff0000) >> 16);
textheight = (int)(widthheight & 0x0000ffff);
MARK_FOR_REFLOW_this;
specialBoxProperties.put("fill", new ColorBoxProperty() {
public void put(final Box b, final Object value) {
if (value == null || !(value instanceof Res)) super.put(b, value);
- else Picture.fromRes((Res)value, new Callback() { public Object call(Object pic) {
- if (pic == b.image) return null;
- b.image = (Picture)pic;
- b.minwidth = b.image.getWidth();
- b.minheight = b.image.getHeight();
- MARK_FOR_REFLOW_b;
- b.dirty();
- return null;
- } });
+ else {
+ Callback callback = new Callback() { public Object call(Object pic) {
+ b.image = (Picture)pic;
+ b.minwidth = b.image.getWidth();
+ b.minheight = b.image.getHeight();
+ MARK_FOR_REFLOW_b;
+ b.dirty();
+ return null;
+ } };
+ Picture pic = Picture.fromRes((Res)value, callback);
+ if (pic != null) callback.call(pic);
+ }
}
public int getColor(Box b) { return b.fillcolor; }
public void putColor(Box b, int argb) { b.fillcolor = argb; }
public Object get(Box b) { return b.font; }
public void put(Box b, Object value) {
// FIXME: translate value into a resource if it is a string
- b.font = value == null ? null : (Res)value;
+ b.font = value == null ? null :
+ Font.getFont((Res)value, b.font == null ? 10 : b.font.pointsize); // FIXME
MARK_FOR_REFLOW_b;
b.flags |= FONT_CHANGED_FLAG;
b.dirty();
specialBoxProperties.put("fontsize", new SpecialBoxProperty() {
public Object get(Box b) { return b.font; }
public void put(Box b, Object value) {
- if (b.fontsize == stoi(value)) return;
- b.fontsize = stoi(value);
+ if (b.font != null && b.font.pointsize == stoi(value)) return;
+ b.font = value == null ? null :
+ Font.getFont(b.font == null ? null : b.font.res, stoi(value)); // FIXME
MARK_FOR_REFLOW_b;
b.flags |= FONT_CHANGED_FLAG;
b.dirty();
Picture.fromRes((Res)Main.builtin.get("org/xwt/builtin/scar.png"), new Callback() {
public Object call(Object arg) {
scarImage = (Picture)arg;
- Scheduler.add(new Scheduler.Task() {
- public Object call(Object args) {
- Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), null, xwt);
- return null;
- }
- });
+ Scheduler.add(new Scheduler.Task() { public void perform() {
+ Template.getTemplate(((Res)final_rr.get(initialTemplate))).apply(new Box(), null, xwt);
+ } });
return null;
} });
- new Thread() { public void run() { Scheduler.run(); } }.start();
+ new Thread() { public void run() { Scheduler.singleton.run(); } }.start();
Platform.running();
}
private static Cache cache = new Cache();
private static GIF gif = new GIF();
+ // FIXME: return a Picture that gets filled in later
/** turns a resource into a Picture.Source and passes it to the callback */
- public static void fromRes(final Res r, final Callback callback) {
+ public static Picture fromRes(final Res r, final Callback callback) {
Picture ret = (Picture)cache.get(r);
- if (ret != null) {
- callback.call(ret);
- return;
- }
-
+ if (ret != null) return ret;
try {
- // FIXME: put self in background
- PushbackInputStream pbis = new PushbackInputStream(r.getInputStream());
- int c = pbis.read();
- pbis.unread(c);
- if (c == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
- else if (c == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
- else if (c == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
- else throw new JS.Exn("couldn't figure out image type from first byte");
- cache.put(r, ret);
- ret.res = r;
- callback.call(ret);
+ Platform.inputStreamToByteArray(r.getInputStream(), new Callback() { public Object call(Object o) {
+ try {
+ Picture ret = null;
+ byte[] b = (byte[])o;
+ InputStream pbis = new ByteArrayInputStream(b);
+ if ((b[0] & 0xff) == 'G') ret = gif.fromInputStream(pbis, r.getDescriptiveName());
+ else if ((b[0] & 0xff) == 137) ret = new PNG().fromInputStream(pbis, r.getDescriptiveName());
+ else if ((b[0] & 0xff) == 0xff) ret = Platform.decodeJPEG(pbis, r.getDescriptiveName());
+ else throw new JS.Exn("couldn't figure out image type from first byte");
+ ret.res = r;
+ cache.put(r, ret);
+ callback.call(ret);
+ } catch (Exception e) {
+ Log.log(Picture.class, e);
+ }
+ return null;
+ }});
} catch (Exception e) {
Log.log(Picture.class, e);
}
+ return null;
}
}
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(); }
+
+ /** read an input stream into a byte array and invoke callback when ready */
+ protected void _inputStreamToByteArray(final InputStream is, final Callback c) {
+ new java.lang.Thread() {
+ public void run() {
+ try {
+ final byte[] b = InputStreamToByteArray.convert(is);
+ Scheduler.add(new Scheduler.Task() { public void perform() { c.call(b); }});
+ } catch (IOException e) {
+ Log.log(Platform.class, e);
+ }
+ }
+ }.start();
+ }
+ public static void inputStreamToByteArray(InputStream is, Callback c) { platform._inputStreamToByteArray(is, c); }
public static void running() { platform._running(); }
public void _running() { new Semaphore().block(); }
public int read(byte[] b, int off, int len) throws IOException {
int ret = super.read(b, off, len);
if (ret != 1) bytesDownloaded += ret;
- Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
+ Scheduler.add(new Scheduler.Task() { public void perform() {
JS.Array args = new JS.Array();
args.addElement(new Integer(bytesDownloaded));
args.addElement(new Integer(is instanceof KnownLength ? ((KnownLength)is).getLength() : 0));
- // FIXME
- // new JS.Thread(callback, callbackScope).resume();
- return null;
+ new JS.Thread(callback, null, args).resume();
} });
return ret;
}
import org.xwt.js.*;
import org.xwt.util.*;
-// FEATURE: reimplement Watcher
/** Implements cooperative multitasking */
public class Scheduler {
- private static Scheduler singleton = new Scheduler();
- public static void run() { singleton.do_run(); }
+ public static final Scheduler singleton = Platform.getScheduler();
protected Scheduler() { }
- public static abstract class Task implements Callback { public abstract Object call(Object o); }
-
- private static Queue runnable = new Queue(50);
+ public static abstract class Task { public abstract void perform(); }
+ protected static Queue runnable = new Queue(50);
public static void add(Task t) { singleton.runnable.append(t); }
- public void do_run() {
+ public void run() {
while(true) {
Task t = (Task)runnable.remove(true);
try {
- t.call(null);
+ t.perform();
+ // FIXME: be smarter about this
for(int i=0; i<Surface.allSurfaces.size(); i++)
((Surface)Surface.allSurfaces.elementAt(i)).render();
} catch (Exception e) {
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 Object call(Object arg) {
+ Scheduler.add(new Scheduler.Task() { public void perform() {
for(int i=0; i<allSurfaces.size(); i++)
((Surface)allSurfaces.elementAt(i)).render();
- return null;
}}); }
public final void setMaximized(boolean b) { if (b != maximized) _setMaximized(maximized = b); }
for (int i=0; children != null && i<children.size(); i++) {
Box kid = new Box();
((Template)children.elementAt(i)).apply(kid, callback, xwt, pis);
- b.put(b.numChildren(), kid);
+
+ // FIXME: can't actually pass a null tailcall here
+ b.put(b.numChildren(), kid, null);
}
if (script != null) new JS.Thread(script, pis).resume();
// Static Data //////////////////////////////////////////////////////////////
+ private static JS.CompiledFunction cascadeHelper = null;
+ private static String cascadeHelperText =
+ "return function(q) { var ret = arguments.doTrap;" +
+ "if (ret != false && !arguments.didCascade) arguments.cascade = q; };";
+ static {
+ try {
+ cascadeHelper = JS.parse("cascadeHelper", 1, new StringReader(cascadeHelperText));
+ cascadeHelper = (JS.CompiledFunction)new JS.Thread(cascadeHelper).resume();
+ } catch (Exception e) {
+ Log.log(Trap.class, e);
+ }
+ }
+
/** List of properties that cannot be trapped */
private static final Hash PROHIBITED = new Hash(120, 3);
if (t.f == f) return;
// actually place the trap
- Trap t = new Trap();
- t.next = (Trap)trapee.get(name, Trap.class);
- trapee.put(name, Trap.class, t);
- t.trapee = trapee;
- t.name = name;
- t.f = f;
+ trapee.put2(name, Trap.class, new Trap(trapee, name.toString(), f, (Trap)trapee.get(name, Trap.class)));
}
*/
static void delTrap(Box trapee, Object name, JS.CompiledFunction f) {
Trap t = (Trap)trapee.get(name, Trap.class);
- if (t.f == f) {
- trapee.put(name, Trap.class, t.next);
- return;
- }
+ if (t.f == f) { trapee.put2(name, Trap.class, t.next); return; }
for(; t.next != null; t = t.next)
- if (t.next.f == f) {
- t.next = t.next.next;
- return;
- }
+ if (t.next.f == f) { t.next = t.next.next; return; }
Log.logJS("warning: tried to remove a trap that had not been placed");
}
// Instance Methods //////////////////////////////////////////////////////////////////////////
- private Trap() { }
+ private Trap(Box b, String n, JS.CompiledFunction f, Trap nx)
+ { trapee = b; name = n; this.f = f; this.next = nx; }
+
+ // Read Traps //////////////////////////////////////////////////////////////////////
public Object perform() {
- try {
- if (f.getNumFormalArgs() > 0) return cascade();
- JS.Thread.current().setTailCall(f, new TrapArgs(this));
- return null;
- } catch (Exception e) {
- Log.log(this, "Exception thrown from within trap: " + e);
- return null;
- }
+ if (f.getNumFormalArgs() > 0) return cascade();
+ else return new JS.TailCall().set(f, new TrapArgs(this));
}
- private static JS.CompiledFunction cascadeHelper = null;
- private static String cascadeHelperText =
- "return function(q) { var ret = arguments.doTrap;" +
- "if (ret != false && !arguments.didCascade) arguments.cascade = q; };";
- static {
- try {
- cascadeHelper = JS.parse("cascadeHelper", 1, new StringReader(cascadeHelperText));
- cascadeHelper = (JS.CompiledFunction)new JS.Thread(cascadeHelper).resume();
- } catch (Exception e) {
- Log.log(Trap.class, e);
- }
+ public Object cascade() {
+ if (next != null) return next.perform();
+ else return trapee.get(name, true);
}
- public void perform(Object val) {
- try {
- if (f.getNumFormalArgs() == 0) cascade(val);
- else JS.Thread.current().setTailCall(cascadeHelper, new TrapArgs(this, val));
- } catch (Exception e) {
- Log.log(this, "Exception thrown from within trap: " + e);
- e.printStackTrace();
- }
+ // Write Traps //////////////////////////////////////////////////////////////////////
+
+ public void perform(Object val, JS.TailCall tail) {
+ if (f.getNumFormalArgs() == 0) cascade(val, tail);
+ else tail.set(cascadeHelper, new TrapArgs(this, val));
}
- public Object cascade() {
- if (next != null) { next.perform(); return null; }
- return trapee.get(name, true);
+ public void cascade(Object val, JS.TailCall tail) {
+ if (next != null) next.perform(val, tail);
+ else trapee.put(name, val, tail, true);
}
- public void cascade(Object val) {
- if (next != null) next.perform(val);
- trapee.put(name, val, true);
- }
+ // Args ///////////////////////////////////////////////////////////////////////////
private static class TrapArgs extends JS.Array {
private Trap t;
public TrapArgs(Trap t) { this.t = t; }
public TrapArgs(Trap t, Object value) { this.t = t; addElement(value); }
- public void put(Object key, Object val) {
- if (key.equals("cascade")) { cascadeHappened = true; t.cascade(val); }
- else super.put(key, val);
+ public void put(Object key, Object val, JS.TailCall tail) {
+ if (key.equals("cascade")) { cascadeHappened = true; t.cascade(val, tail); }
+ else super.put(key, val, (JS.TailCall)tail);
}
public Object get(Object key) {
// common case
if(!(key instanceof String)) return super.get(key);
if (key.equals("trapee")) return t.trapee;
- if (key.equals("doTrap")) { JS.Thread.current().setTailCall(t.f, this); return null; }
+ if (key.equals("doTrap")) return new JS.TailCall().set(t.f, this);
if (key.equals("didCascade")) return cascadeHappened ? Boolean.TRUE : Boolean.FALSE;
if (key.equals("trapname")) return t.name;
if (key.equals("cascade")) return t.cascade();
public void put(Object name, final Object value) {
if (name.equals("thread") && value != null && (value instanceof JS.Callable || value instanceof JS.CompiledFunction)) {
- Scheduler.add(new Scheduler.Task() { public Object call(Object arg) {
+ Scheduler.add(new Scheduler.Task() { public void perform() {
new JS.Thread((CompiledFunction)value).resume();
- return null;
} });
} else if (name.equals("clipboard")) Platform.setClipBoard(value.toString());
else if (name.equals("frame")) Platform.createSurface((Box)value, true, true);
public static void sleep(final int i) {
final JS.Thread jsthread = JS.Thread.current();
final long currentTime = System.currentTimeMillis();
- final Scheduler.Task task = new Scheduler.Task() { public Object call(Object arg) {
- if (System.currentTimeMillis() - currentTime < i) {
- Scheduler.add(this);
- } else {
- jsthread.resume();
- }
- return null;
+ final Scheduler.Task task = new Scheduler.Task() { public void perform() {
+ if (System.currentTimeMillis() - currentTime < i) Scheduler.add(this);
+ else jsthread.resume();
} };
jsthread.pause();
Scheduler.add(task);
try {
if (cx.paused) return null;
cx.bind();
- if (cx.tailCallFunction != null) {
- cx.stack.pop(); // discard actual return value
- cx.pc -= 2;
- cx.stack.push(new CallMarker(cx));
- cx.stack.push(cx.tailCallArgs);
- cx.currentCompiledFunction = cx.tailCallFunction;
- cx.tailCallFunction = null;
- cx.tailCallArgs = null;
- cx.scope = new FunctionScope("unknown", cx.currentCompiledFunction.parentScope);
- cx.pc = 0;
- }
if (cx.currentCompiledFunction == null) return cx.stack.pop();
if (cx.pc >= ((CompiledFunctionImpl)cx.currentCompiledFunction).size) return cx.stack.pop();
String label = null;
throw je("tried to put a value to the " + key + " property on a " + target.getClass().getName());
if (key == null)
throw je("tried to assign \"" + (val==null?"(null)":val.toString()) + "\" to the null key");
- ((JS)target).put(key, val);
+ // FIXME too many allocations here
+ TailCall tail = new TailCall();
+ ((JS)target).put(key, val, tail);
cx.stack.push(val);
+ if (tail.func != null) {
+ cx.stack.push(new CallMarker(cx));
+ cx.stack.push(tail.args);
+ cx.currentCompiledFunction = tail.func;
+ cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", tail.func.parentScope);
+ cx.pc = -1;
+ break;
+ }
break;
}
ret = Internal.getFromPrimitive(o,v);
else if (o instanceof JS) {
ret = ((JS)o).get(v);
+ if (ret instanceof JS.TailCall) {
+ cx.stack.push(new CallMarker(cx));
+ cx.stack.push(((JS.TailCall)ret).args);
+ cx.currentCompiledFunction = ((JS.TailCall)ret).func;
+ cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)ret).func.parentScope);
+ cx.pc = -1;
+ break;
+ }
} else
throw je("tried to get property " + v + " from a " + o.getClass().getName());
cx.stack.push(ret);
break;
} else {
o = ((JS)o).get(method);
+ if (o instanceof JS.TailCall) {
+ cx.stack.push(new CallMarker(cx));
+ cx.stack.push(((JS.TailCall)o).args);
+ cx.currentCompiledFunction = ((JS.TailCall)o).func;
+ cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)o).func.parentScope);
+ cx.pc = -1;
+ break;
+ }
}
} else {
throw new JS.Exn("Tried to call a method on an object that isn't a JS object: " + o);
} else {
ret = ((JS.Callable)o).call(arguments);
+ if (ret instanceof JS.TailCall) {
+ cx.stack.push(new CallMarker(cx));
+ cx.stack.push(((JS.TailCall)ret).args);
+ cx.currentCompiledFunction = ((JS.TailCall)ret).func;
+ cx.scope = new CompiledFunctionImpl.FunctionScope("unknown", ((JS.TailCall)ret).func.parentScope);
+ cx.pc = -1;
+ break;
+ }
}
cx.stack.push(ret);
break;