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