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