new PixelBuffer API (mainly tons of renames)
[org.ibex.core.git] / src / org / ibex / core / Box.java
index 29cd9bc..16bae85 100644 (file)
@@ -1,5 +1,8 @@
+// Copyright 2000-2005 the Contributors, as shown in the revision logs.
+// Licensed under the GNU General Public License version 2 ("the License").
+// You may not use this file except in compliance with the License.
+
 // FIXME
-// Copyright 2004 Adam Megacz, see the COPYING file for licensing [GPL]
 package org.ibex.core;
 
 // FIXME: are traps on x/y meaningful?
@@ -38,7 +41,9 @@ import org.ibex.graphics.*;
  *  trigger a 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 implements Task {
+public final class Box extends JS.Obj implements Callable {
+
+    private static final JS.Method METHOD = new JS.Method();
 
     // Macros //////////////////////////////////////////////////////////////////////
 
@@ -46,18 +51,20 @@ public final class Box extends JSScope implements Task {
     final void RECONSTRAIN() { for(Box b2 = this; b2 != null && !b2.test(RECONSTRAIN); b2 = b2.parent) b2.set(RECONSTRAIN); }
     final void REPACK() { for(Box b2 = this; b2 != null && !b2.test(REPACK); b2 = b2.parent) b2.set(REPACK); }
 
-    //#define CHECKSET_SHORT(prop) short nu = (short)toInt(value); if (nu == prop) break; prop = nu;
-    //#define CHECKSET_INT(prop) int nu = toInt(value); if (nu == prop) break; prop = nu;
-    //#define CHECKSET_FLAG(flag) boolean nu = toBoolean(value); if (nu == test(flag)) break; if (nu) set(flag); else clear(flag);
-    //#define CHECKSET_BOOLEAN(prop) boolean nu = toBoolean(value); if (nu == prop) break; prop = nu;
-    //#define CHECKSET_STRING(prop) if ((value==null&&prop==null)||(value!=null&&JS.toString(value).equals(prop))) break; prop=JS.toString(value);
-
-    public Box() { super(null); }
+    //#define CHECKSET_SHORT(prop) short nu = (short)JSU.toInt(value); if (nu == prop) break; prop = nu;
+    //#define CHECKSET_INT(prop) int nu = JSU.toInt(value); if (nu == prop) break; prop = nu;
+    //#define CHECKSET_FLAG(flag) boolean nu = JSU.toBoolean(value); if (nu == test(flag)) break; if (nu) set(flag); else clear(flag);
+    //#define CHECKSET_BOOLEAJSU.N(prop) boolean nu = JSU.toBoolean(value); if (nu == prop) break; prop = nu;
+    //#define CHECKSET_STRING(prop) if ((value==null&&prop==null)||(value!=null&&JSU.toString(value).equals(prop))) break; prop=JSU.toString(value);
 
     // FIXME memory leak
-    static Hash boxToCursor = new Hash(500, 3);
+    static Basket.Map boxToCursor = new Basket.HashMap(500, 3);
 
-    static final Font DEFAULT_FONT = Font.getFont((Stream)Main.builtin.get(JS.S("fonts/vera/Vera.ttf")), 10);
+    static final Font DEFAULT_FONT;
+    static {
+        try { DEFAULT_FONT = Font.getFont((JS)Main.builtin.get(JSU.S("fonts/vera/Vera.ttf")), 10); }
+        catch(JSExn e) { throw new Error("Error loading default font: " + e); }
+    }
 
 
     // Flags //////////////////////////////////////////////////////////////////////
@@ -95,6 +102,8 @@ public final class Box extends JSScope implements Task {
     Box redirect = this;
     int flags = VISIBLE | PACKED | REPACK | RECONSTRAIN | REPLACE | FIXED | STOP_UPWARD_PROPAGATION | CLIP | MOVED;
 
+    private BalancedTree bt;
+    
     private String text = null;
     private Font font = DEFAULT_FONT; 
     private Picture texture = null;
@@ -109,6 +118,10 @@ public final class Box extends JSScope implements Task {
     public int maxwidth = Integer.MAX_VALUE;
     public int minheight = 0;
     public int maxheight = Integer.MAX_VALUE;
+    public int minwidth() { return minwidth; }
+    public int minheight() { return minheight; }
+    public int maxwidth() { return maxwidth; }
+    public int maxheight() { return maxheight; }
     private short rows = 1;
     private short cols = 0;
     private short rowspan = 1;
@@ -138,13 +151,14 @@ public final class Box extends JSScope implements Task {
     public final int fontSize() { return font == null ? DEFAULT_FONT.pointsize : font.pointsize; }
 
     /** invoked when a resource needed to render ourselves finishes loading */
-    public void perform() throws JSExn {
-        if (texture == null) { Log.warn(Box.class, "perform() called with null texture"); return; }
+    public Object run(Object o) throws JSExn {
+        if (texture == null) { Log.warn(Box.class, "perform() called with null texture"); return null; }
         if (texture.isLoaded) {
             setWidth(max(texture.width, minwidth), maxwidth); 
             setHeight(max(texture.height, minheight), maxheight); 
             dirty(); }
         else { JS res = texture.stream; texture = null; throw new JSExn("image not found: "+res.unclone()); }
+        return null;
     }
 
     // FEATURE: use cx2/cy2 format
@@ -230,11 +244,12 @@ public final class Box extends JSScope implements Task {
         set(REPLACE); // FIXME: be smarter / more incremental
     }
     
-    private final static JS SIZECHANGE = JS.S("SizeChange");
+    private final static JS SIZECHANGE = JSU.S("SizeChange");
     
     void resize(int x, int y, int width, int height) {
         if (x == this.x && y == this.y && width == this.width && height == this.height) return;
-        boolean sizechange = (this.width != width || this.height != height) && hasTrap(SIZECHANGE);
+        boolean sizechange = (this.width != width || this.height != height);
+        try { sizechange = sizechange && getTrap(SIZECHANGE) != null; } catch (JSExn e) {}
         int thisx = parent == null ? 0 : this.x;
         int thisy = parent == null ? 0 : this.y;
         Box who = (parent == null ? this : parent);
@@ -250,7 +265,7 @@ public final class Box extends JSScope implements Task {
             this.width = width; this.height = height; this.x = x; this.y = y;
             dirty();
         }
-        if (sizechange) putAndTriggerTrapsAndCatchExceptions(SIZECHANGE, T);
+        if (sizechange) putAndTriggerTrapsAndCatchExceptions(SIZECHANGE, JSU.T);
     }
 
     private float targetColumnSize = (float)0.0;
@@ -426,7 +441,7 @@ public final class Box extends JSScope implements Task {
             font.rasterizeGlyphs(text, buf, strokecolor, text_x, text_y, cx1, cy1, cx2, cy2);
         }
 
-        if (path != null) path.realize(Affine.translate(globalx, globaly)).stroke(buf, 1, strokecolor);
+        if (path != null) new Polygon(path, Affine.translate(globalx, globaly)).stroke(buf, strokecolor);
 
         for(Box b = getChild(0); b != null; b = b.nextSibling())
             b.render(globalx, globaly, cx1, cy1, cx2, cy2, buf, null);
@@ -436,101 +451,100 @@ public final class Box extends JSScope implements Task {
     // Methods to implement org.ibex.js.JS //////////////////////////////////////
 
   
-    public JS callMethod(JS method, JS a0, JS a1, JS a2, JS[] rest, int nargs) throws JSExn {
-        switch (nargs) {
+    public JS call(JS method, JS[] args) throws JSExn {
+        switch (args.length) {
             case 1: {
-                //#switch(JS.toString(method))
+                //#switch(JSU.toString(method))
                 case "indexof":
-                    Box b = (Box)a0;
+                    Box b = (Box)args[0];
                     if (b.parent != this)
                         return (redirect == null || redirect == this) ?
-                            N(-1) :
-                            redirect.callMethod(method, a0, a1, a2, rest, nargs);
-                    return N(b.getIndexInParent());
+                            JSU.N(-1) : redirect.call(method, args);
+                    return JSU.N(b.getIndexInParent());
 
                 case "distanceto":
-                    Box b = (Box)a0;
-                    JS ret = new JS.O();
-                    ret.put(JS.S("x"), N(b.localToGlobalX(0) - localToGlobalX(0)));
-                    ret.put(JS.S("y"), N(b.localToGlobalY(0) - localToGlobalY(0)));
+                    Box b = (Box)args[0];
+                    JS ret = new JS.Obj();
+                    ret.put(JSU.S("x"), JSU.N(b.localToGlobalX(0) - localToGlobalX(0)));
+                    ret.put(JSU.S("y"), JSU.N(b.localToGlobalY(0) - localToGlobalY(0)));
                     return ret;
 
                 //#end
             }
         }
-        return super.callMethod(method, a0, a1, a2, rest, nargs);
+        return super.call(method, args);
     }
 
     public JS get(JS name) throws JSExn {
-        if (JS.isInt(name))
-            return redirect == null ? null : redirect == this ? getChild(JS.toInt(name)) : redirect.get(name);
+        if (JSU.isInt(name))
+            return redirect == null ? null : redirect == this ? getChild(JSU.toInt(name)) : redirect.get(name);
 
-        //#switch(JS.toString(name))
+        //#switch(JSU.toString(name))
         case "surface": return parent == null ? null : parent.getAndTriggerTraps(name);
         case "indexof": return METHOD;
         case "distanceto": return METHOD;
-        case "text": return JS.S(text);
+        case "text": return JSU.S(text);
         case "path": {
-            if (path != null) return JS.S(path.toString());
+            if (path != null) return JSU.S(path.toString());
             if (text == null) return null;
             if (font == null) return null;
             String ret = "";
             for(int i=0; i<text.length(); i++) ret += font.glyphs[text.charAt(i)].path;
-            return JS.S(ret);
+            return JSU.S(ret);
         }
-        case "fill": return JS.S(Color.colorToString(fillcolor));
-        case "strokecolor": return JS.S(Color.colorToString(strokecolor));
-        case "textcolor": return JS.S(Color.colorToString(strokecolor));
+        case "fill": return JSU.S(Color.colorToString(fillcolor));
+        case "strokecolor": return JSU.S(Color.colorToString(strokecolor));
+        case "textcolor": return JSU.S(Color.colorToString(strokecolor));
         case "font": return font == null ? null : font.stream;
-        case "fontsize": return font == null ? N(10) : N(font.pointsize);
-        case "strokewidth": return N(strokewidth);
-        case "align": return JS.S(alignToString());
+        case "fontsize": return font == null ? JSU.N(10) : JSU.N(font.pointsize);
+        case "strokewidth": return JSU.N(strokewidth);
+        case "align": return JSU.S(alignToString());
         case "thisbox": return this;
-        case "shrink": return B(test(HSHRINK) || test(VSHRINK));
-        case "hshrink": return B(test(HSHRINK));
-        case "vshrink": return B(test(VSHRINK));
-        case "aspect": return N(aspect);
-        case "x": return (parent == null || !test(VISIBLE)) ? N(0) : test(PACKED) ? N(x) : N(ax);
-        case "y": return (parent == null || !test(VISIBLE)) ? N(0) : test(PACKED) ? N(y) : N(ay);
-        case "cols": return test(FIXED) == COLS ? N(cols) : N(0);
-        case "rows": return test(FIXED) == ROWS ? N(rows) : N(0);
-        case "colspan": return N(colspan);
-        case "rowspan": return N(rowspan);
-        case "width": getRoot().reflow(); return N(width);
-        case "height": getRoot().reflow(); return N(height);
-        case "minwidth": return N(minwidth);
-        case "maxwidth": return N(maxwidth);
-        case "minheight": return N(minheight);
-        case "maxheight": return N(maxheight);
-        case "clip": return B(test(CLIP));
-        case "visible": return B(test(VISIBLE) && (parent == null || (parent.get(JS.S("visible")) == T)));
-        case "packed": return B(test(PACKED));
-        case "globalx": return N(localToGlobalX(0));
-        case "globaly": return N(localToGlobalY(0));
-        case "cursor": return test(CURSOR) ? JS.S((String)boxToCursor.get(this)) : null;
+        case "shrink": return JSU.B(test(HSHRINK) || test(VSHRINK));
+        case "hshrink": return JSU.B(test(HSHRINK));
+        case "vshrink": return JSU.B(test(VSHRINK));
+        case "aspect": return JSU.N(aspect);
+        case "x": return (parent == null || !test(VISIBLE)) ? JSU.N(0) : test(PACKED) ? JSU.N(x) : JSU.N(ax);
+        case "y": return (parent == null || !test(VISIBLE)) ? JSU.N(0) : test(PACKED) ? JSU.N(y) : JSU.N(ay);
+        case "cols": return test(FIXED) == COLS ? JSU.N(cols) : JSU.N(0);
+        case "rows": return test(FIXED) == ROWS ? JSU.N(rows) : JSU.N(0);
+        case "colspan": return JSU.N(colspan);
+        case "rowspan": return JSU.N(rowspan);
+        case "width": getRoot().reflow(); return JSU.N(width);
+        case "height": getRoot().reflow(); return JSU.N(height);
+        case "minwidth": return JSU.N(minwidth);
+        case "maxwidth": return JSU.N(maxwidth);
+        case "minheight": return JSU.N(minheight);
+        case "maxheight": return JSU.N(maxheight);
+        case "clip": return JSU.B(test(CLIP));
+        case "visible": return JSU.B(test(VISIBLE) && (parent == null || (parent.get(JSU.S("visible")) == JSU.T)));
+        case "packed": return JSU.B(test(PACKED));
+        case "globalx": return JSU.N(localToGlobalX(0));
+        case "globaly": return JSU.N(localToGlobalY(0));
+        case "cursor": return test(CURSOR) ? JSU.S((String)boxToCursor.get(this)) : null;
         case "mouse":
             if (getSurface() == null) return null;
             if (getSurface()._mousex == Integer.MAX_VALUE)
                 throw new JSExn("you cannot read from the box.mouse property in background thread context");
             return new Mouse();
-        case "numchildren": return redirect == null ? N(0) : redirect == this ? N(treeSize()) : redirect.get(JS.S("numchildren"));
-        case "redirect": return redirect == null ? null : redirect == this ? T : redirect.get(JS.S("redirect"));
-        case "Minimized": if (parent == null && getSurface() != null) return B(getSurface().minimized);
+        case "numchildren": return redirect == null ? JSU.N(0) : redirect == this ? JSU.N(treeSize()) : redirect.get(JSU.S("numchildren"));
+        case "redirect": return redirect == null ? null : redirect == this ? JSU.T : redirect.get(JSU.S("redirect"));
+        case "Minimized": if (parent == null && getSurface() != null) return JSU.B(getSurface().minimized);
         default: return super.get(name);
         //#end
         throw new Error("unreachable"); // unreachable
     }
 
-    private class Mouse extends JS implements JS.Cloneable {
+    private class Mouse extends JS.Immutable implements JS.Cloneable {
         public JS get(JS key) throws JSExn {
-            //#switch(JS.toString(key))
-            case "x": return N(globalToLocalX(getSurface()._mousex));
-            case "y": return N(globalToLocalY(getSurface()._mousey));
+            //#switch(JSU.toString(key))
+            case "x": return JSU.N(globalToLocalX(getSurface()._mousex));
+            case "y": return JSU.N(globalToLocalY(getSurface()._mousey));
 
             // this might not get recomputed if we change mousex/mousey...
-            case "inside": return B(test(MOUSEINSIDE));
+            case "inside": return JSU.B(test(MOUSEINSIDE));
             //#end
-            return null;
+            return super.get(key);
         }
     }
 
@@ -550,29 +564,29 @@ public final class Box extends JSScope implements Task {
     //#end
 
     public void put(JS name, JS value) throws JSExn {
-        if (JS.isInt(name)) { put(JS.toInt(name), value); return; }
-        //#switch(JS.toString(name))
+        if (JSU.isInt(name)) { put(JSU.toInt(name), value); return; }
+        //#switch(JSU.toString(name))
         case "thisbox":     if (value == null) removeSelf();
-        case "text":        { String s = value == null ?  "" : JS.toString(value); CHECKSET_STRING(text); RECONSTRAIN(); dirty(); }
-        case "strokecolor": value = N(Color.stringToColor(JS.toString(value))); CHECKSET_INT(strokecolor); dirty();
-        case "textcolor":   value = N(Color.stringToColor(JS.toString(value))); CHECKSET_INT(strokecolor); dirty();
+        case "text":        { String s = value == null ?  "" : JSU.toString(value); CHECKSET_STRING(text); RECONSTRAIN(); dirty(); }
+        case "strokecolor": value = JSU.N(Color.stringToColor(JSU.toString(value))); CHECKSET_INT(strokecolor); dirty();
+        case "textcolor":   value = JSU.N(Color.stringToColor(JSU.toString(value))); CHECKSET_INT(strokecolor); dirty();
         case "strokewidth": CHECKSET_SHORT(strokewidth); dirty();
         case "shrink":      CHECKSET_FLAG(HSHRINK | VSHRINK); RECONSTRAIN();
         case "hshrink":     CHECKSET_FLAG(HSHRINK); RECONSTRAIN();
         case "vshrink":     CHECKSET_FLAG(VSHRINK); RECONSTRAIN();
-        case "path":        path = Path.parse(toString(value)); RECONSTRAIN(); dirty();
-        case "width":       setWidth(toInt(value), toInt(value));
-        case "height":      setHeight(toInt(value), toInt(value));
-        case "maxwidth":    setWidth(minwidth, toInt(value));
-        case "minwidth":    setWidth(toInt(value), maxwidth);
-        case "maxheight":   setHeight(minheight, toInt(value));
-        case "minheight":   setHeight(toInt(value), maxheight);
-        case "colspan":     if (toInt(value) > 0) { CHECKSET_SHORT(colspan); if (parent != null) parent.REPACK(); }
-        case "rowspan":     if (toInt(value) > 0) { CHECKSET_SHORT(rowspan); if (parent != null) parent.REPACK(); }
+        case "path":        path = new Path(JSU.toString(value)); RECONSTRAIN(); dirty();
+        case "width":       setWidth(JSU.toInt(value), JSU.toInt(value));
+        case "height":      setHeight(JSU.toInt(value), JSU.toInt(value));
+        case "maxwidth":    setWidth(minwidth, JSU.toInt(value));
+        case "minwidth":    setWidth(JSU.toInt(value), maxwidth);
+        case "maxheight":   setHeight(minheight, JSU.toInt(value));
+        case "minheight":   setHeight(JSU.toInt(value), maxheight);
+        case "colspan":     if (JSU.toInt(value) > 0) { CHECKSET_SHORT(colspan); if (parent != null) parent.REPACK(); }
+        case "rowspan":     if (JSU.toInt(value) > 0) { CHECKSET_SHORT(rowspan); if (parent != null) parent.REPACK(); }
         case "visible":     CHECKSET_FLAG(VISIBLE); RECONSTRAIN(); dirty();
         case "packed":      CHECKSET_FLAG(PACKED); if (parent != null) { parent.REPACK(); } else { REPACK(); }
-        case "align":       clear(ALIGNS); setAlign(value == null ? "center" : JS.toString(value)); REPLACE();
-        case "cursor":      setCursor(JS.toString(value));
+        case "align":       clear(ALIGNS); setAlign(value); REPLACE();
+        case "cursor":      setCursor(JSU.toString(value));
         case "fill":        setFill(value);
         case "clip":        CHECKSET_FLAG(CLIP); if (parent == null) dirty(); else parent.dirty();
         case "rows": CHECKSET_SHORT(rows); if (rows==0){set(FIXED, COLS);if(cols==0)cols=1;} else set(FIXED, ROWS); REPACK();
@@ -580,19 +594,19 @@ public final class Box extends JSScope implements Task {
 
         // FIXME: remove
         case "mouse":
-            int mousex = toInt(((JS)value).get(JS.S("x")));
-            int mousey = toInt(((JS)value).get(JS.S("y")));
+            int mousex = JSU.toInt(((JS)value).get(JSU.S("x")));
+            int mousey = JSU.toInt(((JS)value).get(JSU.S("y")));
             getSurface()._mousex = localToGlobalX(mousex);
             getSurface()._mousey = localToGlobalY(mousey);
 
-        case "Minimized": if (parent == null && getSurface() != null) getSurface().minimized = toBoolean(value);  // FEATURE
-        case "Maximized": if (parent == null && getSurface() != null) getSurface().maximized = toBoolean(value);  // FEATURE
+        case "Minimized": if (parent == null && getSurface() != null) getSurface().minimized = JSU.toBoolean(value);  // FEATURE
+        case "Maximized": if (parent == null && getSurface() != null) getSurface().maximized = JSU.toBoolean(value);  // FEATURE
         case "Close":     if (parent == null && getSurface() != null) getSurface().dispose(true);
         case "redirect":
             for(Box cur = (Box)value; cur != null || cur == redirect; cur = cur.parent)
                 if (cur == redirect) { redirect = (Box)value; return; }
-            JS.error("redirect can only be set to a descendant of its current value");
-        case "fontsize": font = Font.getFont(font == null ? null : font.stream, toInt(value)); RECONSTRAIN(); dirty();
+            JSU.error("redirect can only be set to a descendant of its current value");
+        case "fontsize": font = Font.getFont(font == null ? null : font.stream, JSU.toInt(value)); RECONSTRAIN(); dirty();
         case "font":
             if(!(value instanceof Stream)) throw new JSExn("You can only put streams to the font property");
             //FIXME: if (font == value) return;  // FIXME: unclone()
@@ -613,7 +627,7 @@ public final class Box extends JSScope implements Task {
             CHECKSET_INT(ay);
             REPLACE();
         }
-        case "titlebar": if (getSurface()!=null) getSurface().setTitleBarText(toString(value)); super.put(name,value);
+        case "titlebar": if (getSurface()!=null) getSurface().setTitleBarText(JSU.toString(value)); super.put(name,value);
         // FIXME: icon
 
         case "Press1":        if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
@@ -632,9 +646,9 @@ public final class Box extends JSScope implements Task {
         case "KeyReleased":   if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
         case "Move":          if (!test(STOP_UPWARD_PROPAGATION) && parent != null) parent.putAndTriggerTraps(name, value);
         case "HScroll":       if (!test(STOP_UPWARD_PROPAGATION) && parent != null)
-            parent.putAndTriggerTraps(name, N(JS.toFloat(value) * ((float)parent.fontSize()) / ((float)fontSize())));
+            parent.putAndTriggerTraps(name, JSU.N(JSU.toFloat(value) * ((float)parent.fontSize()) / ((float)fontSize())));
         case "VScroll":       if (!test(STOP_UPWARD_PROPAGATION) && parent != null)
-            parent.putAndTriggerTraps(name, N(JS.toFloat(value) * ((float)parent.fontSize()) / ((float)fontSize())));
+            parent.putAndTriggerTraps(name, JSU.N(JSU.toFloat(value) * ((float)parent.fontSize()) / ((float)fontSize())));
 
         case "_Move":         propagateDownward(name, value, false);
         case "_Press1":       propagateDownward(name, value, false);
@@ -678,9 +692,9 @@ public final class Box extends JSScope implements Task {
         }
     }
 
-    private void setAlign(String value) {
+    private void setAlign(JS value) throws JSExn {
         clear(ALIGNS);
-        //#switch(value)
+        //#switch(JSU.toString(value))
         case "topleft": set(ALIGN_TOP | ALIGN_LEFT);
         case "bottomleft": set(ALIGN_BOTTOM | ALIGN_LEFT);
         case "topright": set(ALIGN_TOP | ALIGN_RIGHT);
@@ -689,7 +703,7 @@ public final class Box extends JSScope implements Task {
         case "bottom": set(ALIGN_BOTTOM);
         case "left": set(ALIGN_LEFT);
         case "right": set(ALIGN_RIGHT);
-        default: JS.log("invalid alignment \"" + value + "\"");
+        default: JSU.log("invalid alignment \"" + JSU.str(value) + "\"");
         //#end
     }
     
@@ -710,8 +724,8 @@ public final class Box extends JSScope implements Task {
             if (texture == null && fillcolor == 0) return;
             texture = null;
             fillcolor = 0;
-        } else if (JS.isString(value)) {
-            int newfillcolor = Color.stringToColor(JS.toString(value));
+        } else if (JSU.isString(value)) {
+            int newfillcolor = Color.stringToColor(JSU.toString(value));
             if (newfillcolor == fillcolor) return;
             fillcolor = newfillcolor;
             texture = null;
@@ -720,7 +734,7 @@ public final class Box extends JSScope implements Task {
             if (texture == newtex) return;
             texture = newtex;
             fillcolor = 0;
-            if (texture != null && texture.isLoaded) perform();
+            if (texture != null && texture.isLoaded) run(null);
         }
         dirty();
     }
@@ -731,7 +745,7 @@ public final class Box extends JSScope implements Task {
      */
     private void propagateDownward(JS name_, JS value, boolean obscured) throws JSExn {
 
-        String name = JS.toString(name_);
+        String name = JSU.toString(name_);
         if (getSurface() == null) return;
         int x = globalToLocalX(getSurface()._mousex);
         int y = globalToLocalY(getSurface()._mousey);
@@ -739,12 +753,12 @@ public final class Box extends JSScope implements Task {
         boolean isinside = test(VISIBLE) && inside(x, y) && !obscured;
         if (!wasinside && isinside) {
             set(MOUSEINSIDE);
-            putAndTriggerTrapsAndCatchExceptions(JS.S("Enter"), T);
+            putAndTriggerTrapsAndCatchExceptions(JSU.S("Enter"), JSU.T);
         }
         if (isinside && test(CURSOR)) getSurface().cursor = (String)boxToCursor.get(this);
         if (wasinside && !isinside) {
             clear(MOUSEINSIDE);
-            putAndTriggerTrapsAndCatchExceptions(JS.S("Leave"), T);
+            putAndTriggerTrapsAndCatchExceptions(JSU.S("Leave"), JSU.T);
         }
 
         boolean found = false;
@@ -753,7 +767,7 @@ public final class Box extends JSScope implements Task {
                 boolean save_stop = child.test(STOP_UPWARD_PROPAGATION);
                 JS value2 = value;
                 if (name.equals("_HScroll") || name.equals("_VScroll"))
-                    value2 = N(JS.toFloat(value) * ((float)child.fontSize()) / (float)fontSize());
+                    value2 = JSU.N(JSU.toFloat(value) * ((float)child.fontSize()) / (float)fontSize());
                 if (obscured || !child.inside(x - child.x, y - child.y)) {
                     child.propagateDownward(name_, value2, true);
                 } else try {
@@ -772,7 +786,7 @@ public final class Box extends JSScope implements Task {
         if (!obscured && !found)
             if ("_Move".equals(name) || name.startsWith("_Release") || wasinside)
                 if (name != null)
-                    putAndTriggerTrapsAndCatchExceptions(JS.S(name.substring(1)), value);
+                    putAndTriggerTrapsAndCatchExceptions(JSU.S(name.substring(1)), value);
     }
 
     /** figures out what box in this subtree of the Box owns the pixel at x,y relitave to the Surface */
@@ -863,34 +877,34 @@ public final class Box extends JSScope implements Task {
         deleteNode(i);
         b.parent = null;
         REPACK();
-        putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), b);
+        putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), b);
     }
     
     public void put(int i, JS value) throws JSExn {
         if (i < 0) return;
             
         if (value != null && !(value instanceof Box)) {
-            if (Log.on) JS.warn("attempt to set a numerical property on a box to a non-box");
+            if (Log.on) JSU.warn("attempt to set a numerical property on a box to a non-box");
             return;
         }
 
         if (redirect == null) {
-            if (value == null) putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), getChild(i));
-            else JS.warn("attempt to add/remove children to/from a node with a null redirect");
+            if (value == null) putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), getChild(i));
+            else JSU.warn("attempt to add/remove children to/from a node with a null redirect");
 
         } else if (redirect != this) {
-            if (value != null) putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), value);
+            if (value != null) putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), value);
             redirect.put(i, value);
             if (value == null) {
-                Box b = (Box)redirect.get(JS.N(i));
-                if (b != null) putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), b);
+                Box b = (Box)redirect.get(JSU.N(i));
+                if (b != null) putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), b);
             }
 
         } else if (value == null) {
             if (i < 0 || i > treeSize()) return;
             Box b = getChild(i);
             removeChild(i);
-            putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), b);
+            putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), b);
 
         } else {
             Box b = (Box)value;
@@ -898,14 +912,14 @@ public final class Box extends JSScope implements Task {
             // check if box being moved is currently target of a redirect
             for(Box cur = b.parent; cur != null; cur = cur.parent)
                 if (cur.redirect == b) {
-                    if (Log.on) JS.warn("attempt to move a box that is the target of a redirect");
+                    if (Log.on) JSU.warn("attempt to move a box that is the target of a redirect");
                     return;
                 }
 
             // check for recursive ancestor violation
             for(Box cur = this; cur != null; cur = cur.parent)
                 if (cur == b) {
-                    if (Log.on) JS.warn("attempt to make a node a parent of its own ancestor");
+                    if (Log.on) JSU.warn("attempt to make a node a parent of its own ancestor");
                     if (Log.on) Log.info(this, "box == " + this + "  ancestor == " + b);
                     return;
                 }
@@ -919,7 +933,7 @@ public final class Box extends JSScope implements Task {
             REPACK();
             
             b.dirty(); 
-            putAndTriggerTrapsAndCatchExceptions(JS.S("ChildChange"), b);
+            putAndTriggerTrapsAndCatchExceptions(JSU.S("ChildChange"), b);
         }
     }
     
@@ -927,11 +941,22 @@ public final class Box extends JSScope implements Task {
         try {
             putAndTriggerTraps(name, val);
         } catch (JSExn e) {
-            JS.log("caught js exception while putting to trap \""+ JS.debugToString(name)+"\"");
-            JS.log(e);
+            JSU.log("caught js exception while putting to trap \""+ JSU.str(name)+"\"");
+            JSU.log(e);
         } catch (Exception e) {
-            JS.log("caught exception while putting to trap \""+ JS.debugToString(name)+"\"");
-            JS.log(e);
+            JSU.log("caught exception while putting to trap \""+ JSU.str(name)+"\"");
+            JSU.log(e);
         }
     }
+    
+    // BalancedTree functions
+    private void insertNode(int p, Box b) {
+        if(bt == null) bt = new BalancedTree();
+        bt.insertNode(p,b);
+    }
+    
+    private int treeSize() { return bt == null ? 0 : bt.treeSize(); }
+    private int indexNode(Box b) { return bt == null ? -1 : bt.indexNode(b); }
+    private void deleteNode(int p) { bt.deleteNode(p); }
+    private Box getNode(int p) { return (Box)bt.getNode(p); }
 }