2002/10/30 22:01:04
[org.ibex.core.git] / src / org / xwt / SpecialBoxProperty.java
1 // Copyright 2002 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt;
3
4 import org.mozilla.javascript.*;
5 import org.xwt.util.*;
6 import java.util.*;
7 import java.net.*;
8 import java.text.*;
9
10 /** 
11  *  A helper class for properties of Box which require special
12  *  handling.
13  *
14  *  To avoid excessive use of String.equals(), the Box.get() and
15  *  Box.put() methods employ a Hash keyed on property names that
16  *  require special handling. The value stored in the Hash is an
17  *  instance of an anonymous subclass of SpecialBoxProperty, which knows
18  *  how to handle get()s and put()s for that property name. There
19  *  should be one anonymous subclass of SpecialBoxProperty for each
20  *  specially-handled property on Box.
21  */
22 class SpecialBoxProperty {
23
24     SpecialBoxProperty() { }
25     private static final int NUMINTS = Box.NUMINTS;
26     private static final int dmax = Box.dmax;
27     private static final int dmin = Box.dmin;
28     private static final int cmin = Box.cmin;
29     private static final int abs = Box.abs;
30     private static final int pos = Box.pos;
31     private static final int size = Box.size;
32     private static final int oldpos = Box.oldpos;
33     private static final int oldsize = Box.oldsize;
34     private static final int pad = Box.pad;
35
36     /** stores instances of SpecialBoxProperty; keyed on property name */
37     static Hash specialBoxProperties = new Hash(200, 3);
38
39     /** this method defines the behavior when the property is get()ed from b */
40     Object get(Box b) { return null; }
41
42     /** this method defines the behavior when the property is put() to b */
43     void put(Box b, Object value) { }
44
45     /** this method defines the behavior when the property is put() to b, allows a single SpecialBoxProperty to serve multiple properties */
46     void put(String name, Box b, Object value) { put(b, value); }
47
48     // 'ye olde IBM CGA colours', as defined in the XWT reference
49     static final int black = 0xFF000000;
50     static final int blue = 0xFF000088;
51     static final int green = 0xFF008800;
52     static final int cyan = 0xFF008888;
53     static final int red = 0xFF880000;
54     static final int magenta = 0xFF880088;
55     static final int brown = 0xFF888800;
56     static final int lightGray = 0xFFBDBEBD;
57     static final int darkGray = 0xFF242424;
58     static final int brightBlue = 0xFF0000FF;
59     static final int brightGreen = 0xFF00FF00;
60     static final int brightCyan = 0xFF00FFFF;
61     static final int brightRed = 0xFFFF0000;
62     static final int pink = 0xFFFF00FF;
63     static final int yellow = 0xFFFFFF00;
64     static final int white = 0xFFFFFFFF;
65
66     static {
67
68         specialBoxProperties.put("color", new SpecialBoxProperty() {
69                 public Object get(Box b) {
70                     if ((b.color & 0xFF000000) == 0) return null;
71                     String red = Integer.toHexString((b.color & 0x00FF0000) >> 16);
72                     String green = Integer.toHexString((b.color & 0x0000FF00) >> 8);
73                     String blue = Integer.toHexString(b.color & 0x000000FF);
74                     if (red.length() < 2) red = "0" + red;
75                     if (blue.length() < 2) blue = "0" + blue;
76                     if (green.length() < 2) green = "0" + green;
77                     return "#" + red + green + blue;
78                 }
79                 public void put(Box b, Object value) {
80                     int newcolor = b.color;
81                     String s = value == null ? null : value.toString();
82                     if (value == null) newcolor = 0x00000000;
83                     else if (s.length() > 0 && s.charAt(0) == '#')
84                         newcolor = 0xFF000000 |
85                             (Integer.parseInt(s.substring(1, 3), 16) << 16) |
86                             (Integer.parseInt(s.substring(3, 5), 16) << 8) |
87                             Integer.parseInt(s.substring(5, 7), 16);
88                     else if (s.equals("black")) newcolor = black;
89                     else if (s.equals("blue")) newcolor = blue;
90                     else if (s.equals("green")) newcolor = green;
91                     else if (s.equals("cyan")) newcolor = cyan;
92                     else if (s.equals("red")) newcolor = red;
93                     else if (s.equals("magenta")) newcolor = magenta;
94                     else if (s.equals("brown")) newcolor = brown;
95                     else if (s.equals("lightGray")) newcolor = lightGray;
96                     else if (s.equals("darkGray")) newcolor = darkGray;
97                     else if (s.equals("brightBlue")) newcolor = brightBlue;
98                     else if (s.equals("brightGreen")) newcolor = brightGreen;
99                     else if (s.equals("brightCyan")) newcolor = brightCyan;
100                     else if (s.equals("brightRed")) newcolor = brightRed;
101                     else if (s.equals("pink")) newcolor = pink;
102                     else if (s.equals("yellow")) newcolor = yellow;
103                     else if (s.equals("white")) newcolor = white;
104                     else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " +
105                                              Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
106                     if (newcolor == b.color) return;
107                     b.color = newcolor;
108                     b.dirty();
109                 } });
110         
111         
112         specialBoxProperties.put("textcolor", new SpecialBoxProperty() {
113                 public Object get(Box b) {
114                     if ((b.textcolor & 0xFF000000) == 0) return null;
115                     String red = Integer.toHexString((b.textcolor & 0x00FF0000) >> 16);
116                     String green = Integer.toHexString((b.textcolor & 0x0000FF00) >> 8);
117                     String blue = Integer.toHexString(b.textcolor & 0x000000FF);
118                     if (red.length() < 2) red = "0" + red;
119                     if (blue.length() < 2) blue = "0" + blue;
120                     if (green.length() < 2) green = "0" + green;
121                     return "#" + red + green + blue;
122                 }
123                 public void put(Box b, Object value) {
124                     int newtextcolor = b.color;
125                     if (value == null) return;
126                     String s = value.toString();
127                     if (s.length() > 0 && s.charAt(0) == '#')
128                         newtextcolor = 0xFF000000 |
129                             (Integer.parseInt(s.substring(1, 3), 16) << 16) |
130                             (Integer.parseInt(s.substring(3, 5), 16) << 8) |
131                             Integer.parseInt(s.substring(5, 7), 16);
132                     else if (s.equals("black")) newtextcolor = black;
133                     else if (s.equals("blue")) newtextcolor = blue;
134                     else if (s.equals("green")) newtextcolor = green;
135                     else if (s.equals("cyan")) newtextcolor = cyan;
136                     else if (s.equals("red")) newtextcolor = red;
137                     else if (s.equals("magenta")) newtextcolor = magenta;
138                     else if (s.equals("brown")) newtextcolor = brown;
139                     else if (s.equals("lightGray")) newtextcolor = lightGray;
140                     else if (s.equals("darkGray")) newtextcolor = darkGray;
141                     else if (s.equals("brightBlue")) newtextcolor = brightBlue;
142                     else if (s.equals("brightGreen")) newtextcolor = brightGreen;
143                     else if (s.equals("brightCyan")) newtextcolor = brightCyan;
144                     else if (s.equals("brightRed")) newtextcolor = brightRed;
145                     else if (s.equals("pink")) newtextcolor = pink;
146                     else if (s.equals("yellow")) newtextcolor = yellow;
147                     else if (s.equals("white")) newtextcolor = white;
148                     else if (Log.on) Log.log(this, "invalid color \"" + s + "\" at " +
149                                              Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
150
151                     if (newtextcolor == b.textcolor) return;
152                     b.textcolor = newtextcolor;
153                     b.dirty();
154                 } });
155         
156         specialBoxProperties.put("text", new SpecialBoxProperty() {
157                 public Object get(Box b) { return b.text; }
158                 public void put(Box b, Object value) {
159                     String t = value == null ? "null" : value.toString();
160                     if (t.equals(b.text)) return;
161
162                     for(int i=0; i<t.length(); i++)
163                         if (Character.isISOControl(t.charAt(i))) {
164                             if (Log.on) Log.log(this, 
165                                                 "ISO Control characters are not permitted in box text strings; offending character is ASCII " +
166                                                ((int)t.charAt(i)));
167                             /* FIXME: reinstate
168                             return;
169                             */
170                         }
171
172                     // FIXME: don't know why this is needed
173                     b.dirty();
174
175                     b.text = t;
176                     b.textupdate();
177                     b.dirty();
178                 } });
179         
180         specialBoxProperties.put("thisbox", new SpecialBoxProperty() {
181                 public Object get(Box b) { return b; }
182                 public void put(Box b, Object value) {
183                     if (value == null) b.remove();
184                     else if (value.equals("window") || value.equals("frame")) {
185                         if (b.redirect != b && Log.on)
186                             Log.log(this, "WARNING: you have created a surface whose root box's redirect is not itself " +
187                                     "-- this isn't usually a good idea");
188                         Platform.createSurface(b, value.equals("frame"), true);
189                     } else if (Log.on) Log.log(this, "put invalid value to 'thisbox' property: " + value);
190                 }
191             });
192
193         specialBoxProperties.put("orient", new SpecialBoxProperty() {
194                 public Object get(Box b) {
195                     if (b.redirect == null) return "horizontal";
196                     else if (b.redirect != b) return get(b.redirect);
197                     else if (b.o == 1) return "vertical";
198                     else return "horizontal";
199                 }
200                 public void put(Box b, Object value) {
201                     if (value == null) return;
202                     if (b.redirect == null) return;
203                     if (b.redirect != b) {
204                         put(b.redirect, value);
205                         return;
206                     }
207                     if (value.equals("vertical")) {
208                         if (b.o == 1) return;
209                         b.o = 1;
210                         b.xo = 0;
211                     } else if (value.equals("horizontal")) {
212                         if (b.o == 0) return;
213                         b.o = 0;
214                         b.xo = 1;
215                     } else if (Log.on)
216                         Log.log(this, "invalid value put to orient property: " + value);
217                     b.mark_for_prerender();
218                     b.sync_cmin_to_children();
219                 } });
220
221         specialBoxProperties.put("static", new SpecialBoxProperty() {
222                 public Object get(Box b) {
223                     String cfsn = JSObject.getCurrentFunctionSourceName();
224                     for(int i=0; i<cfsn.length() - 1; i++)
225                         if (cfsn.charAt(i) == '.' && (cfsn.charAt(i+1) == '_' || Character.isDigit(cfsn.charAt(i+1)))) {
226                             cfsn = cfsn.substring(0, i);
227                             break;
228                         }
229                     return Static.getStatic(cfsn);
230                 }
231                 public void put(Box b, Object value) { }
232             });
233
234         specialBoxProperties.put("sizetoimage", new SpecialBoxProperty() {
235                 public Object get(Box b) { return b.sizetoimage ? Boolean.TRUE : Boolean.FALSE; }
236                 public void put(Box b, Object value) {
237                     if (stob(value)) {
238                         if (b.sizetoimage) return;
239                         b.sizetoimage = true;
240                         b.syncSizeToImage();
241                     } else {
242                         b.sizetoimage = false;
243                     }
244                 } });
245         
246         specialBoxProperties.put("fixedaspect", new SpecialBoxProperty() {
247                 public Object get(Box b) { return b.sizetoimage ? Boolean.TRUE : Boolean.FALSE; }
248                 public void put(Box b, Object value) {
249                     boolean newval = stob(value);
250                     if (newval == b.fixedaspect) return;
251                     b.fixedaspect = newval;
252                     b.dirty();
253                 } });
254         
255         specialBoxProperties.put("shrink", new SpecialBoxProperty() {
256                 public Object get(Box b) { return (b.vshrink && b.hshrink) ? Boolean.TRUE : Boolean.FALSE; }
257                 public void put(Box b, Object value) {
258                     boolean newshrink = stob(value);
259                     if (b.hshrink == newshrink && b.vshrink == newshrink) return;
260                     b.hshrink = b.vshrink = newshrink;
261                     b.mark_for_prerender();
262                 } });
263         
264         specialBoxProperties.put("hshrink", new SpecialBoxProperty() {
265                 public Object get(Box b) { return new Boolean(b.hshrink); }
266                 public void put(Box b, Object value) {
267                     boolean newshrink = stob(value);
268                     if (b.hshrink == newshrink) return;
269                     b.hshrink = newshrink;
270                     b.mark_for_prerender();
271                 }
272             });
273         
274         specialBoxProperties.put("vshrink", new SpecialBoxProperty() {
275                 public Object get(Box b) { return b.vshrink ? Boolean.TRUE : Boolean.FALSE; }
276                 public void put(Box b, Object value) {
277                     boolean newshrink = stob(value);
278                     if (b.vshrink == newshrink) return;
279                     b.vshrink = newshrink;
280                     b.mark_for_prerender();
281                 }
282             });
283         
284         specialBoxProperties.put("x", new SpecialBoxProperty() {
285                 public Object get(Box b) {
286                     if (b.surface == null) return new Integer(0);
287                     if (b.invisible) return new Integer(0);
288                     return new Integer(b.abs(0));
289                 }
290                 public void put(Box b, Object value) {
291                     if (b.getParent() == null && b.surface != null) {
292                         b.surface.setLocation(stoi(value), b.abs(1));
293                         b.surface.centerSurfaceOnRender = false;
294                     }
295                     b.set(abs, 0, stoi(value));
296                 }
297             });
298         
299         specialBoxProperties.put("y", new SpecialBoxProperty() {
300                 public Object get(Box b) {
301                     if (b.surface == null) return new Integer(0);
302                     if (b.invisible) return new Integer(0);
303                     return new Integer(b.abs(1));
304                 }
305                 public void put(Box b, Object value) {
306                     if (b.getParent() == null && b.surface != null) {
307                         b.surface.setLocation(b.abs(0), stoi(value));
308                         b.surface.centerSurfaceOnRender = false;
309                     }
310                     b.set(abs, 1, stoi(value));
311                 }
312             });
313
314         specialBoxProperties.put("width", new SpecialBoxProperty() {
315                 public Object get(Box b) { return new Integer(b.size(0)); }
316                 public void put(Box b, Object value) {
317                     if (b.sizetoimage) return;
318                     if (b.getParent() == null && b.surface != null) {
319                         b.set(size, 0, Box.max(Surface.scarPicture.getWidth(), stoi(value)));
320                         b.mark_for_prerender();
321                     } else {
322                         b.set(dmax, 0, stoi(value));
323                         b.set(dmin, 0, stoi(value));
324                     }
325                 } });
326         
327         specialBoxProperties.put("height", new SpecialBoxProperty() {
328                 public Object get(Box b) { return new Integer(b.size(1)); }
329                 public void put(Box b, Object value) {
330                     if (b.sizetoimage) return;
331                     if (b.getParent() == null && b.surface != null) {
332                         b.set(size, 1, Box.max(Surface.scarPicture.getHeight(), stoi(value)));
333                         b.mark_for_prerender();
334                     } else {
335                         b.set(dmax, 1, stoi(value));
336                         b.set(dmin, 1, stoi(value));
337                     }
338                 } });
339
340         specialBoxProperties.put("flex", new SpecialBoxProperty() {
341                 public Object get(Box b) { return new Double(b.flex); }
342                 public void put(Box b, Object value) {
343                     if (value == null) return;
344                     int newflex = stoi(value);
345                     if (newflex == b.flex) return;
346                     b.flex = newflex;
347                     b.mark_for_prerender();
348                 } });
349         
350         specialBoxProperties.put("tile", new SpecialBoxProperty() {
351                 public Object get(Box b) { return b.tile ? Boolean.TRUE : Boolean.FALSE; }
352                 public void put(Box b, Object value) {
353                     boolean newtile = stob(value);
354                     if (newtile == b.tile) return;
355                     b.tile = newtile;
356                     b.dirty();
357                 } });
358         
359         specialBoxProperties.put("align", new SpecialBoxProperty() {
360                 public Object get(Box b) {
361                     if (b.align == -1) return "topleft";
362                     else if (b.align == 1) return "bottomright";
363                     else return "center";
364                 }
365                 public void put(Box b, Object value) {
366                     String s = value == null ? "" : value.toString();
367                     byte newalign = b.align;
368                     if (s.equals("topleft")) newalign = -1;
369                     else if (s.equals("bottomright")) newalign = 1;
370                     else if (s.equals("center")) newalign = 0;
371                     else if (Log.on) Log.log(this, "invalid value put to align property: " + value);
372                     if (newalign == b.align) return;
373                     b.align = newalign;
374                     if (b.getParent() != null) b.getParent().mark_for_prerender();
375                 } });
376         
377         specialBoxProperties.put("invisible", new SpecialBoxProperty() {
378                 public Object get(Box b) {
379                     for (Box cur = b; cur != null; cur = cur.getParent()) { if (cur.invisible) return Boolean.TRUE; }
380                     return Boolean.FALSE;
381                 }
382                 public void put(Box b, Object value) {
383                     boolean newinvisible = stob(value);
384                     if (newinvisible == b.invisible) return;
385                     b.invisible = newinvisible;
386                     if (b.getParent() == null) {
387                         if (b.surface != null) b.surface.setInvisible(newinvisible);
388                     } else {
389                         b.dirty();
390                         b.getParent().mark_for_prerender();
391                         b.getParent().sync_cmin_to_children();
392                         b.getParent().dirty(b.pos(0), b.pos(1), b.size(0), b.size(1));
393                         b.getParent().dirty(b.oldpos(0), b.oldpos(1), b.oldsize(0), b.oldsize(1));
394                     }
395                 }});
396         
397         specialBoxProperties.put("absolute", new SpecialBoxProperty() {
398                 public Object get(Box b) { return b.absolute ? Boolean.TRUE : Boolean.FALSE; }
399                 public void put(Box b, Object value) {
400                     boolean newabsolute = stob(value);
401                     if (newabsolute == b.absolute) return;
402                     b.absolute = newabsolute;
403                     if (b.getParent() != null) {
404                         b.getParent().mark_for_prerender();
405                         b.getParent().sync_cmin_to_children();
406                     }
407                 } });
408         
409         specialBoxProperties.put("image", new SpecialBoxProperty() {
410                 public Object get(Box b) { return b.image == null ? null : Box.imageToNameMap.get(b.image); }
411                 public void put(Box b, Object value) { b.setImage(value == null ? null : value.toString()); }
412             });
413
414         specialBoxProperties.put("border", new SpecialBoxProperty() {
415                 public Object get(Box b) { return b.border == null ? null : Box.imageToNameMap.get(b.border); }
416                 public void put(Box b, Object value) { b.setBorder(value == null ? null : value.toString()); }
417             });
418
419         specialBoxProperties.put("font", new SpecialBoxProperty() {
420                 public Object get(Box b) { return b.font; }
421                 public void put(Box b, Object value) {
422                     if (value == null) value = Platform.getDefaultFont();
423                     b.font = value.toString();
424                     b.textupdate();
425                     b.dirty();
426                 } });
427         
428         specialBoxProperties.put("globalx", new SpecialBoxProperty() {
429                 public Object get(Box b) { return new Integer(b.getParent() == null || b.surface == null ? 0 : b.pos(0)); }
430                 public void put(Box b, Object value) {
431                     if (b.surface == null || b.getParent() == null) return;
432                     b.put("x", null, new Integer(stoi(value) - stoi(get(b.getParent()))));
433                 }
434             });
435         
436         specialBoxProperties.put("globaly", new SpecialBoxProperty() {
437                 public Object get(Box b) { return new Integer(b.getParent() == null || b.surface == null ? 0 : b.pos(1)); }
438                 public void put(Box b, Object value) {
439                     if (b.surface == null || b.getParent() == null) return;
440                     b.put("y", null, new Integer(stoi(value) - stoi(get(b.getParent()))));
441                 }
442             });
443         
444         specialBoxProperties.put("cursor", new SpecialBoxProperty() {
445                 public Object get(Box b) { return b.cursor; } 
446                 public void put(Box b, Object value) {
447                     b.cursor = (String)value;
448                     if (b.surface == null) return;
449
450                     // see if we need to update the surface cursor
451                     String tempcursor = b.surface.cursor;
452                     b.Move(b.surface.mousex, b.surface.mousey, b.surface.mousex, b.surface.mousey);
453                     if (b.surface.cursor != tempcursor) b.surface.syncCursor();
454                 } 
455             });
456         
457         specialBoxProperties.put("mousex", new SpecialBoxProperty() {
458                 public Object get(Box b) { return new Integer(b.surface == null ? 0 : b.surface.mousex - b.pos(0)); }
459             });
460         
461         specialBoxProperties.put("mousey", new SpecialBoxProperty() {
462                 public Object get(Box b) { return new Integer(b.surface == null ? 0 : b.surface.mousey - b.pos(1)); }
463             });
464         
465         specialBoxProperties.put("xwt", new SpecialBoxProperty() {
466                 public Object get(Box b) { return XWT.singleton; }
467             });
468         
469         specialBoxProperties.put("mouseinside", new SpecialBoxProperty() {
470                 public Object get(Box b) { return b.mouseinside ? Boolean.TRUE : Boolean.FALSE; }
471             });
472         
473         specialBoxProperties.put("numchildren", new SpecialBoxProperty() {
474                 public Object get(Box b) {
475                     if (b.redirect == null) return new Integer(0);
476                     if (b.redirect != b) return get(b.redirect);
477                     return new Integer(b.numChildren());
478                 } });
479         
480         SpecialBoxProperty mouseEventHandler = new SpecialBoxProperty() {
481                 public void put(String name, Box b, Object value) {
482                     if (b.surface == null) return;
483                     for(Box c = b.prevSibling(); c != null; c = c.prevSibling()) {
484                         Box siblingChild = c.whoIs(c.surface.mousex, c.surface.mousey);
485                         if (siblingChild != null) {
486                             siblingChild.put(name, c, value);
487                             return;
488                         }
489                     }
490                     if (b.getParent() != null)
491                         b.getParent().put(name, b.getParent(), value);
492                 }};
493
494         specialBoxProperties.put("Press1", mouseEventHandler);
495         specialBoxProperties.put("Press2", mouseEventHandler);
496         specialBoxProperties.put("Press3", mouseEventHandler);
497         specialBoxProperties.put("Release1", mouseEventHandler);
498         specialBoxProperties.put("Release2", mouseEventHandler);
499         specialBoxProperties.put("Release3", mouseEventHandler);
500         specialBoxProperties.put("Click1", mouseEventHandler);
501         specialBoxProperties.put("Click2", mouseEventHandler);
502         specialBoxProperties.put("Click3", mouseEventHandler);
503         specialBoxProperties.put("DoubleClick1", mouseEventHandler);
504         specialBoxProperties.put("DoubleClick2", mouseEventHandler);
505         specialBoxProperties.put("DoubleClick3", mouseEventHandler);
506
507         specialBoxProperties.put("root", new SpecialBoxProperty() {
508                 public Object get(Box b) {
509                     if (b.surface == null) return null;
510                     else if (b.getRoot() == null) return null;
511                     else if (b.getParent() == null) return b;
512                     else return b.getRoot().getRootProxy();
513                 } });
514
515         specialBoxProperties.put("Minimized", new SpecialBoxProperty() {
516                 public Object get(Box b) {
517                     if (b.getParent() == null && b.surface != null) return b.surface.minimized ? Boolean.TRUE : Boolean.FALSE;
518                     else return null;
519                 }
520                 public void put(Box b, Object value) {
521                     if (b.surface == null) return;
522                     boolean val = stob(value);
523                     if (b.getParent() == null && b.surface.minimized != val) b.surface.setMinimized(val);
524                 }
525             });
526
527         specialBoxProperties.put("Maximized", new SpecialBoxProperty() {
528                 public Object get(Box b) {
529                     if (b.getParent() == null && b.surface != null) return b.surface.maximized ? Boolean.TRUE : Boolean.FALSE;
530                     else return null;
531                 }
532                 public void put(Box b, Object value) {
533                     if (b.surface == null) return;
534                     boolean val = stob(value);
535                     if (b.getParent() == null && b.surface.maximized != val) b.surface.setMaximized(val);
536                 }
537             });
538
539         specialBoxProperties.put("toback", new SpecialBoxProperty() {
540                 public void put(Box b, Object value) {
541                     if (b.getParent() == null && stob(value) && b.surface != null) b.surface.toBack();
542                 }
543             });
544
545         specialBoxProperties.put("tofront", new SpecialBoxProperty() {
546                 public void put(Box b, Object value) {
547                     if (b.getParent() == null && stob(value) && b.surface != null) b.surface.toFront();
548                 }
549             });
550
551         specialBoxProperties.put("hscar", new SpecialBoxProperty() {
552                 public void put(Box b, Object value) {
553                     if (b.getParent() == null && b.surface != null) {
554                         b.surface.hscar = stoi(value);
555                         b.surface.dirty(0, 0, b.surface.width, b.surface.height);
556                         b.surface.Refresh();
557                     }
558                 }
559             });
560
561         specialBoxProperties.put("vscar", new SpecialBoxProperty() {
562                 public void put(Box b, Object value) {
563                     if (b.getParent() == null && b.surface != null) {
564                         b.surface.vscar = stoi(value);
565                         b.surface.dirty(0, 0, b.surface.width, b.surface.height);
566                         b.surface.Refresh();
567                     }
568                 }
569             });
570
571         specialBoxProperties.put("Close", new SpecialBoxProperty() {
572                 public void put(Box b, Object value) {
573                     if (b.getParent() == null && b.surface != null) b.surface.dispose(true);
574                 }
575             });
576
577         // these are all do-nothings; just to prevent space from getting taken up in the params Hash.
578         specialBoxProperties.put("KeyPressed", new SpecialBoxProperty());
579         specialBoxProperties.put("KeyReleased", new SpecialBoxProperty());
580         specialBoxProperties.put("PosChange", new SpecialBoxProperty());
581         specialBoxProperties.put("SizeChange", new SpecialBoxProperty());
582
583         specialBoxProperties.put("hpad", new SpecialBoxProperty() {
584                 public Object get(Box b) {
585                     if (b.redirect == null) return new Integer(0);
586                     if (b.redirect != b) return get(b.redirect);
587                     return new Integer(b.pad(0));
588                 }
589                 public void put(Box b, Object value) {
590                     if (b.redirect == null) return;
591                     if (b.redirect != b) { put(b.redirect, value); return; }
592                     int newval = stoi(value);
593                     if (newval == b.pad(0)) return;
594                     b.set(pad, 0, newval);
595                 }
596             });
597
598         specialBoxProperties.put("vpad", new SpecialBoxProperty() {
599                 public Object get(Box b) {
600                     if (b.redirect == null) return new Integer(0);
601                     if (b.redirect != b) return get(b.redirect);
602                     return new Integer(b.pad(1));
603                 }
604                 public void put(Box b, Object value) {
605                     if (b.redirect == null) return;
606                     if (b.redirect != b) { put(b.redirect, value); return; }
607                     int newval = stoi(value);
608                     if (newval == b.pad(1)) return;
609                     b.set(pad, 1, newval);
610                 }
611             });
612
613         specialBoxProperties.put("indexof", new SpecialBoxProperty() {
614                 public Object get(Box b) { return b.indexof(); }
615             });
616
617         specialBoxProperties.put("minwidth", new SpecialBoxProperty() {
618                 public Object get(Box b) { return new Integer(b.dmin(0)); }
619                 public void put(Box b, Object value) {
620                     if (b.sizetoimage) return;
621                     b.set(dmin, 0, stoi(value));
622                 }
623             });
624
625         specialBoxProperties.put("maxwidth", new SpecialBoxProperty() {
626                 public Object get(Box b) { return new Integer(b.dmax(0)); }
627                 public void put(Box b, Object value) {
628                     if (b.sizetoimage) return;
629                     b.set(dmax, 0, stoi(value));
630                 }
631             });
632
633         specialBoxProperties.put("minheight", new SpecialBoxProperty() {
634                 public Object get(Box b) { return new Integer(b.dmin(1)); }
635                 public void put(Box b, Object value) {
636                     if (b.sizetoimage) return;
637                     b.set(dmin, 1, stoi(value));
638                 }
639             });
640
641         specialBoxProperties.put("maxheight", new SpecialBoxProperty() {
642                 public Object get(Box b) { return new Integer(b.dmax(1)); }
643                 public void put(Box b, Object value) {
644                     if (b.sizetoimage) return;
645                     b.set(dmax, 1, stoi(value));
646                 }
647             });
648
649         specialBoxProperties.put("redirect", new SpecialBoxProperty() {
650                 public void put(Box b, Object value) { }
651                 public Object get(Box b) {
652                     if (b.redirect == null) return null;
653                     if (b.redirect == b) return Boolean.TRUE;
654                     return get(b.redirect);
655                 }
656             });
657
658         specialBoxProperties.put("apply", new SpecialBoxProperty() {
659                 public void put(Box b, Object value) { }
660                 public Object get(Box b) { return new Apply(b); }
661             });
662
663         specialBoxProperties.put("id", new SpecialBoxProperty() {
664                 public void put(Box b, Object value) { }
665                 public Object get(Box b) { return b.id; }
666             });
667     }
668
669         
670     /** helper that converts a String to a boolean according to JavaScript coercion rules */
671     public static boolean stob(Object o) {
672         if (o == null) return false;
673         return Boolean.TRUE.equals(o) || "true".equals(o);
674     }
675
676     /** helper that converts a String to an int according to JavaScript coercion rules */
677     public static int stoi(Object o) {
678         if (o == null) return 0;
679         if (o instanceof Integer) return ((Integer)o).intValue();
680
681         String s;
682         if (!(o instanceof String)) s = o.toString();
683         else s = (String)o;
684
685         try { return Integer.parseInt(s.indexOf('.') == -1 ? s : s.substring(0, s.indexOf('.'))); }
686         catch (NumberFormatException e) { return 0; }
687     }
688         
689     /** helper that converts a String to a int according to JavaScript coercion rules */
690     private static class Apply extends JSObject.JSFunction {
691
692         Box b;
693         public Apply(Box b) { this.b = b; }
694
695         public Object call(Context cx, Scriptable thisObj, Scriptable ctorObj, Object[] args) throws JavaScriptException {
696
697             // apply a template
698             if (args[0] instanceof String) {
699                 String templatename = (String)args[0];
700                 Template t = Template.getTemplate(templatename, null);
701                 if (t == null) {
702                     if (Log.on) Log.log(this, "template " + templatename + " not found at " +
703                                         Context.enter().interpreterSourceFile + ":" + Context.enter().interpreterLine);
704                 } else {
705                     if (ThreadMessage.suspendThread()) try {
706                         Function callback = args.length < 2 ? null : (Function)args[1];
707                         t.apply(b, null, null, callback, 0, t.numUnits());
708                     } finally {
709                         ThreadMessage.resumeThread();
710                     }
711                 }
712
713             // apply a list of properties
714             } else if (args[0] instanceof Scriptable && !(args[0] instanceof Box)) {
715                 // FIXME: need to ensure that this is putGlobally(), but still run traps...
716                 Scriptable s = (Scriptable)args[0];
717                 Object[] keys = s.getIds();
718                 for(int j=0; j<keys.length; j++) b.put(keys[j].toString(), null, s.get(keys[j].toString(), s));
719             }
720
721             return b;
722         }
723     }
724
725 }
726
727         
728