KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > thinlet > Thinlet


1 /* Thinlet GUI toolkit - www.thinlet.com
2  * Copyright (C) 2002-2003 Robert Bajzat (robert.bajzat@thinlet.com)
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */

17 package thinlet;
18
19 import java.awt.*;
20 import java.awt.datatransfer.*;
21 import java.awt.image.*;
22 import java.awt.event.*;
23 import java.lang.reflect.*;
24 import java.io.*;
25 import java.net.*;
26 import java.util.*;
27
28 /**
29  *
30  */

31 public class Thinlet extends Container
32     implements Runnable JavaDoc, Serializable {
33
34     private transient Font font;
35     private transient Color c_bg;
36     private transient Color c_text;
37     private transient Color c_textbg;
38     private transient Color c_border;
39     private transient Color c_disable;
40     private transient Color c_hover;
41     private transient Color c_press;
42     private transient Color c_focus;
43     private transient Color c_select;
44     private transient Color c_ctrl = null;
45     private transient int block;
46     private transient Image hgradient, vgradient;
47
48     private transient Thread JavaDoc timer;
49     private transient long watchdelay;
50     private transient long watch;
51     private transient String JavaDoc clipboard;
52     private transient ResourceBundle resourcebundle; // for internationalization
53

54     private static ResourceBundle langResource = null; // for I18N
55
private static ResourceBundle langResourceDefault = null; // for I18N
56
private transient boolean allI18n = false; // for I18N
57

58     // enter the starting characters of a list item text within a short time to select
59
private transient String JavaDoc findprefix = "";
60     private transient long findtime;
61
62     private Object JavaDoc content = createImpl("desktop");
63     private transient Object JavaDoc mouseinside;
64     private transient Object JavaDoc insidepart;
65     private transient Object JavaDoc mousepressed;
66     private transient Object JavaDoc pressedpart;
67     private transient int referencex, referencey;
68     private transient int mousex, mousey;
69     private transient Object JavaDoc focusowner;
70     private transient boolean focusinside;
71     private transient Object JavaDoc popupowner;
72     private transient Object JavaDoc tooltipowner;
73     //private transient int pressedkey;
74

75     private static final int DRAG_ENTERED = AWTEvent.RESERVED_ID_MAX + 1;
76     private static final int DRAG_EXITED = AWTEvent.RESERVED_ID_MAX + 2;
77     
78     private static long WHEEL_MASK = 0;
79     private static int MOUSE_WHEEL = 0;
80     private static Method wheelrotation = null;
81     private static int evm = 0;
82     static {
83         try {
84             WHEEL_MASK = AWTEvent.class.getField("MOUSE_WHEEL_EVENT_MASK").getLong(null);
85             MOUSE_WHEEL = MouseEvent.class.getField("MOUSE_WHEEL").getInt(null);
86         } catch (Exception JavaDoc exc) { /* not 1.4 */ }
87     }
88     {
89         setFont(new Font("SansSerif", Font.PLAIN, 12));
90         //setFont((Font) getToolkit().getDesktopProperty("win.messagebox.font"));
91
setColors(0xe6e6e6, 0x000000, 0xffffff,
92             0x909090, 0xb0b0b0, 0xededed, 0xb9b9b9, 0x89899a, 0xc5c5dd);
93             
94         // disable global focus-manager for this component in 1.4
95
if (MOUSE_WHEEL != 0) {
96             try {
97                 getClass().getMethod("setFocusTraversalKeysEnabled", new Class JavaDoc[] { Boolean.TYPE }).
98                     invoke(this, new Object JavaDoc[] { Boolean.FALSE });
99             } catch (Exception JavaDoc exc) { /* never */ }
100         }
101         // set listeners flags
102
enableEvents(AWTEvent.COMPONENT_EVENT_MASK |
103             AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK |
104             AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | WHEEL_MASK);
105             // EVM has larger fillRect, fillOval, and drawImage(part), others are correct
106
// contributed by Ibsen Ramos-Bonilla and AK
107
try {
108                 if ((System.getProperty("java.vendor").indexOf("Insignia") != -1) &&
109                     System.getProperty("os.name").indexOf("Windows CE") == -1) { evm = -1; }
110             } catch (Exception JavaDoc exc) { /* never */ }
111     }
112
113     /**
114      * Sets the 9 colors used for components, and repaints the whole UI
115      *
116      * @param background the backround of panels (dialogs, desktops),
117      * and disabled controls, not editable texts, lines between list items
118      * (the default value if <i>#e6e6e6</i>)
119      * @param text for text, arrow foreground (<i>black</i> by default)
120      * @param textbackground the background of text components, and lists
121      * (<i>white</i> by default)
122      * @param border for outer in inner borders of enabled components
123      * (<i>#909090</i> by default)
124      * @param disable for text, border, arrow color in disabled components
125      * (<i>#b0b0b0</i> by default)
126      * @param hover indicates that the mouse is inside a button area
127      * (<i>#ededed</i> by default)
128      * @param press for pressed buttons,
129      * gradient image is calculated using the background and this press color
130      * (<i>#b9b9b9</i> by default)
131      * @param focus for text caret and rectagle color marking the focus owner
132      * (<i>#89899a</i> by default)
133      * @param select used as the background of selected text, and list items,
134      * and in slider (<i>#c5c5dd</i> by default)
135      */

136     public void setColors(int background, int text, int textbackground,
137             int border, int disable, int hover, int press,
138             int focus, int select) {
139         c_bg = new Color(background); c_text = new Color(text);
140         c_textbg = new Color(textbackground); c_border = new Color(border);
141         c_disable = new Color(disable); c_hover = new Color(hover);
142         c_press = new Color(press); c_focus = new Color(focus);
143         c_select = new Color(select);
144         hgradient = vgradient = null;
145         repaint();
146     }
147     
148     //setDesktopProperty+
149

150     /**
151      * Sets the only one font used everywhere, and revalidates the whole UI.
152      * Scrollbar width/height, spinbox, and combobox button width,
153      * and slider size is the same as the font height
154      *
155      * @param font the default font is <i>SansSerif</i>, <i>plain</i>, and <i>12pt</i>
156      */

157     public void setFont(Font font) {
158         block = getFontMetrics(font).getHeight();
159         super.setFont(font);
160         this.font = font;
161         hgradient = vgradient = null;
162         if (content != null) validate(content);
163     }
164     
165     /**
166      *
167      */

168     private void doLayout(Object JavaDoc component) {
169         String JavaDoc classname = getClass(component);
170         if ("combobox" == classname) {
171             if (getBoolean(component, "editable", true)) {
172                 Image icon = getIcon(component, "icon", null);
173                 layoutField(component, block, false,
174                     (icon != null) ? icon.getWidth(this) : 0);
175             } // set editable -> validate (overwrite textfield repaint)
176
else {
177                 int selected = getInteger(component, "selected", -1);
178                 if (selected != -1) { //...
179
Object JavaDoc choice = getItem(component, selected);
180                     set(component, "text", get(choice, "text"));
181                     set(component, "icon", get(choice, "icon"));
182                 }
183             }
184         }
185         else if (("textfield" == classname) || ("passwordfield" == classname)) {
186             layoutField(component, 0, ("passwordfield" == classname), 0);
187         }
188         else if ("textarea" == classname) {
189             String JavaDoc text = getString(component, "text", "");
190             int start = getInteger(component, "start", 0);
191             if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); }
192             int end = getInteger(component, "end", 0);
193             if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); }
194             
195             boolean wrap = getBoolean(component, "wrap", false);
196             char[] chars = null;
197             if (wrap) {
198                 Rectangle bounds = getRectangle(component, "bounds");
199                 chars = getChars(component, text, true, bounds.width - 4, bounds.height);
200                 if (chars == null) { // need scrollbars
201
chars = getChars(component, text, true, bounds.width - block - 4, 0);
202                 }
203             }
204             else {
205                 chars = getChars(component, text, false, 0, 0);
206             }
207             
208             Font currentfont = (Font) get(component, "font");
209             FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
210             int width = 0, height = 0;
211             int caretx = 0; int carety = 0;
212             for (int i = 0, j = 0; j <= chars.length; j++) {
213                 if ((j == chars.length) || (chars[j] == '\n')) {
214                     width = Math.max(width, fm.charsWidth(chars, i, j - i));
215                     if ((end >= i) && (end <= j)) {
216                         caretx = fm.charsWidth(chars, i, end - i);
217                         carety = height;
218                     }
219                     height += fm.getHeight();
220                     i = j + 1;
221                 }
222             }
223             layoutScroll(component, width + 2, height - fm.getLeading() + 2, 0, 0, 0, 0,
224                 getBoolean(component, "border", true), 0);
225             scrollToVisible(component, caretx, carety, 2, fm.getAscent() + fm.getDescent() + 2); //?
226
}
227         else if ("tabbedpane" == classname) {
228             // tabbedpane (not selected) tab padding are 1, 3, 1, and 3 pt
229
Rectangle bounds = getRectangle(component, "bounds");
230             String JavaDoc placement = getString(component, "placement", "top");
231             boolean horizontal = ((placement == "top") || (placement == "bottom"));
232             boolean stacked = (placement == "stacked");
233     
234             // draw up tabs in row/column
235
int tabd = 0; Rectangle first = null; // x/y location of tab left/top
236
int tabsize = 0; // max height/width of tabs
237
for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
238                 if ((tabd == 0) && ((first = getRectangle(tab, "bounds")) != null)) {
239                     tabd = horizontal ? first.x : first.y; // restore previous offset
240
}
241                 Dimension d = getSize(tab, stacked ? 8 : horizontal ? 12 : 9,
242                     stacked ? 3 : horizontal ? 5 : 8);
243                 setRectangle(tab, "bounds", horizontal ? tabd : 0, horizontal ? 0 : tabd,
244                     stacked ? bounds.width : d.width, d.height);
245                 if (stacked) {
246                     tabd += d.height;
247                 } else {
248                     tabd += (horizontal ? d.width : d.height) - 3;
249                     tabsize = Math.max(tabsize, horizontal ? d.height : d.width);
250                 }
251             }
252             
253             // match tab height/width, set tab content size
254
int cx = (placement == "left") ? (tabsize + 1) : 2;
255             int cy = (placement == "top") ? (tabsize + 1) : 2;
256             int cwidth = bounds.width - ((horizontal || stacked) ? 4 : (tabsize + 3));
257             int cheight = bounds.height - (stacked ? (tabd + 3) :
258                 (horizontal ? (tabsize + 3) : 4));
259             for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
260                 Rectangle r = getRectangle(tab, "bounds");
261                 if (!stacked) {
262                     if (horizontal) {
263                         if (placement == "bottom") { r.y = bounds.height - tabsize; }
264                         r.height = tabsize;
265                     }
266                     else {
267                         if (placement == "right") { r.x = bounds.width - tabsize; }
268                         r.width = tabsize;
269                     }
270                 }
271                 
272                 Object JavaDoc comp = get(tab, ":comp"); // relative to the tab location
273
if ((comp != null) && getBoolean(comp, "visible", true)) {
274                     setRectangle(comp, "bounds",
275                         cx - r.x, stacked ? (r.height + 1) : (cy - r.y), cwidth, cheight);
276                     doLayout(comp);
277                 }
278             }
279             checkOffset(component);
280         }
281         else if (("panel" == classname) || (classname == "dialog")) {
282             int gap = getInteger(component, "gap", 0);
283             int[][] grid = getGrid(component);
284             int top = 0; int left = 0;
285             int contentwidth = 0; int contentheight = 0;
286             if (grid != null) { // has subcomponents
287
top = getInteger(component, "top", 0);
288                 left = getInteger(component, "left", 0);
289                 int bottom = getInteger(component, "bottom", 0);
290                 int right = getInteger(component, "right", 0);
291                 // sums the preferred size of cell widths and heights, gaps
292
contentwidth = left + getSum(grid[0], 0, grid[0].length, gap, false) + right;
293                 contentheight = top + getSum(grid[1], 0, grid[1].length, gap, false) + bottom;
294             }
295             
296             int titleheight = getSize(component, 0, 0).height; // title text and icon
297
setInteger(component, ":titleheight", titleheight, 0);
298             boolean scrollable = getBoolean(component, "scrollable", false);
299             boolean border = ("panel" == classname) && getBoolean(component, "border", false);
300             int iborder = (border ? 1 : 0);
301             if (scrollable) { // set scrollpane areas
302
if ("panel" == classname) {
303                     int head = titleheight / 2;
304                     int headgap = (titleheight > 0) ? (titleheight - head - iborder) : 0;
305                     scrollable = layoutScroll(component, contentwidth, contentheight,
306                         head, 0, 0, 0, border, headgap);
307                 }
308                 else { // dialog
309
scrollable = layoutScroll(component, contentwidth, contentheight,
310                         3 + titleheight, 3, 3, 3, true, 0);
311                 }
312             }
313             if (!scrollable) { // clear scrollpane bounds //+
314
set(component, ":view", null); set(component, ":port", null);
315             }
316             
317             if (grid != null) {
318                 int areax = 0; int areay = 0; int areawidth = 0; int areaheight = 0;
319                 if (scrollable) {
320                     // components are relative to the viewport
321
Rectangle view = getRectangle(component, ":view");
322                     areawidth = view.width; areaheight = view.height;
323                 }
324                 else { // scrollpane isn't required
325
// components are relative to top/left corner
326
Rectangle bounds = getRectangle(component, "bounds");
327                     areawidth = bounds.width; areaheight = bounds.height;
328                     if ("panel" == classname) {
329                         areax = iborder; areay = Math.max(iborder, titleheight);
330                         areawidth -= 2 * iborder; areaheight -= areay + iborder;
331                     }
332                     else { // dialog
333
areax = 4; areay = 4 + titleheight;
334                         areawidth -= 8; areaheight -= areay + 4;
335                     }
336                 }
337             
338                 for (int i = 0; i < 2; i++) { // i=0: horizontal, i=1: vertical
339
// remaining space
340
int d = ((i == 0) ? (areawidth - contentwidth) : (areaheight - contentheight));
341                     if (d != 0) { //+ > 0
342
int w = getSum(grid[2 + i], 0, grid[2 + i].length, 0, false);
343                         if (w > 0) {
344                             for (int j = 0; j < grid[i].length; j++) {
345                                 if (grid[2 + i][j] != 0) {
346                                     grid[i][j] += d * grid[2 + i][j] / w;
347                                 }
348                             }
349                         }
350                     }
351                 }
352                 
353                 Object JavaDoc comp = get(component, ":comp");
354                 for (int i = 0; comp != null; comp = get(comp, ":next")) {
355                     if (!getBoolean(comp, "visible", true)) { continue; }
356                     int ix = areax + left + getSum(grid[0], 0, grid[4][i], gap, true);
357                     int iy = areay + top + getSum(grid[1], 0, grid[5][i], gap, true);
358                     int iwidth = getSum(grid[0], grid[4][i], grid[6][i], gap, false);
359                     int iheight = getSum(grid[1], grid[5][i], grid[7][i], gap, false);
360                     String JavaDoc halign = getString(comp, "halign", "fill");
361                     String JavaDoc valign = getString(comp, "valign", "fill");
362                     if ((halign != "fill") || (valign != "fill")) {
363                         Dimension d = getPreferredSize(comp);
364                         if (halign != "fill") {
365                             int dw = Math.max(0, iwidth - d.width);
366                             if (halign == "center") { ix += dw / 2; }
367                                 else if (halign == "right") { ix += dw; }
368                             iwidth -= dw;
369                         }
370                         if (valign != "fill") {
371                             int dh = Math.max(0, iheight - d.height);
372                             if (valign == "center") { iy += dh / 2; }
373                                 else if (valign == "bottom") { iy += dh; }
374                             iheight -= dh;
375                         }
376                     }
377                     setRectangle(comp, "bounds", ix, iy, iwidth, iheight);
378                     doLayout(comp);
379                     i++;
380                 }
381             }
382         }
383         else if ("desktop" == classname) {
384             Rectangle bounds = getRectangle(component, "bounds");
385             for (Object JavaDoc comp = get(component, ":comp");
386                     comp != null; comp = get(comp, ":next")) {
387                 String JavaDoc iclass = getClass(comp);
388                 if (iclass == "dialog") {
389                     Dimension d = getPreferredSize(comp);
390                     if (get(comp, "bounds") == null)
391                     setRectangle(comp, "bounds",
392                         Math.max(0, (bounds.width - d.width) / 2),
393                         Math.max(0, (bounds.height - d.height) / 2),
394                         Math.min(d.width, bounds.width), Math.min(d.height, bounds.height));
395                 } else if ((iclass != ":combolist") && (iclass != ":popup")) {
396                     setRectangle(comp, "bounds", 0, 0, bounds.width, bounds.height);
397                 }
398                 doLayout(comp);
399             }
400         }
401         else if ("spinbox" == classname) {
402             layoutField(component, block, false, 0);
403         }
404         else if ("splitpane" == classname) {
405             Rectangle bounds = getRectangle(component, "bounds");
406             boolean horizontal = ("vertical" != get(component, "orientation"));
407             int divider = getInteger(component, "divider", -1);
408             int maxdiv = Math.max(0, (horizontal ? bounds.width : bounds.height) - 5);
409
410             Object JavaDoc comp1 = get(component, ":comp");
411             boolean visible1 = (comp1 != null) && getBoolean(comp1, "visible", true);
412             if (divider == -1) {
413                 int d1 = 0;
414                 if (visible1) {
415                     Dimension d = getPreferredSize(comp1);
416                     d1 = horizontal ? d.width : d.height;
417                 }
418                 divider = Math.min(d1, maxdiv);
419                 setInteger(component, "divider", divider, -1);
420             }
421             else if (divider > maxdiv) {
422                 setInteger(component, "divider", divider = maxdiv, -1);
423             }
424
425             if (visible1) {
426                 setRectangle(comp1, "bounds", 0, 0, horizontal ? divider : bounds.width,
427                     horizontal ? bounds.height : divider);
428                 doLayout(comp1);
429             }
430             Object JavaDoc comp2 = (comp1 != null) ? get(comp1, ":next") : null;
431             if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
432                 setRectangle(comp2, "bounds", horizontal ? (divider + 5) : 0,
433                     horizontal ? 0 : (divider + 5),
434                     horizontal ? (bounds.width - 5 - divider) : bounds.width,
435                     horizontal ? bounds.height : (bounds.height - 5 - divider));
436                 doLayout(comp2);
437             }
438         }
439         else if (("list" == classname) ||
440                 ("table" == classname) || ("tree" == classname)) {
441             int line = getBoolean(component, "line", true) ? 1 : 0;
442             int width = 0;
443             int columnheight = 0;
444             if ("table" == classname) {
445                 Object JavaDoc header = get(component, "header");
446                 int[] columnwidths = null;
447                 if (header != null) {
448                     columnwidths = new int[getCount(header)];
449                     Object JavaDoc column = get(header, ":comp");
450                     for (int i = 0; i < columnwidths.length; i++) {
451                         if (i != 0) { column = get(column, ":next"); }
452                         columnwidths[i] = getInteger(column, "width", 80);
453                         width += columnwidths[i];
454                         Dimension d = getSize(column, 2, 2);
455                         columnheight = Math.max(columnheight, d.height);
456                     }
457                 }
458                 set(component, ":widths", columnwidths);
459             }
460             int y = 0;
461             int level = 0;
462             for (Object JavaDoc item = get(component, ":comp"); item != null;) {
463                 int x = 0;
464                 int iwidth = 0; int iheight = 0;
465                 if ("table" == classname) {
466                     iwidth = width;
467                     for (Object JavaDoc cell = get(item, ":comp"); cell != null; cell = get(cell, ":next")) {
468                         Dimension d = getSize(cell, 2, 2);
469                         iheight = Math.max(iheight, d.height);
470                     }
471                 }
472                 else {
473                     if ("tree" == classname) {
474                         x = (level + 1) * block;
475                     }
476                     Dimension d = getSize(item, 6, 2);
477                     iwidth = d.width; iheight = d.height;
478                     width = Math.max(width, x + d.width);
479                 }
480                 setRectangle(item, "bounds", x, y, iwidth, iheight);
481                 y += iheight + line;
482                 if ("tree" == classname) {
483                     Object JavaDoc next = get(item, ":comp");
484                     if ((next != null) && getBoolean(item, "expanded", true)) {
485                         level++;
486                     } else {
487                         while (((next = get(item, ":next")) == null) && (level > 0)) {
488                             item = getParent(item);
489                             level--;
490                         }
491                     }
492                     item = next;
493                 } else {
494                     item = get(item, ":next");
495                 }
496             }
497             layoutScroll(component, width, y - line, columnheight, 0, 0, 0, true, 0);
498         }
499         else if ("menubar" == classname) {
500             Rectangle bounds = getRectangle(component, "bounds");
501             int x = 0;
502             for (Object JavaDoc menu = get(component, ":comp");
503                     menu != null; menu = get(menu, ":next")) {
504                 Dimension d = getSize(menu, 8, 4);
505                 setRectangle(menu, "bounds", x, 0, d.width, bounds.height);
506                 x += d.width;
507             }
508         }
509         else if ("bean" == classname) {
510             Rectangle r = getRectangle(component, "bounds");
511             ((Component) get(component, "bean")).setBounds(r);
512         }
513     }
514     
515     /**
516      * Scroll tabs to make the selected one visible
517      * @param component a tabbedpane
518      */

519     private void checkOffset(Object JavaDoc component) {
520         String JavaDoc placement = getString(component, "placement", "top");
521         int selected = getInteger(component, "selected", 0); int i = 0;
522         if (placement == "stacked") {
523             int dy = 0;
524             for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
525                 Rectangle r = getRectangle(tab, "bounds");
526                 r.y = dy;
527                 dy += r.height;
528                 if (i == selected) { dy += getRectangle(get(tab, ":comp"), "bounds").height + 2; }
529                 i++;
530             }
531             if (mouseinside == component) { // layout changed, check the hovered tab
532
checkLocation();
533             }
534             return;
535         }
536         boolean horizontal = ((placement == "top") || (placement == "bottom"));
537         Rectangle bounds = getRectangle(component, "bounds");
538         int panesize = horizontal ? bounds.width : bounds.height;
539         int first = 0; int last = 0; int d = 0;
540         for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
541             Rectangle r = getRectangle(tab, "bounds");
542             if (i == 0) { first = (horizontal ? r.x : r.y); }
543             last = (horizontal ? (r.x + r.width) : (r.y + r.height));
544             if (i == selected) {
545                 int ifrom = (horizontal ? r.x : r.y) - 6;
546                 int ito = (horizontal ? (r.x + r.width) : (r.y + r.height)) + 6;
547                 if (ifrom < 0) { d = -ifrom; }
548                 else if (ito > panesize) { d = panesize - ito; }
549             }
550             i++;
551         }
552         d = Math.min(-first, Math.max(d, panesize - last));
553         if (d != 0) {
554             for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
555                 Rectangle r = getRectangle(tab, "bounds");
556                 if (horizontal) { r.x += d; } else { r.y += d; }
557                 Object JavaDoc comp = get(tab, ":comp"); // relative to the tab location
558
if ( (comp != null) && getBoolean(comp, "visible", true)) {
559                     Rectangle rc = getRectangle(comp, "bounds");
560                     if (horizontal) { rc.x -= d; } else { rc.y -= d; }
561                 }
562             }
563             if (mouseinside == component) { // layout changed, check the hovered tab
564
checkLocation();
565             }
566         }
567     }
568     
569     /**
570      *
571      */

572     private char[] getChars(Object JavaDoc component,
573             String JavaDoc text, boolean wrap, int width, int height) {
574         char[] chars = (char[]) get(component, ":text");
575         if ((chars == null) || (chars.length != text.length())) {
576             chars = text.toCharArray();
577             set(component, ":text", chars);
578         }
579         else text.getChars(0, chars.length, chars, 0);
580         
581         if (wrap) {
582             Font currentfont = (Font) get(component, "font");
583             FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
584             int lines = (height - 4 + fm.getLeading()) / fm.getHeight();
585             boolean prevletter = false; int n = chars.length; int linecount = 0;
586             for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k)
587
if (((k == n) || (chars[k] == '\n') || (chars[k] == ' ')) &&
588                         (j > i) && (fm.charsWidth(chars, i, k - i) > width)) {
589                     chars[j] = '\n';
590                     k--; // draw line to the begin of the current word (+ spaces) if it is out of width
591
}
592                 else if ((k == n) || (chars[k] == '\n')) { // draw line to the text/line end
593
j = k; prevletter = false;
594                 }
595                 else {
596                     if ((chars[k] == ' ') && (prevletter || (j > i))) { j = k; } // keep spaces starting the line
597
prevletter = (chars[k] != ' ');
598                     continue;
599                 }
600                 linecount++;
601                 if ((lines != 0) && (linecount == lines)) { return null; }
602                 i = j + 1;
603             }
604         }
605         return chars;
606     }
607     
608     /**
609      *
610      */

611     /*private boolean wrap(char[] chars, int width, int lines) {
612         FontMetrics fm = getFontMetrics(font);
613         boolean prevletter = false; int n = chars.length; int linecount = 0;
614         for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k)
615             if (((k == n) || (chars[k] == '\n') || (chars[k] == ' ')) &&
616                     (j > i) && (fm.charsWidth(chars, i, k - i) > width)) {
617                 chars[j] = '\t';
618                 k--; // draw line to the begin of the current word (+ spaces) if it is out of width
619             }
620             else if ((k == n) || (chars[k] == '\n')) { // draw line to the text/line end
621                 j = k; prevletter = false;
622             }
623             else {
624                 if (chars[k] == '\t') { chars[k] = ' '; }
625                 if ((chars[k] == ' ') && (prevletter || (j > i))) { j = k; } // keep spaces starting the line
626                 prevletter = (chars[k] != ' ');
627                 continue;
628             }
629             linecount++;
630             if ((lines != 0) && (linecount == lines)) { return false; }
631             i = j + 1;
632         }
633         return true;
634     }*/

635     
636     /**
637      * @param component a menuitem
638      * @return key modifier strings and key text
639      */

640     private String JavaDoc getAccelerator(Object JavaDoc component) {
641         Object JavaDoc accelerator = get(component, "accelerator");
642         if (accelerator != null) {
643             long keystroke = ((Long JavaDoc) accelerator).longValue();
644             int keycode = (int) (keystroke >> 32);
645             int modifiers = (int) (keystroke & 0xffff);
646             return KeyEvent.getKeyModifiersText(keycode) + " " +
647                 KeyEvent.getKeyText(modifiers);
648         }
649         return null;
650     }
651     
652     /**
653      * Pop up the list of choices for the given combobox
654      * @param combobox
655      * @return the created combolist
656      */

657     private Object JavaDoc popupCombo(Object JavaDoc combobox) {
658         // combobox bounds relative to the root desktop
659
int combox = 0, comboy = 0, combowidth = 0, comboheight = 0;
660         for (Object JavaDoc comp = combobox; comp != content; comp = getParent(comp)) {
661             Rectangle r = getRectangle(comp, "bounds");
662             combox += r.x; comboy += r.y;
663             Rectangle view = getRectangle(comp, ":view");
664             if (view != null) {
665                 combox -= view.x; comboy -= view.y;
666                 Rectangle port = getRectangle(comp, ":port");
667                 combox += port.x; comboy+= port.y;
668             }
669             if (comp == combobox) { combowidth = r.width; comboheight = r.height; }
670         }
671         // :combolist -> combobox and combobox -> :combolist
672
Object JavaDoc combolist = createImpl(":combolist");
673         set(combolist, "combobox", combobox);
674         set(combobox, ":combolist", combolist);
675         // add :combolist to the root desktop and set the combobox as popupowner
676
popupowner = combobox;
677         insertItem(content, ":comp", combolist, 0);
678         set(combolist, ":parent", content);
679         // lay out choices verticaly and calculate max width and height sum
680
int pw = 0; int ph = 0;
681         for (Object JavaDoc item = get(combobox, ":comp");
682                 item != null; item = get(item, ":next")) {
683             Dimension d = getSize(item, 8 , 4);
684             setRectangle(item, "bounds", 0, ph, d.width, d.height);
685             pw = Math.max(pw, d.width);
686             ph += d.height;
687         }
688         // set :combolist bounds
689
int listy = 0, listheight = 0;
690         int bellow = getRectangle(content, "bounds").height - comboy - comboheight - 1;
691         if ((ph + 2 > bellow) && (comboy - 1 > bellow)) { // popup above combobox
692
listy = Math.max(0, comboy - 1 - ph - 2);
693             listheight = Math.min(comboy - 1, ph + 2);
694         }
695         else { // popup bellow combobox
696
listy = comboy + comboheight + 1;
697             listheight = Math.min(bellow, ph + 2);
698         }
699         setRectangle(combolist, "bounds", combox, listy, combowidth, listheight);
700         layoutScroll(combolist, pw, ph, 0, 0, 0, 0, true, 0);
701         repaint(combolist);
702         // hover the selected item
703
int selected = getInteger(combobox, "selected", -1);
704         setInside(combolist, (selected != -1) ? getItem(combobox, selected) : null, true);
705         return combolist;
706     }
707     
708     /**
709      * @param component menubar or :popup
710      * @return the created popupmenu
711      */

712     private Object JavaDoc popupMenu(Object JavaDoc component) {
713         Object JavaDoc popup = get(component, ":popup"); // first :popup child
714
Object JavaDoc selected = get(component, "selected"); // selected menu in of the component
715
if (popup != null) { // remove its current :popup
716
if (get(popup, "menu") == selected) { return null; } // but the currect one
717
set(popup, "selected", null);
718             set(popup, "menu", null);
719             repaint(popup);
720             removeItemImpl(content, popup);
721             set(popup, ":parent", null);
722             set(component, ":popup", null);
723             if (mouseinside == popup) {
724                 checkLocation();
725             }
726             popupMenu(popup); // remove recursively
727
}
728         // pop up the selected menu only
729
if ((selected == null) || (getClass(selected) != "menu")) { return null; }
730         // create the :popup, :popup.menu -> menu,
731
// menubar|:popup.:popup -> :popup, menubar|:popup.selected -> menu
732
popup = createImpl(":popup");
733         set(popup, "menu", selected);
734         set(component, ":popup", popup);
735         insertItem(content, ":comp", popup, 0);
736         set(popup, ":parent", content);
737         // calculates the bounds of the previous menubar/:popup relative to the root desktop
738
int menux = 0, menuy = 0, menuwidth = 0, menuheight = 0;
739         for (Object JavaDoc comp = component; comp != content; comp = getParent(comp)) {
740             Rectangle r = getRectangle(comp, "bounds");
741             menux += r.x; menuy += r.y;
742             Rectangle view = getRectangle(comp, ":view");
743             if (view != null) {
744                 menux -= view.x; menuy -= view.y;
745                 Rectangle port = getRectangle(comp, ":port");
746                 menux += port.x; menuy+= port.y;
747             }
748             if (comp == component) { menuwidth = r.width; menuheight = r.height; }
749         }
750         // set :popup bounds
751
Rectangle menubounds = getRectangle(selected, "bounds");
752         boolean menubar = ("menubar" == getClass(component));
753         if (menubar) { popupowner = component; }
754         popup(selected, popup,
755             menubar ? (("bottom" != get(component, "placement")) ? 'D' : 'U') : 'R',
756             menubar ? (menux + menubounds.x) : menux, menuy + menubounds.y,
757             menubar ? menubounds.width : menuwidth,
758             menubar ? menuheight : menubounds.height, menubar ? 1 : 3);
759         return popup;
760     }
761     
762     /**
763      * @param popupmenu
764      */

765     private void popupPopup(Object JavaDoc popupmenu, int x, int y) {
766         // :popup.menu -> popupmenu, popupmenu.:popup -> :popup
767
Object JavaDoc popup = createImpl(":popup");
768         set(popup, "menu", popupmenu);
769         set(popupmenu, ":popup", popup);
770         // add :popup to the root desktop and set the combobox as popupowner
771
popupowner = popupmenu;
772         insertItem(content, ":comp", popup, 0);
773         set(popup, ":parent", content);
774         // lay out
775
popup(popupmenu, popup, 'D', x, y, 0, 0, 0);
776         // invoke menushown listener
777
invoke(popupmenu, null, "menushown"); // TODO before
778
}
779     
780     /**
781      * Lays out a popupmenu
782      * @param menu menubar's menu, menu's menu,
783      * or component's popupmenu including items
784      * @param popup created popupmenu
785      * @param direction 'U' for up, 'D' for down, and 'R' for right
786      * @param x menu's x location relative to the desktop
787      * @param y menu's y location
788      * @param width menu's width, or zero for popupmenu
789      * @param height menu's height
790      * @param offset inner padding relative to the menu's bounds
791      */

792     private void popup(Object JavaDoc menu, Object JavaDoc popup,
793             char direction, int x, int y, int width, int height, int offset) {
794         int pw = 0; int ph = 0;
795         for (Object JavaDoc item = get(menu, ":comp"); item != null; item = get(item, ":next")) {
796             String JavaDoc itemclass = getClass(item);
797             Dimension d = (itemclass == "separator") ? new Dimension(1, 1) :
798                 getSize(item, 8 , 4);
799             if (itemclass == "checkboxmenuitem") {
800                 d.width = d.width + block + 3;
801                 d.height = Math.max(block, d.height);
802             }
803             else if (itemclass == "menu") {
804                 d.width += block;
805             }
806             String JavaDoc accelerator = getAccelerator(item); // add accelerator width
807
if (accelerator != null) {
808                 d.width += 4 + getFontMetrics(font).stringWidth(accelerator); //TODO font, height and gap
809
}
810             setRectangle(item, "bounds", 1, 1 + ph, d.width, d.height);
811             pw = Math.max(pw, d.width);
812             ph += d.height;
813         }
814         pw += 2; ph += 2; // add border widths
815
// set :popup bounds
816
Rectangle desktop = getRectangle(content, "bounds");
817         if (direction == 'R') {
818             x += ((x + width - offset + pw > desktop.width) &&
819                 (x >= pw - offset)) ? (offset - pw) : (width - offset);
820             if ((y + ph > desktop.height) && (ph <= y + height)) { y -= ph - height; }
821         } else {
822             boolean topspace = (y >= ph - offset); // sufficient space above
823
boolean bottomspace = (desktop.height - y - height >= ph - offset);
824             y += ((direction == 'U') ? (topspace || !bottomspace) :
825                 (!bottomspace && topspace)) ? (offset - ph) : (height - offset);
826         }
827         setRectangle(popup, "bounds",
828             Math.max(0, Math.min(x, desktop.width - pw)),
829             Math.max(0, Math.min(y, desktop.height - ph)), pw, ph);
830         repaint(popup);
831     }
832
833     /**
834      * @param item //TODO can be scrollbar string
835      */

836     private void closeCombo(Object JavaDoc combobox, Object JavaDoc combolist, Object JavaDoc item) {
837         if ((item != null) && getBoolean(item, "enabled", true)) {
838             String JavaDoc text = getString(item, "text", "");
839             set(combobox, "text", text); // if editable
840
putProperty(combobox, "i18n.text", null); // for I18N
841
setInteger(combobox, "start", text.length(), 0);
842             setInteger(combobox, "end", 0, 0);
843             set(combobox, "icon", get(item, "icon"));
844             validate(combobox);
845             setInteger(combobox, "selected", getIndex(combobox, item), -1);
846             invoke(combobox, item, "action");
847         }
848         set(combolist, "combobox", null);
849         set(combobox, ":combolist", null);
850         removeItemImpl(content, combolist);
851         repaint(combolist);
852         set(combolist, ":parent", null);
853         popupowner = null;
854         if (mouseinside == combolist) {
855             checkLocation();
856         }
857     }
858
859     /**
860      *
861      */

862     private void closeup() {
863         if (popupowner != null) {
864             String JavaDoc classname = getClass(popupowner);
865             if ("menubar" == classname) {
866                 set(popupowner, "selected", null);
867                 popupMenu(popupowner);
868                 repaint(popupowner); // , selected
869
}
870             else if ("combobox" == classname) {
871                 closeCombo(popupowner, get(popupowner, ":combolist"), null);
872             }
873             else { // "popupmenu"
874
popupMenu(popupowner);
875             }
876             popupowner = null;
877         }
878     }
879
880     /**
881      *
882      */

883     private void showTip() {
884         String JavaDoc text = null;
885         tooltipowner = null;
886         String JavaDoc classname = getClass(mouseinside);
887         if ((classname == "tabbedpane") || (classname == "menubar") || (classname == ":popup")) {
888             if (insidepart != null) {
889                 text = getString(insidepart, "tooltip", null);
890             }
891         }
892         else if (classname == ":combolist") {
893             if (insidepart instanceof Object JavaDoc[]) {
894                 text = getString(insidepart, "tooltip", null);
895             }
896         }
897         // TODO list table tree
898
if (text == null) { text = getString(mouseinside, "tooltip", null); }
899             else { tooltipowner = insidepart; }
900         if (text != null) {
901             FontMetrics fm = getFontMetrics(font);
902             int width = fm.stringWidth(text) + 4;
903             int height = fm.getAscent() + fm.getDescent() + 4;
904             if (tooltipowner == null) { tooltipowner = mouseinside; }
905             Rectangle bounds = getRectangle(content, "bounds");
906             int tx = Math.max(0, Math.min(mousex + 10, bounds.width - width));
907             int ty = Math.max(0, Math.min(mousey + 10, bounds.height - height));
908             setRectangle(tooltipowner, ":tooltipbounds", tx, ty, width, height);
909             repaint(tx, ty, width, height);
910         }
911     }
912
913     /**
914      *
915      */

916     private void hideTip() {
917         if (tooltipowner != null) {
918             Rectangle bounds = getRectangle(tooltipowner, ":tooltipbounds");
919             set(tooltipowner, ":tooltipbounds", null);
920             tooltipowner = null;
921             repaint(bounds.x, bounds.y, bounds.width, bounds.height);
922         }
923     }
924
925     /**
926      *
927      */

928     private void layoutField(Object JavaDoc component, int dw, boolean hidden, int left) {
929         int width = getRectangle(component, "bounds").width - left -dw;
930         String JavaDoc text = getString(component, "text", "");
931         int start = getInteger(component, "start", 0);
932         if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); }
933         int end = getInteger(component, "end", 0);
934         if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); }
935         int offset = getInteger(component, ":offset", 0);
936         int off = offset;
937         Font currentfont = (Font) get(component, "font");
938         FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
939         int caret = hidden ? (fm.charWidth('*') * end) :
940             fm.stringWidth(text.substring(0, end));
941         if (off > caret) {
942             off = caret;
943         }
944         else if (off < caret - width + 4) {
945             off = caret - width + 4;
946         }
947         off = Math.max(0, Math.min(off, (hidden ? (fm.charWidth('*') *
948             text.length()) : fm.stringWidth(text)) - width + 4));
949         if (off != offset) {
950             setInteger(component, ":offset", off, 0);
951         }
952     }
953     
954     /**
955      * Set viewport (:port) bounds excluding borders, view position and content
956      * size (:view), horizontal (:horizontal), and vertical (:vertical) scrollbar
957      * bounds
958      *
959      * @param component scrollable widget
960      * @param contentwidth preferred component width
961      * @param contentheight preferred component height
962      * @param top top inset (e.g. table header, dialog title, half of panel title)
963      * @param left left inset (e.g. dialog border)
964      * @param bottom bottom inset (e.g. dialog border)
965      * @param right right inset (e.g. dialog border)
966      * @param topgap (lower half of panel title)
967      * @return true if scrollpane is required, otherwise false
968      *
969      * list: 0, 0, 0, 0, true, 0 | table: header, ... | dialog: header, 3, 3, 3, true, 0
970      * title-border panel: header / 2, 0, 0, 0, true, head
971      */

972     private boolean layoutScroll(Object JavaDoc component,
973             int contentwidth, int contentheight,
974             int top, int left, int bottom, int right, boolean border, int topgap) {
975         Rectangle bounds = getRectangle(component, "bounds");
976         int iborder = border ? 1 : 0; int iscroll = block + 1 - iborder;
977         int portwidth = bounds.width - left - right - 2 * iborder; // available horizontal space
978
int portheight = bounds.height - top - topgap - bottom - 2 * iborder; // vertical space
979
boolean hneed = contentwidth > portwidth; // horizontal scrollbar required
980
boolean vneed = contentheight > portheight - (hneed ? iscroll : 0); // vertical scrollbar needed
981
if (vneed) { portwidth -= iscroll; } // subtract by vertical scrollbar width
982
hneed = hneed || (vneed && (contentwidth > portwidth));
983         if (hneed) { portheight -= iscroll; } // subtract by horizontal scrollbar height
984

985         setRectangle(component, ":port", left + iborder, top + iborder + topgap, portwidth, portheight);
986         if (hneed) {
987             setRectangle(component, ":horizontal", left, bounds.height - bottom - block - 1,
988                 bounds.width - left - right - (vneed ? block : 0), block + 1);
989         } else { set(component, ":horizontal", null); }
990         if (vneed) {
991             setRectangle(component, ":vertical", bounds.width - right - block - 1, top,
992                 block + 1, bounds.height - top - bottom - (hneed ? block : 0));
993         } else { set(component, ":vertical", null); }
994         
995         contentwidth = Math.max(contentwidth, portwidth);
996         contentheight = Math.max(contentheight, portheight);
997         int viewx = 0, viewy = 0;
998         Rectangle view = getRectangle(component, ":view");
999         if (view != null) { // check the previous location
1000
viewx = Math.max(0, Math.min(view.x, contentwidth - portwidth));
1001            viewy = Math.max(0, Math.min(view.y, contentheight - portheight));
1002        }
1003        setRectangle(component, ":view", viewx, viewy, contentwidth, contentheight);
1004        return vneed || hneed;
1005    }
1006
1007    /**
1008     *
1009     */

1010    private void scrollToVisible(Object JavaDoc component,
1011            int x, int y, int width, int height) {
1012        Rectangle view = getRectangle(component, ":view");
1013        Rectangle port = getRectangle(component, ":port");
1014        int vx = Math.max(x + width - port.width, Math.min(view.x, x));
1015        int vy = Math.max(y + height - port.height, Math.min(view.y, y));
1016        if ((view.x != vx) || (view.y != vy)) {
1017            repaint(component); // horizontal | vertical
1018
view.x = vx; view.y = vy;
1019        }
1020    }
1021    
1022    /**
1023     * Gets the preferred size of the root component
1024     *
1025     * @return a dimension object indicating the root component's preferred size
1026     */

1027    public Dimension getPreferredSize() {
1028        return getPreferredSize(content);
1029    }
1030
1031    /**
1032     *
1033     * @throws java.lang.IllegalArgumentException
1034     */

1035    private Dimension getPreferredSize(Object JavaDoc component) {
1036        int width = getInteger(component, "width", 0);
1037        int height = getInteger(component, "height", 0);
1038        if ((width > 0) && (height > 0)) {
1039            return new Dimension(width, height);
1040        }
1041        String JavaDoc classname = getClass(component);
1042        if ("label" == classname) {
1043            return getSize(component, 0, 0);
1044        }
1045        if (("button" == classname) || ("togglebutton" == classname)) {
1046            boolean link = ("button" == classname) && (get(component, "type") == "link");
1047            return getSize(component, link ? 0 : 12, link ? 0 : 6);
1048        }
1049        if ("checkbox" == classname) {
1050            Dimension d = getSize(component, 0, 0);
1051            d.width = d.width + block + 3;
1052            d.height = Math.max(block, d.height);
1053            return d;
1054        }
1055        if ("combobox" == classname) {
1056            if (getBoolean(component, "editable", true)) {
1057                Dimension size = getFieldSize(component);
1058                Image icon = getIcon(component, "icon", null);
1059                if (icon != null) {
1060                    size.width += icon.getWidth(this);
1061                    size.height = Math.max(size.height, icon.getHeight(this) + 2);
1062                }
1063                size.width += block;
1064                return size;
1065            } else {
1066                // maximum size of current values and choices including 2-2-2-2 insets
1067
Dimension size = getSize(component, 4 , 4);
1068                for (Object JavaDoc item = get(component, ":comp"); item != null; item = get(item, ":next")) {
1069                    Dimension d = getSize(item, 4 , 4);
1070                    size.width = Math.max(d.width, size.width); size.height = Math.max(d.height, size.height);
1071                }
1072                size.width += block;
1073                if (size.height == 4) { // no content nor items, set text height
1074
Font customfont = (Font) get(component, "font");
1075                    FontMetrics fm = getFontMetrics((customfont != null) ? customfont : font);
1076                    size.height = fm.getAscent() + fm.getDescent() + 4;
1077                }
1078                return size;
1079            }
1080        }
1081        if (("textfield" == classname) || ("passwordfield" == classname)) {
1082            return getFieldSize(component);
1083        }
1084        if ("textarea" == classname) {
1085            int columns = getInteger(component, "columns", 0);
1086            int rows = getInteger(component, "rows", 0); // 'e' -> 'm' ?
1087
Font currentfont = (Font) get(component, "font");
1088            FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
1089            return new Dimension(
1090                ((columns > 0) ? (columns * fm.charWidth('e') + 2) : 76) + 2 + block,
1091                ((rows > 0) ? (rows * fm.getHeight() - fm.getLeading() + 2) : 76) + 2 + block);
1092        }
1093        if ("tabbedpane" == classname) {
1094            String JavaDoc placement = getString(component, "placement", "top");
1095            boolean horizontal = ((placement != "left") && (placement != "right"));
1096            int tabsize = 0; // max tab height (for horizontal),
1097
// max tabwidth (for vertical), or sum of tab heights for stacked
1098
int contentwidth = 0; int contentheight = 0; // max content size
1099
for (Object JavaDoc tab = get(component, ":comp");
1100                    tab != null; tab = get(tab, ":next")) {
1101                Dimension d = getSize(tab, 0, 0);
1102                if (placement == "stacked") { tabsize += d.height + 3; }
1103                else { tabsize = Math.max(tabsize, horizontal ? d.height + 5 : d.width + 9); }
1104                
1105                Object JavaDoc comp = get(tab, ":comp");
1106                if ((comp != null) && getBoolean(comp, "visible", true)) {
1107                    Dimension dc = getPreferredSize(comp);
1108                    contentwidth = Math.max(contentwidth, dc.width);
1109                    contentheight = Math.max(contentheight, dc.height);
1110                }
1111            }
1112            return new Dimension(contentwidth + (horizontal ? 4 : (tabsize + 3)),
1113                contentheight + (horizontal ? (tabsize + 3) : 4));
1114        }
1115        if (("panel" == classname) || (classname == "dialog")) {
1116            // title text and icon height
1117
Dimension size = getSize(component, 0, 0);
1118            // add border size
1119
if (classname == "dialog") {
1120                size.width = 8; size.height += 8; // title width neglected
1121
}
1122            else if (getBoolean(component, "border", false)) { // bordered panel
1123
size.width = 2; size.height += (size.height > 0) ? 1 : 2; // title includes line
1124
}
1125            else { size.width = 0; } // title width is clipped
1126
// add paddings
1127
size.width += getInteger(component, "left", 0) + getInteger(component, "right", 0);
1128            size.height += getInteger(component, "top", 0) + getInteger(component, "bottom", 0);
1129            // add content preferred size
1130
int gap = getInteger(component, "gap", 0);
1131            int[][] grid = getGrid(component);
1132            if (grid != null) { // has components
1133
size.width += getSum(grid[0], 0, grid[0].length, gap, false);
1134                size.height += getSum(grid[1], 0, grid[1].length, gap, false);
1135            }
1136            return size;
1137        }
1138        else if ("desktop" == classname) {
1139            Dimension size = new Dimension();
1140            for (Object JavaDoc comp = get(component, ":comp");
1141                    comp != null; comp = get(comp, ":next")) {
1142                String JavaDoc iclass = getClass(comp);
1143                if ((iclass != "dialog") && (iclass != ":popup") &&
1144                        (iclass != ":combolist")) {
1145                    Dimension d = getPreferredSize(comp);
1146                    size.width = Math.max(d.width, size.width);
1147                    size.height = Math.max(d.height, size.height);
1148                }
1149            }
1150            return size;
1151        }
1152        if ("spinbox" == classname) {
1153            Dimension size = getFieldSize(component);
1154            size.width += block;
1155            return size;
1156        }
1157        if ("progressbar" == classname) {
1158            boolean horizontal = ("vertical" != get(component, "orientation"));
1159            return new Dimension(horizontal ? 76 : 6, horizontal ? 6 : 76);
1160        }
1161        if ("slider" == classname) {
1162            boolean horizontal = ("vertical" != get(component, "orientation"));
1163            return new Dimension(horizontal ? 76 : 10, horizontal ? 10 : 76);
1164        }
1165        if ("splitpane" == classname) {
1166            boolean horizontal = ("vertical" != get(component, "orientation"));
1167            Object JavaDoc comp1 = get(component, ":comp");
1168            Dimension size = ((comp1 == null) || !getBoolean(comp1, "visible", true)) ?
1169                new Dimension() : getPreferredSize(comp1);
1170            Object JavaDoc comp2 = get(comp1, ":next");
1171            if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
1172                Dimension d = getPreferredSize(comp2);
1173                size.width = horizontal ? (size.width + d.width) :
1174                    Math.max(size.width, d.width);
1175                size.height = horizontal ? Math.max(size.height, d.height) :
1176                    (size.height + d.height);
1177            }
1178            if (horizontal) { size.width += 5; } else { size.height += 5; }
1179            return size;
1180        }
1181        if (("list" == classname) ||
1182                ("table" == classname) || ("tree" == classname)) {
1183            return new Dimension(76 + 2 + block, 76 + 2 + block);
1184        }
1185        if ("separator" == classname) {
1186            return new Dimension(1, 1);
1187        }
1188        if ("menubar" == classname) {
1189            Dimension size = new Dimension(0, 0);
1190            for (Object JavaDoc menu = get(component, ":comp");
1191                    menu != null; menu = get(menu, ":next")) {
1192                Dimension d = getSize(menu, 8, 4);
1193                size.width += d.width;
1194                size.height = Math.max(size.height, d.height);
1195            }
1196            return size;
1197        }
1198        if ("bean" == classname) {
1199                return ((Component) get(component, "bean")).getPreferredSize();
1200        }
1201        throw new IllegalArgumentException JavaDoc(classname);
1202    }
1203
1204    /**
1205     * @param component a container
1206     * @return null for zero visible subcomponent, otherwise an array contains the following lists:
1207     * <ul><li>columnwidths, preferred width of grid columns</li>
1208     * <li>rowheights, preferred heights of grid rows</li>
1209     * <li>columnweights, grid column-width weights</li>
1210     * <li>rowweights, grid row-height weights</li>
1211     * <li>gridx, horizontal location of the subcomponents</li>
1212     * <li>gridy, vertical locations</li>
1213     * <li>gridwidth, column spans</li>
1214     * <li>gridheight, row spans</li></ul>
1215     */

1216    private int[][] getGrid(Object JavaDoc component) {
1217        int count = 0; // count of the visible subcomponents
1218
for (Object JavaDoc comp = get(component, ":comp"); comp != null;
1219                comp = get(comp, ":next")) {
1220            if (getBoolean(comp, "visible", true)) { count++; }
1221        }
1222        if (count == 0) { return null; } // zero subcomponent
1223
int columns = getInteger(component, "columns", 0);
1224        int icols = (columns != 0) ? columns : count;
1225        int irows = (columns != 0) ? ((count + columns - 1) / columns) : 1;
1226        int[][] grid = {
1227            new int[icols], new int[irows], // columnwidths, rowheights
1228
new int[icols], new int[irows], // columnweights, rowweights
1229
new int[count], new int[count], // gridx, gridy
1230
new int[count], new int[count] }; // gridwidth, gridheight
1231
int[] columnheight = new int[icols];
1232        int[][] cache = null; // preferredwidth, height, columnweight, rowweight
1233

1234        int i = 0; int x = 0; int y = 0;
1235        int nextsize = 0;
1236        for (Object JavaDoc comp = get(component, ":comp");
1237                comp != null; comp = get(comp, ":next")) {
1238            if (!getBoolean(comp, "visible", true)) { continue; }
1239            int colspan = ((columns != 0) && (columns < count)) ?
1240                Math.min(getInteger(comp, "colspan", 1), columns) : 1;
1241            int rowspan = (columns != 1) ? getInteger(comp, "rowspan", 1) : 1;
1242            
1243            for (int j = 0; j < colspan; j++) {
1244                if ((columns != 0) && (x + colspan > columns)) {
1245                    x = 0; y++; j = -1;
1246                }
1247                else if (columnheight[x + j] > y) {
1248                    x += (j + 1); j = -1;
1249                }
1250            }
1251            if (y + rowspan > grid[1].length) {
1252                int[] rowheights = new int[y + rowspan];
1253                System.arraycopy(grid[1], 0, rowheights, 0, grid[1].length);
1254                grid[1] = rowheights;
1255                int[] rowweights = new int[y + rowspan];
1256                System.arraycopy(grid[3], 0, rowweights, 0, grid[3].length);
1257                grid[3] = rowweights;
1258            }
1259            for (int j = 0; j < colspan; j++) {
1260                columnheight[x + j] = y + rowspan;
1261            }
1262
1263            int weightx = getInteger(comp, "weightx", 0);
1264            int weighty = getInteger(comp, "weighty", 0);
1265            Dimension d = getPreferredSize(comp);
1266
1267            if (colspan == 1) {
1268                grid[0][x] = Math.max(grid[0][x], d.width); // columnwidths
1269
grid[2][x] = Math.max(grid[2][x], weightx); // columnweights
1270
}
1271            else {
1272                if (cache == null) { cache = new int[4][count]; }
1273                cache[0][i] = d.width;
1274                cache[2][i] = weightx;
1275                if ((nextsize == 0) || (colspan < nextsize)) { nextsize = colspan; }
1276            }
1277            if (rowspan == 1) {
1278                grid[1][y] = Math.max(grid[1][y], d.height); // rowheights
1279
grid[3][y] = Math.max(grid[3][y], weighty); // rowweights
1280
}
1281            else {
1282                if (cache == null) { cache = new int[4][count]; }
1283                cache[1][i] = d.height;
1284                cache[3][i] = weighty;
1285                if ((nextsize == 0) || (rowspan < nextsize)) { nextsize = rowspan; }
1286            }
1287            grid[4][i] = x; //gridx
1288
grid[5][i] = y; //gridy
1289
grid[6][i] = colspan; //gridwidth
1290
grid[7][i] = rowspan; //gridheight
1291

1292            x += colspan;
1293            i++;
1294        }
1295
1296        while (nextsize != 0) {
1297            int size = nextsize; nextsize = 0;
1298            for (int j = 0; j < 2; j++) { // horizontal, vertical
1299
for (int k = 0; k < count; k++) {
1300                    if (grid[6 + j][k] == size) { // gridwidth, gridheight
1301
int gridpoint = grid[4 + j][k]; // gridx, gridy
1302

1303                        int weightdiff = cache[2 + j][k];
1304                        for (int m = 0; (weightdiff > 0) && (m < size); m++) {
1305                            weightdiff -= grid[2 + j][gridpoint + m];
1306                        }
1307                        if (weightdiff > 0) {
1308                            int weightsum = cache[2 + j][k] - weightdiff;
1309                            for (int m = 0; (weightsum > 0) && (m < size); m++) {
1310                                int weight = grid[2 + j][gridpoint + m];
1311                                if (weight > 0) {
1312                                    int weightinc = weight * weightdiff / weightsum;
1313                                    grid[2 + j][gridpoint + m] += weightinc;
1314                                    weightdiff -= weightinc;
1315                                    weightsum -= weightinc;
1316                                }
1317                            }
1318                            grid[2 + j][gridpoint + size - 1] += weightdiff;
1319                        }
1320
1321                        int sizediff = cache[j][k];
1322                        int weightsum = 0;
1323                        for (int m = 0; (sizediff > 0) && (m < size); m++) {
1324                            sizediff -= grid[j][gridpoint + m];
1325                            weightsum += grid[2 + j][gridpoint + m];
1326                        }
1327                        if (sizediff > 0) {
1328                            for (int m = 0; (weightsum > 0) && (m < size); m++) {
1329                                int weight = grid[2 + j][gridpoint + m];
1330                                if (weight > 0) {
1331                                    int sizeinc = weight * sizediff / weightsum;
1332                                    grid[j][gridpoint + m] += sizeinc;
1333                                    sizediff -= sizeinc;
1334                                    weightsum -= weight;
1335                                }
1336                            }
1337                            grid[j][gridpoint + size - 1] += sizediff;
1338                        }
1339                    }
1340                    else if ((grid[6 + j][k] > size) &&
1341                            ((nextsize == 0) || (grid[6 + j][k] < nextsize))) {
1342                        nextsize = grid[6 + j][k];
1343                    }
1344                }
1345            }
1346        }
1347        return grid;
1348    }
1349
1350    /**
1351     *
1352     */

1353    private int getSum(int[] values,
1354            int from, int length, int gap, boolean last) {
1355        if (length <= 0) { return 0; }
1356        int value = 0;
1357        for (int i = 0; i < length; i++) {
1358            value += values[from + i];
1359        }
1360        return value + (length - (last ? 0 : 1)) * gap;
1361    }
1362
1363    /**
1364     *
1365     */

1366    private Dimension getFieldSize(Object JavaDoc component) {
1367        int columns = getInteger(component, "columns", 0);
1368        Font currentfont = (Font) get(component, "font");
1369        FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
1370        return new Dimension(((columns > 0) ?
1371            (columns * fm.charWidth('e')) : 76) + 4,
1372            fm.getAscent() + fm.getDescent() + 4); // fm.stringWidth(text)
1373
}
1374
1375    /**
1376     * @param component a widget including the text and icon parameters
1377     * @param dx increase width by this value
1378     * @param dy increase height by this value
1379     * @return size of the text and the image (plus a gap) including the given offsets
1380     */

1381    private Dimension getSize(Object JavaDoc component, int dx, int dy) {
1382        String JavaDoc text = getString(component, "text", null);
1383        int tw = 0; int th = 0;
1384        if (text != null) {
1385            Font customfont = (Font) get(component, "font");
1386            FontMetrics fm = getFontMetrics((customfont != null) ? customfont : font);
1387            tw = fm.stringWidth(text);
1388            th = fm.getAscent() + fm.getDescent();
1389        }
1390        Image icon = getIcon(component, "icon", null);
1391        int iw = 0; int ih = 0;
1392        if (icon != null) {
1393            iw = icon.getWidth(this);
1394            ih = icon.getHeight(this);
1395            if (text != null) { iw += 2; }
1396        }
1397        return new Dimension(tw + iw + dx, Math.max(th, ih) + dy);
1398    }
1399
1400    /**
1401     * Invokes the paint method
1402     */

1403    public void update(Graphics g) {
1404        paint(g);
1405    }
1406
1407    /*public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
1408        if (infoflags == ImageObserver.ALLBITS) {
1409            validate(content);
1410        }
1411        return super.imageUpdate(img, infoflags, x, y, width, height);
1412    }*/

1413
1414    /**
1415     * Paints the components inside the graphics clip area
1416     */

1417    public void paint(Graphics g) {
1418        g.setFont(font);
1419        if (hgradient == null) {
1420            int[][] pix = new int[2][block * block];
1421            int r1 = c_bg.getRed(); int r2 = c_press.getRed();
1422            int g1 = c_bg.getGreen(); int g2 = c_press.getGreen();
1423            int b1 = c_bg.getBlue(); int b2 = c_press.getBlue();
1424            for (int i = 0; i < block; i++) {
1425                int cr = r1 - (r1 - r2) * i / block;
1426                int cg = g1 - (g1 - g2) * i / block;
1427                int cb = b1 - (b1 - b2) * i / block;
1428                int color = (255 << 24) | (cr << 16) | (cg << 8) | cb;
1429                for (int j = 0; j < block; j++) {
1430                    pix[0][i * block + j] = color;
1431                    pix[1][j * block + i] = color;
1432                }
1433            }
1434            hgradient = createImage(new MemoryImageSource(block, block, pix[0], 0, block));
1435            vgradient = createImage(new MemoryImageSource(block, block, pix[1], 0, block));
1436        }
1437        //g.setColor(Color.orange);
1438
//g.fillRect(0, 0, getSize().width, getSize().height);
1439
//long time = System.currentTimeMillis();
1440
Rectangle clip = g.getClipBounds();
1441        ///dg.setClip(r.x, r.y, r.width, r.height);
1442
paint(g, clip.x, clip.y, clip.width, clip.height, content, isEnabled());
1443        //System.out.println(System.currentTimeMillis() - time);
1444
//g.setClip(0, 0, getSize().width, getSize().height);
1445
//g.setColor(Color.red); g.drawRect(clip.x, clip.y, clip.width - 1, clip.height - 1);
1446
}
1447
1448    /**
1449     * @param clipx the cliping rectangle is relative to the component's
1450     * parent location similar to the component's bounds rectangle
1451     * @param clipy
1452     * @param clipwidth
1453     * @param clipheight
1454     * @throws java.lang.IllegalArgumentException
1455     */

1456    private void paint(Graphics g,
1457            int clipx, int clipy, int clipwidth, int clipheight,
1458            Object JavaDoc component, boolean enabled) {
1459        if (!getBoolean(component, "visible", true)) { return; }
1460        Rectangle bounds = getRectangle(component, "bounds");
1461        if (bounds == null) { return; }
1462        // negative component width indicates invalid component layout
1463
if (bounds.width < 0) {
1464            bounds.width = Math.abs(bounds.width);
1465            doLayout(component);
1466        }
1467        // return if the component was out of the cliping rectangle
1468
if ((clipx + clipwidth < bounds.x) ||
1469                (clipx > bounds.x + bounds.width) ||
1470                (clipy + clipheight < bounds.y) ||
1471                (clipy > bounds.y + bounds.height)) {
1472            return;
1473        }
1474        // set the clip rectangle relative to the component location
1475
clipx -= bounds.x; clipy -= bounds.y;
1476        g.translate(bounds.x, bounds.y);
1477        //g.setClip(0, 0, bounds.width, bounds.height);
1478
String JavaDoc classname = getClass(component);
1479        boolean pressed = (mousepressed == component);
1480        boolean inside = (mouseinside == component) &&
1481            ((mousepressed == null) || pressed);
1482        boolean focus = focusinside && (focusowner == component);
1483        enabled = getBoolean(component, "enabled", true); //enabled &&
1484

1485        if ("label" == classname) {
1486            paint(component, 0, 0, bounds.width, bounds.height,
1487                g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1488                0, 0, 0, 0, false, enabled ? 'e' : 'd', "left", true, false);
1489        }
1490        else if (("button" == classname) || ("togglebutton" == classname)) {
1491            boolean toggled = ("togglebutton" == classname) && getBoolean(component, "selected", false);
1492            boolean link = ("button" == classname) && (get(component, "type") == "link");
1493            if (link) {
1494                paint(component, 0, 0, bounds.width, bounds.height,
1495                    g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1496                    0, 0, 0, 0, focus, enabled ? (pressed ? 'e' : 'l') : 'd', "center",
1497                    true, enabled && (inside != pressed));
1498            } else { // disabled toggled
1499
char mode = enabled ? ((inside != pressed) ? 'h' : ((pressed || toggled) ? 'p' : 'g')) : 'd';
1500                paint(component, 0, 0, bounds.width, bounds.height,
1501                    g, clipx, clipy, clipwidth, clipheight, true, true, true, true,
1502                    2, 5, 2, 5, focus, mode, "center", true, false);
1503                //(enabled && ("button" == classname) && get(component, "type") == "default")...
1504
}
1505        }
1506        else if ("checkbox" == classname) {
1507            paint(component, 0, 0, bounds.width, bounds.height,
1508                g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1509                0, block + 3, 0, 0, false, enabled ? 'e' : 'd', "left", true, false);
1510
1511            boolean selected = getBoolean(component, "selected", false);
1512            String JavaDoc group = getString(component, "group", null);
1513            Color border = enabled ? c_border : c_disable;
1514            Color foreground = enabled ? ((inside != pressed) ? c_hover :
1515                (pressed ? c_press : c_ctrl)) : c_bg;
1516            int dy = (bounds.height - block + 2) / 2;
1517            if (group == null) {
1518                paintRect(g, 1, dy + 1, block - 2, block - 2,
1519                    border, foreground, true, true, true, true, true);
1520            } else {
1521                g.setColor((foreground != c_ctrl) ? foreground : c_bg);
1522                g.fillOval(1, dy + 1, block - 3 + evm, block - 3 + evm);
1523                g.setColor(border);
1524                g.drawOval(1, dy + 1, block - 3, block - 3);
1525            }
1526            if (focus) {
1527                g.setColor(c_focus);
1528                if (group == null) {
1529                    g.drawRect(3, dy + 3, block - 7, block - 7);
1530                } else {
1531                    g.drawOval(3, dy + 3, block - 7, block - 7);
1532                }
1533            }
1534            if((!selected && inside && pressed) ||
1535                    (selected && (!inside || !pressed))) {
1536                g.setColor(enabled ? c_text : c_disable);
1537                if (group == null) {
1538                    g.fillRect(3, dy + block - 9, 2 + evm, 6 + evm);
1539                    g.drawLine(3, dy + block - 4, block - 4, dy + 3);
1540                    g.drawLine(4, dy + block - 4, block - 4, dy + 4);
1541                } else {
1542                    g.fillOval(5, dy + 5, block - 10 + evm, block - 10 + evm);
1543                    g.drawOval(4, dy + 4, block - 9, block - 9);
1544                }
1545            }
1546        }
1547        else if ("combobox" == classname) {
1548            if (getBoolean(component, "editable", true)) {
1549                Image icon = getIcon(component, "icon", null);
1550                int left = (icon != null) ? icon.getWidth(this) : 0;
1551                paintField(g, clipx, clipy, clipwidth, clipheight, component,
1552                    bounds.width - block, bounds.height, focus, enabled, false, left);
1553                if (icon != null) {
1554                    g.drawImage(icon, 2, (bounds.height - icon.getHeight(this)) / 2, this);
1555                }
1556                paintArrow(g, bounds.width - block, 0, block, bounds.height,
1557                    'S', enabled, inside, pressed, "down", true, false, true, true, true);
1558            } else {
1559                paint(component, 0, 0, bounds.width, bounds.height,
1560                    g, clipx, clipy, clipwidth, clipheight,
1561                    true, true, true, true, 1, 1, 1, 1 + block, focus,
1562                    enabled ? ((inside != pressed) ? 'h' : (pressed ? 'p' : 'g')) : 'd',
1563                    "left", false, false);
1564                g.setColor(enabled ? c_text : c_disable);
1565                paintArrow(g, bounds.width - block, 0, block, bounds.height, 'S');
1566            }
1567        }
1568        else if (":combolist" == classname) {
1569            paintScroll(component, classname, pressed, inside, focus, enabled,
1570                g, clipx, clipy, clipwidth, clipheight);
1571        }
1572        else if (("textfield" == classname) || ("passwordfield" == classname)) {
1573            paintField(g, clipx, clipy, clipwidth, clipheight, component,
1574                bounds.width, bounds.height, focus, enabled, ("passwordfield" == classname), 0);
1575        }
1576        else if ("textarea" == classname) {
1577            paintScroll(component, classname, pressed, inside, focus, enabled,
1578                g, clipx, clipy, clipwidth, clipheight);
1579        }
1580        else if ("tabbedpane" == classname) {
1581            int i = 0; Object JavaDoc selectedtab = null;
1582            int selected = getInteger(component, "selected", 0);
1583            String JavaDoc placement = getString(component, "placement", "top");
1584            boolean horizontal = ((placement == "top") || (placement == "bottom"));
1585            boolean stacked = (placement == "stacked");
1586            int bx = stacked ? 0 : horizontal ? 2 : 1, by = stacked ? 0 : horizontal ? 1 : 2,
1587                bw = 2 * bx, bh = 2 * by;
1588            // paint tabs except the selected one
1589
g.clipRect(0, 0, bounds.width, bounds.height); //+clip
1590
for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
1591                Rectangle r = getRectangle(tab, "bounds");
1592                if (selected != i) {
1593                    boolean hover = inside && (mousepressed == null) && (insidepart == tab);
1594                    boolean tabenabled = enabled && getBoolean(tab, "enabled", true);
1595                    paint(tab, r.x + bx, r.y + by, r.width - bw, r.height - bh,
1596                        g, clipx, clipy, clipwidth, clipheight,
1597                        (placement != "bottom"), (placement != "right"),
1598                        !stacked && (placement != "top"), (placement != "left"),
1599                        1, 3, 1, 3, false, tabenabled ? (hover ? 'h' : 'g') : 'd', "left", true, false);
1600                                        
1601                } else {
1602                    selectedtab = tab;
1603                    // paint tabbedpane border
1604
paint(tab, (placement == "left") ? r.width - 1 : 0,
1605                        stacked ? (r.y + r.height - 1) : (placement == "top") ? r.height - 1 : 0,
1606                        (horizontal || stacked) ? bounds.width : (bounds.width - r.width + 1),
1607                        stacked ? (bounds.height - r.y - r.height + 1) :
1608                        horizontal ? (bounds.height - r.height + 1) : bounds.height,
1609                        g, true, true, true, true, enabled ? 'e' : 'd');
1610                    Object JavaDoc comp = get(selectedtab, ":comp");
1611                    if ((comp != null) && getBoolean(comp, "visible", true)) {
1612                        clipx -= r.x; clipy -= r.y; g.translate(r.x, r.y); // relative to tab
1613
paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
1614                        clipx += r.x; clipy += r.y; g.translate(-r.x, -r.y);
1615                    }
1616                }
1617                i++;
1618            }
1619            
1620            // paint selected tab and its content
1621
if (selectedtab != null) {
1622                Rectangle r = getRectangle(selectedtab, "bounds");
1623                // paint selected tab
1624
int ph = stacked ? 3 : (horizontal ? 5 : 4);
1625                int pv = stacked ? 1 : (horizontal ? 2 : 3);
1626                paint(selectedtab, r.x, r.y, r.width, r.height,
1627                    g, clipx, clipy, clipwidth, clipheight,
1628                    (placement != "bottom"), (placement != "right"),
1629                    !stacked && (placement != "top"), (placement != "left"),
1630                    pv, ph, pv, ph, focus, enabled ? 'b' : 'i', "left", true, false);
1631            }
1632            g.setClip(clipx, clipy, clipwidth, clipheight); //+clip
1633
}
1634        else if (("panel" == classname) || ("dialog" == classname)) {
1635            int titleheight = getInteger(component, ":titleheight", 0);
1636            if ("dialog" == classname) {
1637                paint(component, 0, 0, bounds.width, 3 + titleheight,
1638                    g, clipx, clipy, clipwidth, clipheight, true, true, false, true,
1639                    1, 2, 1, 2, false, 'g', "left", false, false);
1640                int controlx = bounds.width - titleheight - 1;
1641                if (getBoolean(component, "closable", false)) {
1642                    paint(component, g, controlx, 3, titleheight - 2, titleheight - 2, 'c');
1643                    controlx -= titleheight;
1644                }
1645                if (getBoolean(component, "maximizable", false)) {
1646                    paint(component, g, controlx, 3, titleheight - 2, titleheight - 2, 'm');
1647                    controlx -= titleheight;
1648                }
1649                if (getBoolean(component, "iconifiable", false)) {
1650                    paint(component, g, controlx, 3, titleheight - 2, titleheight - 2, 'i');
1651                }
1652                paintRect(g, 0, 3 + titleheight, bounds.width, bounds.height - 3 - titleheight,
1653                    c_border, c_press, false, true, true, true, true); // lower part excluding titlebar
1654
paint(component, // content area
1655
3, 3 + titleheight, bounds.width - 6, bounds.height - 6 - titleheight,
1656                    g, true, true, true, true, 'b');
1657            } else { // panel
1658
boolean border = getBoolean(component, "border", false);
1659                paint(component, 0, titleheight / 2, bounds.width, bounds.height - (titleheight / 2),
1660                    g, border, border, border, border, enabled ? 'e' : 'd');
1661                paint(component, 0, 0, bounds.width, titleheight, // panel title
1662
g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1663                    0, 3, 0, 3, false, enabled ? 'x' : 'd', "left", false, false);
1664            }
1665            
1666            if (get(component, ":port") != null) {
1667                paintScroll(component, classname, pressed, inside, focus, enabled,
1668                    g, clipx, clipy, clipwidth, clipheight);
1669            }
1670            else {
1671                for (Object JavaDoc comp = get(component, ":comp");
1672                        comp != null; comp = get(comp, ":next")) {
1673                    paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
1674                }
1675            }
1676        }
1677        else if ("desktop" == classname) {
1678            paintRect(g, 0, 0, bounds.width, bounds.height,
1679                c_border, c_bg, false, false, false, false, true);
1680            paintReverse(g, clipx, clipy, clipwidth, clipheight,
1681                get(component, ":comp"), enabled);
1682            //g.setColor(Color.red); if (clip != null) g.drawRect(clipx, clipy, clipwidth, clipheight);
1683
if ((tooltipowner != null) && (component == content)) {
1684                Rectangle r = getRectangle(tooltipowner, ":tooltipbounds");
1685                paintRect(g, r.x, r.y, r.width, r.height,
1686                    c_border, c_bg, true, true, true, true, true);
1687                String JavaDoc text = getString(tooltipowner, "tooltip", null);
1688                g.setColor(c_text);
1689                g.drawString(text, r.x + 2, r.y + g.getFontMetrics().getAscent() + 2); //+nullpointerexception
1690
}
1691        }
1692        else if ("spinbox" == classname) {
1693            paintField(g, clipx, clipy, clipwidth, clipheight, component,
1694                bounds.width - block, bounds.height, focus, enabled, false, 0);
1695            paintArrow(g, bounds.width - block, 0, block, bounds.height / 2,
1696                    'N', enabled, inside, pressed, "up", true, false, false, true, true);
1697            paintArrow(g, bounds.width - block, bounds.height / 2,
1698                block, bounds.height - (bounds.height / 2),
1699                'S', enabled, inside, pressed, "down", true, false, true, true, true);
1700        }
1701        else if ("progressbar" == classname) {
1702            int minimum = getInteger(component, "minimum", 0);
1703            int maximum = getInteger(component, "maximum", 100);
1704            int value = getInteger(component, "value", 0);
1705            boolean horizontal = ("vertical" != get(component, "orientation"));
1706            int length = (value - minimum) *
1707                ((horizontal ? bounds.width : bounds.height) - 1) / (maximum - minimum);
1708            paintRect(g, 0, 0, horizontal ? length : bounds.width,
1709                horizontal ? bounds.height : length, enabled ? c_border : c_disable,
1710                c_select, true, true, horizontal, !horizontal, true);
1711            paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
1712                horizontal ? (bounds.width - length) : bounds.width ,
1713                horizontal ? bounds.height : (bounds.height - length),
1714                enabled ? c_border : c_disable, c_bg, true, true, true, true, true);
1715        }
1716        else if ("slider" == classname) {
1717            int minimum = getInteger(component, "minimum", 0);
1718            int maximum = getInteger(component, "maximum", 100);
1719            int value = getInteger(component, "value", 0);
1720            boolean horizontal = ("vertical" != get(component, "orientation"));
1721            int length = (value - minimum) *
1722                ((horizontal ? bounds.width : bounds.height) - block) /
1723                (maximum - minimum);
1724            paintRect(g, horizontal ? 0 : 3, horizontal ? 3 : 0,
1725                horizontal ? length : (bounds.width - 6),
1726                horizontal ? (bounds.height - 6) : length,
1727                enabled ? c_border : c_disable,
1728                c_bg, true, true, horizontal, !horizontal, true);
1729            paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
1730                horizontal ? block : bounds.width, horizontal ? bounds.height : block,
1731                enabled ? c_border : c_disable,
1732                enabled ? c_ctrl : c_bg, true, true, true, true, true);
1733            if (focus) {
1734                g.setColor(c_focus);
1735                g.drawRect(horizontal ? (length + 2) : 2, horizontal ? 2 : (length + 2),
1736                    (horizontal ? block : bounds.width) - 5,
1737                    (horizontal ? bounds.height : block) - 5);
1738                //g.drawRect(length + 1, 1, block - 3, bounds.height - 3);
1739
}
1740            paintRect(g, horizontal ? (block + length) : 3,
1741                horizontal ? 3 : (block + length),
1742                bounds.width - (horizontal ? (block + length) : 6),
1743                bounds.height - (horizontal ? 6 : (block + length)),
1744                enabled ? c_border : c_disable,
1745                c_bg, horizontal, !horizontal, true, true, true);
1746        }
1747        else if ("splitpane" == classname) {
1748            boolean horizontal = ("vertical" != get(component, "orientation"));
1749            int divider = getInteger(component, "divider", -1);
1750            paintRect(g, horizontal ? divider : 0, horizontal ? 0 : divider,
1751                horizontal ? 5 : bounds.width, horizontal ? bounds.height : 5,
1752                c_border, c_bg, false, false, false, false, true);
1753            g.setColor(enabled ? (focus ? c_focus : c_border) : c_disable);
1754            int xy = horizontal ? bounds.height : bounds.width;
1755            int xy1 = Math.max(0, xy / 2 - 12);
1756            int xy2 = Math.min(xy / 2 + 12, xy - 1);
1757            for (int i = divider + 1; i < divider + 4; i += 2) {
1758                if (horizontal) { g.drawLine(i, xy1, i, xy2); }
1759                    else { g.drawLine(xy1, i, xy2, i); }
1760            }
1761            Object JavaDoc comp1 = get(component, ":comp");
1762            if (comp1 != null) {
1763                paint(g, clipx, clipy, clipwidth, clipheight, comp1, enabled);
1764                Object JavaDoc comp2 = get(comp1, ":next");
1765                if (comp2 != null) {
1766                    paint(g, clipx, clipy, clipwidth, clipheight, comp2, enabled);
1767                }
1768            }
1769        }
1770        else if (("list" == classname) ||
1771                ("table" == classname) || ("tree" == classname)) {
1772            paintScroll(component, classname, pressed, inside, focus, enabled,
1773                g, clipx, clipy, clipwidth, clipheight);
1774        }
1775        else if ("separator" == classname) {
1776            g.setColor(enabled ? c_border : c_disable);
1777            g.fillRect(0, 0, bounds.width + evm, bounds.height + evm);
1778        }
1779        else if ("menubar" == classname) {
1780            Object JavaDoc selected = get(component, "selected");
1781            boolean bottom = ("bottom" == get(component, "placement"));
1782            int lastx = 0;
1783            for (Object JavaDoc menu = get(component, ":comp");
1784                    menu != null; menu = get(menu, ":next")) {
1785                Rectangle mb = getRectangle(menu, "bounds");
1786                if (clipx + clipwidth <= mb.x) { break; }
1787                if (clipx >= mb.x + mb.width) { continue; }
1788                boolean menuenabled = enabled && getBoolean(menu, "enabled", true);
1789                boolean armed = (selected == menu);
1790                boolean hoover = (selected == null) && (insidepart == menu);
1791                paint(menu, mb.x, 0, mb.width, bounds.height,
1792                    g, clipx, clipy, clipwidth, clipheight, // TODO disabled
1793
bottom || armed, armed, !bottom || armed, armed, 1, 3, 1, 3, false,
1794                    enabled ? (menuenabled ? (armed ? 's' : (hoover ? 'h' : 'g')) : 'r') : 'd', "left", true, false);
1795                lastx = mb.x + mb.width;
1796            }
1797            paintRect(g, lastx, 0, bounds.width - lastx, bounds.height,
1798                enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg,
1799                bottom, false, !bottom, false, true);
1800        }
1801        else if (":popup" == classname) {
1802            paintRect(g, 0, 0, bounds.width, bounds.height,
1803                c_border, c_textbg, true, true, true, true, true);
1804            Object JavaDoc selected = get(component, "selected");
1805            for (Object JavaDoc menu = get(get(component, "menu"), ":comp");
1806                    menu != null; menu = get(menu, ":next")) {
1807                Rectangle r = getRectangle(menu, "bounds");
1808                if (clipy + clipheight <= r.y) { break; }
1809                if (clipy >= r.y + r.height) { continue; }
1810                String JavaDoc itemclass = getClass(menu);
1811                if (itemclass == "separator") {
1812                    g.setColor(c_border);
1813                    g.fillRect(r.x, r.y, bounds.width - 2 + evm, r.height + evm);
1814                } else {
1815                    boolean armed = (selected == menu);
1816                    boolean menuenabled = getBoolean(menu, "enabled", true);
1817                    paint(menu, r.x, r.y, bounds.width - 2, r.height,
1818                        g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1819                        2, (itemclass == "checkboxmenuitem") ? (block + 7) : 4, 2, 4, false,
1820                        menuenabled ? (armed ? 's' : 't') : 'd', "left", true, false);
1821                    if (itemclass == "checkboxmenuitem") {
1822                        boolean checked = getBoolean(menu, "selected", false);
1823                        String JavaDoc group = getString(menu, "group", null);
1824                        g.translate(r.x + 4, r.y + 2);
1825                        g.setColor(menuenabled ? c_border : c_disable);
1826                        if (group == null) {
1827                            g.drawRect(1, 1, block - 3, block - 3);
1828                        } else {
1829                            g.drawOval(1, 1, block - 3, block - 3);
1830                        }
1831                        if (checked) {
1832                            g.setColor(menuenabled ? c_text : c_disable);
1833                            if (group == null) {
1834                                g.fillRect(3, block - 9, 2 + evm, 6 + evm);
1835                                g.drawLine(3, block - 4, block - 4, 3);
1836                                g.drawLine(4, block - 4, block - 4, 4);
1837                            } else {
1838                                g.fillOval(5, 5, block - 10 + evm, block - 10 + evm);
1839                                g.drawOval(4, 4, block - 9, block - 9);
1840                            }
1841                        }
1842                        g.translate(-r.x - 4, -r.y - 2);
1843                    }
1844                    if (itemclass == "menu") {
1845                        paintArrow(g, r.x + bounds.width - block, r.y, block, r.height, 'E');
1846                    }
1847                    else {
1848                        String JavaDoc accelerator = getAccelerator(menu);
1849                        if (accelerator != null) { //TODO
1850
g.drawString(accelerator, bounds.width - 4 -
1851                                getFontMetrics(font).stringWidth(accelerator), r.y + 2 + 10);
1852                        }
1853                    }
1854                }
1855            }
1856        }
1857        else if ("bean" == classname) {
1858                g.clipRect(0, 0, bounds.width, bounds.height);
1859                ((Component) get(component, "bean")).paint(g);
1860                g.setClip(clipx, clipy, clipwidth, clipheight);
1861        }
1862        else throw new IllegalArgumentException JavaDoc(classname);
1863        g.translate(-bounds.x, -bounds.y);
1864        clipx += bounds.x; clipy += bounds.y;
1865    }
1866
1867    /**
1868     *
1869     */

1870    private void paintReverse(Graphics g,
1871            int clipx, int clipy, int clipwidth, int clipheight,
1872            Object JavaDoc component, boolean enabled) {
1873        if (component != null) {
1874            Rectangle bounds = getRectangle(component, "bounds");
1875            if ((clipx < bounds.x) ||
1876                    (clipx + clipwidth > bounds.x + bounds.width) ||
1877                    (clipy < bounds.y) ||
1878                    (clipy + clipheight > bounds.y + bounds.height)) {
1879                paintReverse(g, clipx, clipy, clipwidth, clipheight,
1880                    get(component, ":next"), enabled);
1881            }
1882            paint(g, clipx, clipy, clipwidth, clipheight, component, enabled);
1883        }
1884    }
1885
1886    /**
1887     *
1888     */

1889    private void paintField(Graphics g,
1890            int clipx, int clipy, int clipwidth, int clipheight, Object JavaDoc component,
1891            int width, int height,
1892            boolean focus, boolean enabled, boolean hidden, int left) {
1893        boolean editable = getBoolean(component, "editable", true);
1894        paintRect(g, 0, 0, width, height, enabled ? c_border : c_disable,
1895            editable ? c_textbg : c_bg, true, true, true, true, true);
1896        g.clipRect(1 + left, 1, width - left - 2, height - 2);
1897
1898        String JavaDoc text = getString(component, "text", "");
1899        int offset = getInteger(component, ":offset", 0);
1900        Font currentfont = (Font) get(component, "font");
1901        if (currentfont != null) { g.setFont(currentfont); }
1902        FontMetrics fm = g.getFontMetrics();
1903
1904        int caret = 0;
1905        if (focus) {
1906            int start = getInteger(component, "start", 0);
1907            int end = getInteger(component, "end", 0);
1908            caret = hidden ? (fm.charWidth('*') * end) :
1909                fm.stringWidth(text.substring(0, end));
1910            if (start != end) {
1911                int is = hidden ? (fm.charWidth('*') * start) :
1912                    fm.stringWidth(text.substring(0, start));
1913                g.setColor(c_select);
1914                g.fillRect(2 + left - offset + Math.min(is, caret), 1,
1915                    Math.abs(caret - is) + evm, height - 2 + evm);
1916            }
1917        }
1918
1919        if (focus) {
1920            g.setColor(c_focus);
1921            g.fillRect(1 + left - offset + caret, 1, 1 + evm, height - 2 + evm);
1922        }
1923
1924        g.setColor(enabled ? c_text : c_disable);
1925        int fx = 2 + left - offset;
1926        int fy = (height + fm.getAscent() - fm.getDescent()) / 2;
1927        if (hidden) {
1928            int fh = fm.charWidth('*');
1929            for (int i = text.length(); i > 0; i--) {
1930                g.drawString("*", fx, fy);
1931                fx += fh;
1932            }
1933        } else {
1934            g.drawString(text, fx, fy);
1935        }
1936        if (currentfont != null) { g.setFont(font); }
1937        g.setClip(clipx, clipy, clipwidth, clipheight);
1938    }
1939    
1940    /**
1941     * @param component scrollable widget
1942     * @param classname
1943     * @param pressed
1944     * @param inside
1945     * @param focus
1946     * @param enabled
1947     * @param g grahics context
1948     * @param clipx current cliping x location relative to the component
1949     * @param clipy y location of the cliping area relative to the component
1950     * @param clipwidth width of the cliping area
1951     * @param clipheight height of the cliping area
1952     * @param header column height
1953     * @param topborder bordered on the top if true
1954     * @param border define left, bottom, and right border if true
1955     */

1956    private void paintScroll(Object JavaDoc component, String JavaDoc classname,
1957            boolean pressed, boolean inside, boolean focus, boolean enabled,
1958            Graphics g, int clipx, int clipy, int clipwidth, int clipheight) {
1959        Rectangle port = getRectangle(component, ":port");
1960        Rectangle horizontal = getRectangle(component, ":horizontal");
1961        Rectangle vertical = getRectangle(component, ":vertical");
1962        Rectangle view = getRectangle(component, ":view");
1963        
1964        if (horizontal != null) { // paint horizontal scrollbar
1965
int x = horizontal.x; int y = horizontal.y; int width = horizontal.width; int height = horizontal.height;
1966            paintArrow(g, x, y, block, height,
1967                'W', enabled, inside, pressed, "left", true, true, true, false, true);
1968            paintArrow(g, x + width - block, y, block, height,
1969                'E', enabled, inside, pressed, "right", true, false, true, true, true);
1970                
1971            int track = width - (2 * block);
1972            if (track < 10) {
1973                paintRect(g, x + block, y, track, height,
1974                    enabled ? c_border : c_disable, c_bg, true, true, true, true, true);
1975            }
1976            else {
1977                int knob = Math.max(track * port.width / view.width, 10);
1978                int decrease = view.x * (track - knob) / (view.width - port.width);
1979                paintRect(g, x + block, y, decrease, height,
1980                    enabled ? c_border : c_disable, c_bg, false, true, true, false, true);
1981                paintRect(g, x + block + decrease, y, knob, height,
1982                    enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, true, true, true, true);
1983                int n = Math.min(5, (knob - 4) / 3);
1984                g.setColor(enabled ? c_border : c_disable);
1985                int cx = (x + block + decrease) + (knob + 2 - n * 3) / 2;
1986                for (int i = 0; i < n; i++ ) {
1987                    g.drawLine(cx + i * 3, y + 3, cx + i * 3, y + height - 5);
1988                }
1989                int increase = track - decrease - knob;
1990                paintRect(g, x + block + decrease + knob, y, increase, height,
1991                    enabled ? c_border : c_disable, c_bg, false, false, true, true, true);
1992            }
1993        }
1994            
1995        if (vertical != null) { // paint vertical scrollbar
1996
int x = vertical.x; int y = vertical.y; int width = vertical.width; int height = vertical.height;
1997            paintArrow(g, x, y, width, block,
1998                'N', enabled, inside, pressed, "up", true, true, false, true, false);
1999            paintArrow(g, x, y + height - block, width, block,
2000                'S', enabled, inside, pressed, "down", false, true, true, true, false);
2001                
2002            int track = height - (2 * block);
2003            if (track < 10) {
2004                paintRect(g, x, y + block, width, track,
2005                    enabled ? c_border : c_disable, c_bg, true, true, true, true, false);
2006            }
2007            else {
2008                int knob = Math.max(track * port.height / view.height, 10);
2009                int decrease = view.y * (track - knob) / (view.height - port.height);
2010                paintRect(g, x, y + block, width, decrease,
2011                    enabled ? c_border : c_disable, c_bg, true, false, false, true, false);
2012                paintRect(g, x, y + block + decrease, width, knob,
2013                    enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, true, true, true, false);
2014                int n = Math.min(5, (knob - 4) / 3);
2015                g.setColor(enabled ? c_border : c_disable);
2016                int cy = (y + block + decrease) + (knob + 2 - n * 3) / 2;
2017                for (int i = 0; i < n; i++ ) {
2018                    g.drawLine(x + 3, cy + i * 3, x + width - 5, cy + i * 3);
2019                }
2020                int increase = track - decrease - knob;
2021                paintRect(g, x, y + block + decrease + knob, width, increase,
2022                    enabled ? c_border : c_disable, c_bg, false, false, true, true, false);
2023            }
2024        }
2025        
2026        boolean hneed = (horizontal != null); boolean vneed = (vertical != null);
2027        if (("panel" != classname) && ("dialog" != classname) &&
2028                (("textarea" != classname) || getBoolean(component, "border", true))) {
2029            paintRect(g, port.x - 1, port.y - 1, port.width + (vneed ? 1 : 2), port.height + (hneed ? 1 : 2),
2030                enabled ? c_border : c_disable, c_textbg, true, true, !hneed, !vneed, true);
2031            if ("table" == classname) {
2032                Object JavaDoc header = get(component, "header");
2033                if (header != null) {
2034                    int[] columnwidths = (int []) get(component, ":widths");
2035                    Object JavaDoc column = get(header, ":comp"); int x = 0;
2036                    g.clipRect(0, 0, port.width + 2, port.y); // not 2 and decrease clip area...
2037
for (int i = 0; i < columnwidths.length; i++) {
2038                        if (i != 0) { column = get(column, ":next"); }
2039                        boolean lastcolumn = (i == columnwidths.length - 1);
2040                        int width = lastcolumn ? (view.width - x + 2) : columnwidths[i];
2041                        
2042                        paint(column, x - view.x, 0, width, port.y - 1,
2043                            g, clipx, clipy, clipwidth, clipheight,
2044                            true, true, false, lastcolumn, 1, 1, 0, 0, false,
2045                            enabled ? 'g' : 'd', "left", false, false);
2046                        
2047                        Object JavaDoc sort = get(column, "sort"); // "none", "ascent", "descent"
2048
if (sort != null) {
2049                            paintArrow(g, x - view.x + width - block, 0, block, port.y,
2050                                (sort == "ascent") ? 'S' : 'N');
2051                        }
2052                        x += width;
2053                    }
2054                    g.setClip(clipx, clipy, clipwidth, clipheight);
2055                }
2056            }
2057        }
2058        int x1 = Math.max(clipx, port.x);
2059        int x2 = Math.min(clipx + clipwidth, port.x + port.width);
2060        int y1 = Math.max(clipy, port.y);
2061        int y2 = Math.min(clipy + clipheight, port.y + port.height);
2062        if ((x2 > x1) && (y2 > y1)) {
2063            g.clipRect(x1, y1, x2 - x1, y2 - y1);
2064            g.translate(port.x - view.x, port.y - view.y);
2065            
2066            paint(component, classname, focus, enabled,
2067                g, view.x - port.x + x1, view.y - port.y + y1, x2 - x1, y2 - y1, port.width, view.width);
2068            
2069            g.translate(view.x - port.x, view.y - port.y);
2070            g.setClip(clipx, clipy, clipwidth, clipheight);
2071        }
2072    }
2073    
2074    /**
2075     * Paint scrollable content
2076     * @param component a panel
2077     */

2078    private void paint(Object JavaDoc component,
2079            String JavaDoc classname, boolean focus, boolean enabled,
2080            Graphics g, int clipx, int clipy, int clipwidth, int clipheight,
2081            int portwidth, int viewwidth) {
2082        if ("textarea" == classname) {
2083            char[] chars = (char[]) get(component, ":text");
2084            int start = focus ? getInteger(component, "start", 0) : 0;
2085            int end = focus ? getInteger(component, "end", 0) : 0;
2086            int is = Math.min(start, end); int ie = Math.max(start, end);
2087            Font customfont = (Font) get(component, "font");
2088            if (customfont != null) { g.setFont(customfont); }
2089            FontMetrics fm = g.getFontMetrics();
2090            int fontascent = fm.getAscent(); int fontheight = fm.getHeight();
2091            int ascent = 1;
2092            
2093            for (int i = 0, j = 0; j <= chars.length; j++) {
2094                if ((j == chars.length) || (chars[j] == '\n')) {
2095                    if (clipy + clipheight <= ascent) { break; } // the next lines are bellow paint rectangle
2096
if (clipy < ascent + fontheight) { // this line is not above painting area
2097
if (focus && (is != ie) && (ie >= i) && (is <= j)) {
2098                            int xs = (is < i) ? -1 : ((is > j) ? (viewwidth - 1) :
2099                                fm.charsWidth(chars, i, is - i));
2100                            int xe = ((j != -1) && (ie > j)) ? (viewwidth - 1) :
2101                                fm.charsWidth(chars, i, ie - i);
2102                            g.setColor(c_select);
2103                            g.fillRect(1 + xs, ascent, xe - xs + evm, fontheight + evm);
2104                        }
2105                        g.setColor(enabled ? c_text : c_disable);
2106                        g.drawChars(chars, i, j - i, 1, ascent + fontascent);
2107                        if (focus && (end >= i) && (end <= j)) {
2108                            int caret = fm.charsWidth(chars, i, end - i);
2109                            g.setColor(c_focus);
2110                            g.fillRect(caret, ascent, 1 + evm, fontheight + evm);
2111                        }
2112                    }
2113                    ascent += fontheight;
2114                    i = j + 1;
2115                }
2116            }
2117            if (customfont != null) { g.setFont(font); } //restore the default font
2118
}
2119        else if (":combolist" == classname) {
2120            Object JavaDoc lead = get(component, ":lead");
2121            for (Object JavaDoc choice = get(get(component, "combobox"), ":comp");
2122                    choice != null; choice = get(choice, ":next")) {
2123                Rectangle r = getRectangle(choice, "bounds");
2124                if (clipy + clipheight <= r.y) { break; }
2125                if (clipy >= r.y + r.height) { continue; }
2126                paint(choice, r.x, r.y, portwidth, r.height,
2127                    g, clipx, clipy, clipwidth, clipheight,
2128                    false, false, false, false, 2, 4, 2, 4, false,
2129                    getBoolean(choice, "enabled", true) ? ((lead == choice) ? 's' : 't') : 'd',
2130                    "left", false, false);
2131            }
2132        }
2133        else if (("panel" == classname) || ("dialog" == classname)) {
2134            for (Object JavaDoc comp = get(component, ":comp");
2135                    comp != null; comp = get(comp, ":next")) {
2136                paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
2137            }
2138        }
2139        else { //if (("list" == classname) || ("table" == classname) || ("tree" == classname))
2140
Object JavaDoc lead = get(component, ":lead");
2141            int[] columnwidths = ("table" == classname) ? ((int []) get(component, ":widths")) : null;
2142            boolean line = getBoolean(component, "line", true); int iline = line ? 1 : 0;
2143            boolean angle = ("tree" == classname) && getBoolean(component, "angle", false);
2144            for (Object JavaDoc item = get(component, ":comp"), next = null; item != null; item = next) {
2145                if (focus && (lead == null)) {
2146                    set(component, ":lead", lead = item); // draw first item focused when lead is null
2147
}
2148                Rectangle r = getRectangle(item, "bounds");
2149                if (clipy + clipheight <= r.y) { break; } // clip rectangle is above
2150
boolean subnode = false; boolean expanded = false;
2151                if ("tree" != classname) {
2152                    next = get(item, ":next");
2153                }
2154                else {
2155                    subnode = (next = get(item, ":comp")) != null;
2156                    expanded = subnode && getBoolean(item, "expanded", true);
2157                    if (!expanded) {
2158                        for (Object JavaDoc node = item; (node != component) &&
2159                            ((next = get(node, ":next")) == null); node = getParent(node));
2160                    }
2161                }
2162                if (clipy >= r.y + r.height + iline) {
2163                    if (angle) {
2164                        Object JavaDoc nodebelow = get(item, ":next");
2165                        if (nodebelow != null) { // and the next node is bellow clipy
2166
g.setColor(c_bg); int x = r.x - block / 2;
2167                            g.drawLine(x, r.y, x, getRectangle(nodebelow, "bounds").y);
2168                        }
2169                    }
2170                    continue; // clip rectangle is bellow
2171
}
2172                
2173                boolean selected = getBoolean(item, "selected", false);
2174                boolean focused = focus && (lead == item);
2175                paintRect(g, ("tree" != classname) ? 0 : r.x, r.y,
2176                    ("tree" != classname) ? viewwidth : r.width, r.height, c_focus,
2177                    selected ? c_select : c_textbg, focused, focused, focused, focused, true);
2178                if (line) {
2179                    g.setColor(c_bg);
2180                    g.drawLine(0, r.y + r.height, viewwidth, r.y + r.height);
2181                }
2182                if ("table" != classname) { // list or tree
2183
boolean itemenabled = enabled && getBoolean(item, "enabled", true);
2184                    paint(item, r.x, r.y, viewwidth, r.height,
2185                        g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
2186                        1, 3, 1, 3, false, itemenabled ? 'e' : 'd', "left", false, false);
2187                    if ("tree" == classname) {
2188                        int x = r.x - block / 2; int y = r.y + (r.height - 1) / 2;
2189                        if (angle) {
2190                            g.setColor(c_bg);
2191                            g.drawLine(x, r.y, x, y); g.drawLine(x, y, r.x - 1, y);
2192                            Object JavaDoc nodebelow = get(item, ":next");
2193                            if (nodebelow != null) {
2194                                g.drawLine(x, y, x, getRectangle(nodebelow, "bounds").y);
2195                            }
2196                        }
2197                        if (subnode) {
2198                            paintRect(g, x - 4, y - 4, 9, 9, itemenabled ? c_border : c_disable,
2199                                itemenabled ? c_ctrl : c_bg, true, true, true, true, true);
2200                            g.setColor(itemenabled ? c_text : c_disable);
2201                            g.drawLine(x - 2, y, x + 2, y);
2202                            if (!expanded) { g.drawLine(x, y - 2, x, y + 2); }
2203                        }
2204                    }
2205                }
2206                else { // table
2207
int i = 0; int x = 0;
2208                    for (Object JavaDoc cell = get(item, ":comp"); cell != null; cell = get(cell, ":next")) {
2209                        if (clipx + clipwidth <= x) { break; }
2210                        //column width is defined by header calculated in layout, otherwise is 80
2211
int iwidth = 80;
2212                        if ((columnwidths != null) && (columnwidths.length > i)) {
2213                            iwidth = (i != columnwidths.length - 1) ?
2214                                columnwidths[i] : Math.max(iwidth, viewwidth - x);
2215                        }
2216                        if (clipx < x + iwidth) {
2217                            boolean cellenabled = enabled && getBoolean(cell, "enabled", true);
2218                            paint(cell, r.x + x, r.y, iwidth, r.height - 1,
2219                                g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
2220                                1, 1, 1, 1, false, cellenabled ? 'e' : 'd', "left", false, false);
2221                        }
2222                        i++; x += iwidth;
2223                    }
2224                }
2225            }
2226        }
2227    }
2228
2229    /**
2230     *
2231     */

2232    private void paintRect(Graphics g, int x, int y, int width, int height,
2233            Color border, Color bg,
2234            boolean top, boolean left, boolean bottom, boolean right, boolean horizontal) {
2235        if ((width <= 0) || (height <= 0)) return;
2236        g.setColor(border);
2237        if (top) {
2238            g.drawLine(x + width - 1, y, x, y);
2239            y++; height--; if (height <= 0) return;
2240        }
2241        if (left) {
2242            g.drawLine(x, y, x, y + height - 1);
2243            x++; width--; if (width <= 0) return;
2244        }
2245        if (bottom) {
2246            g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
2247            height--; if (height <= 0) return;
2248        }
2249        if (right) {
2250            g.drawLine(x + width - 1, y + height - 1, x + width - 1, y);
2251            width--; if (width <= 0) return;
2252        }
2253
2254        if (bg == c_ctrl) {
2255            fill(g, x, y, width, height, horizontal);
2256        }
2257        else {
2258            g.setColor(bg);
2259            g.fillRect(x, y, width + evm, height + evm);
2260        }
2261    }
2262    
2263    /**
2264     * Fill the given rectangle with gradient
2265     */

2266    private void fill(Graphics g, int x, int y, int width, int height, boolean horizontal) {
2267        if (horizontal) {
2268            if (height > block) {
2269                g.setColor(c_bg);
2270                g.fillRect(x, y, width + evm, height - block + evm);
2271            }
2272            for (int i = 0; i < width; i += block) {
2273                g.drawImage(hgradient, x + i, (height > block) ? (y + height - block) : y,
2274                    x + Math.min(i + block, width) + evm, y + height + evm,
2275                    0, 0, Math.min(block, width - i) + evm, Math.min(block, height) + evm, null);
2276            }
2277        }
2278        else {
2279            if (width > block) {
2280                g.setColor(c_bg);
2281                g.fillRect(x, y, width - block + evm, height + evm);
2282            }
2283            for (int i = 0; i < height; i += block) {
2284                g.drawImage(vgradient, (width > block) ? (x + width - block) : x, y + i,
2285                    x + width + evm, y + Math.min(i + block, height) + evm,
2286                    0, 0, Math.min(block, width) + evm, Math.min(block, height - i) + evm, null);
2287            }
2288        }
2289    }
2290
2291    /**
2292     *
2293     */

2294    private void paintArrow(Graphics g, int x, int y, int width, int height,
2295            char dir, boolean enabled, boolean inside, boolean pressed, String JavaDoc part,
2296            boolean top, boolean left, boolean bottom, boolean right, boolean horizontal) {
2297        inside = inside && (insidepart == part);
2298        pressed = pressed && (pressedpart == part);
2299        paintRect(g, x, y, width, height, enabled ? c_border : c_disable,
2300            enabled ? ((inside != pressed) ? c_hover :
2301                (pressed ? c_press : c_ctrl)) : c_bg,
2302            top, left, bottom, right, horizontal);
2303        g.setColor(enabled ? c_text : c_disable);
2304        paintArrow(g, x + (left ? 1 : 0), y + (top ? 1 : 0),
2305            width - (left ? 1 : 0) - (right ? 1 : 0), height - (top ? 1 : 0) - (bottom ? 1 : 0), dir);
2306    }
2307
2308    /**
2309     *
2310     */

2311    private void paintArrow(Graphics g,
2312            int x, int y, int width, int height, char dir) {
2313        int cx = x + width / 2 - 2;
2314        int cy = y + height / 2 - 2;
2315        for (int i = 0; i < 4; i++) {
2316            if (dir == 'N') { // north
2317
g.drawLine(cx + 1 - i, cy + i, cx + 1/*2*/ + i, cy + i);
2318            }
2319            else if (dir == 'W') { // west
2320
g.drawLine(cx + i, cy + 1 - i, cx + i, cy + 1/*2*/ + i);
2321            }
2322            else if (dir == 'S') { // south
2323
g.drawLine(cx + 1 - i, cy + 4 - i, cx + 1/*2*/ + i, cy + 4 - i);
2324            }
2325            else { // east
2326
g.drawLine(cx + 4 - i, cy + 1 - i, cx + 4 - i, cy + 1/*2*/ + i);
2327            }
2328        }
2329    }
2330    
2331    /**
2332     * Paint component's borders and background
2333     */

2334    private void paint(Object JavaDoc component, int x, int y, int width, int height,
2335            Graphics g, boolean top, boolean left, boolean bottom, boolean right,
2336            char mode) {
2337        if ((width <= 0) || (height <= 0)) { return; }
2338    
2339        if (top || left || bottom || right) { // draw border
2340
g.setColor(((mode != 'd') && (mode != 'i')) ? c_border : c_disable);
2341            if (top) {
2342                g.drawLine(x + width - 1, y, x, y);
2343                y++; height--; if (height <= 0) { return; }
2344            }
2345            if (left) {
2346                g.drawLine(x, y, x, y + height - 1);
2347                x++; width--; if (width <= 0) { return; }
2348            }
2349            if (bottom) {
2350                g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
2351                height--; if (height <= 0) { return; }
2352            }
2353            if (right) {
2354                g.drawLine(x + width - 1, y + height - 1, x + width - 1, y);
2355                width--; if (width <= 0) { return; }
2356            }
2357        }
2358    
2359        Color background = (Color) get(component, "background");
2360        switch (mode) {
2361            case 'e': case 'l': case 'd': case 'g': case 'r': break;
2362            case 'b': case 'i': case 'x': if (background == null) { background = c_bg; } break;
2363            case 'h': background = (background != null) ? background.brighter() : c_hover; break;
2364            case 'p': background = (background != null) ? background.darker() : c_press; break;
2365            case 't': if (background == null) { background = c_textbg; } break;
2366            case 's': background = c_select; break;
2367            default: throw new IllegalArgumentException JavaDoc();
2368        }
2369        if (((mode == 'g') || (mode == 'r')) && (background == null)) {
2370            fill(g, x, y, width, height, true);
2371        }
2372        else if (background != null) {
2373            g.setColor(background);
2374            if (mode != 'x') { g.fillRect(x, y, width + evm, height + evm); }
2375        }
2376    }
2377
2378    /**
2379     *
2380     */

2381    private void paint(Object JavaDoc component, Graphics g,
2382            int x, int y, int width, int height, char type) {
2383        paint(component, x, y, width, height, g, true, true, true, true, 'g');
2384        g.setColor(Color.black);
2385        switch (type) {
2386            case 'c': // closable dialog button
2387
g.drawLine(x + 3, y + 4, x + width - 5, y + height - 4);
2388                g.drawLine(x + 3, y + 3, x + width - 4, y + height - 4);
2389                g.drawLine(x + 4, y + 3, x + width - 4, y + height - 5);
2390                g.drawLine(x + width - 5, y + 3, x + 3, y + height - 5);
2391                g.drawLine(x + width - 4, y + 3, x + 3, y + height - 4);
2392                g.drawLine(x + width - 4, y + 4, x + 4, y + height - 4);
2393                break;
2394            case 'm': // maximizable dialog button
2395
g.drawRect(x + 3, y + 3, width - 7, height - 7);
2396                g.drawLine(x + 4, y + 4, x + width - 5, y + 4);
2397                break;
2398            case 'i': // iconifiable dialog button
2399
g.fillRect(x + 3, y + height - 5, width - 6, 2);
2400                break;
2401        }
2402    }
2403
2404    /**
2405     * Paint component icon and text (using default or custom font)
2406     * @param mnemonic find mnemonic index and underline text
2407     */

2408    private void paint(Object JavaDoc component, int x, int y, int width, int height,
2409            Graphics g, int clipx, int clipy, int clipwidth, int clipheight,
2410            boolean top, boolean left, boolean bottom, boolean right,
2411            int toppadding, int leftpadding, int bottompadding, int rightpadding, boolean focus,
2412            char mode, String JavaDoc alignment, boolean mnemonic, boolean underline) {
2413        paint(component, x, y, width, height,
2414            g, top, left, bottom, right, mode);
2415        if (top) { y++; height--; } if (left) { x++; width--; }
2416        if (bottom) { height--; } if (right) { width--; }
2417        if ((width <= 0) || (height <= 0)) { return; }
2418        
2419        if (focus) {
2420            g.setColor(c_focus);
2421            g.drawRect(x + 1, y + 1, width - 3, height - 3);
2422        }
2423
2424        String JavaDoc text = getString(component, "text", null);
2425        Image icon = getIcon(component, "icon", null);
2426        if ((text == null) && (icon == null)) { return; }
2427    
2428        x += leftpadding; y += toppadding;
2429        width -= leftpadding + rightpadding; height -= toppadding + bottompadding;
2430
2431        alignment = getString(component, "alignment", alignment);
2432        Font customfont = (text != null) ? (Font) get(component, "font") : null;
2433        if (customfont != null) { g.setFont(customfont); }
2434
2435        FontMetrics fm = null;
2436        int tw = 0, th = 0;
2437        int ta = 0;
2438        if (text != null) {
2439            fm = g.getFontMetrics();
2440            tw = fm.stringWidth(text);
2441            ta = fm.getAscent();
2442            th = fm.getDescent() + ta;
2443        }
2444        int iw = 0, ih = 0;
2445        if (icon != null) {
2446            iw = icon.getWidth(this);
2447            ih = icon.getHeight(this);
2448            if (text != null) { iw += 2; }
2449        }
2450
2451        boolean clipped = (tw + iw > width) || (th > height) || (ih > height);
2452        int cx = x;
2453        if ("center" == alignment) { cx += (width - tw - iw) / 2; }
2454            else if ("right" == alignment) { cx += width - tw - iw; }
2455
2456        if (clipped) { g.clipRect(x, y, width, height); }
2457        if (mode == 'x') { g.drawLine(cx, y + height / 2, cx + iw + tw, y + height / 2); }
2458        if (icon != null) {
2459            g.drawImage(icon, cx, y + (height - ih) / 2, this);
2460            cx += iw;
2461        }
2462        if (text != null) {
2463            Color foreground = (Color) get(component, "foreground");
2464            if (foreground == null) {
2465                foreground = (mode == 'l') ? Color.blue :
2466                    (((mode != 'd') && (mode != 'r')) ? c_text : c_disable);
2467            }
2468            g.setColor(foreground);
2469            int ty = y + (height - th) / 2 + ta;
2470            g.drawString(text, cx, ty);
2471            if (mnemonic) {
2472                int imnemonic = getInteger(component, "mnemonic", -1);
2473                if ((imnemonic != -1) && (imnemonic < text.length())) {
2474                    int mx = cx + fm.stringWidth(text.substring(0, imnemonic));
2475                    g.drawLine(mx, ty + 1, mx + fm.charWidth(text.charAt(imnemonic)), ty + 1);
2476                }
2477            }
2478            if (underline) { // for link button
2479
g.drawLine(cx, ty + 1, cx + tw, ty + 1);
2480            }
2481        }
2482        if (clipped) { g.setClip(clipx, clipy, clipwidth, clipheight); }
2483        
2484        if (customfont != null) { g.setFont(font); } //restore the default font
2485
}
2486
2487    /**
2488     * A second thread is used to repeat value change events for scrollbar or spinbox
2489     * during the mouse is pressed, or to pop up tooltip
2490     */

2491    public synchronized void run() {
2492        while (timer == Thread.currentThread()) {
2493            try {
2494                if (watch == 0) {
2495                    wait(0);
2496                } else {
2497                    long current = System.currentTimeMillis();
2498                    if (watch > current) {
2499                        wait(watch - current);
2500                    } else {
2501                        watch = 0;
2502                        if ((watchdelay == 300L) || (watchdelay == 60L)) {
2503                            if (processScroll(mousepressed, pressedpart)) { setTimer(60L); }
2504                        } else if ((watchdelay == 375L) || (watchdelay == 75L)) {
2505                            if (processSpin(mousepressed, pressedpart)) { setTimer(75L); }
2506                        } else if (watchdelay == 750L) {
2507                            showTip();
2508                        }
2509                    }
2510                }
2511            } catch (InterruptedException JavaDoc ie) {} //ie.printStackTrace();
2512
}
2513    }
2514
2515    /**
2516     *
2517     */

2518    private void setTimer(long delay) {
2519        watchdelay = delay;
2520        if (delay == 0) {
2521            watch = 0;
2522        } else {
2523            long prev = watch;
2524            watch = System.currentTimeMillis() + delay;
2525            if (timer == null) {
2526                timer = new Thread JavaDoc(this);
2527                timer.setPriority(Thread.MIN_PRIORITY);
2528                timer.setDaemon(true);
2529                timer.start();
2530            }
2531            if ((prev == 0) || (watch < prev)) {
2532                synchronized (this) { notify(); }
2533                //synchronized (this) { try { notify(); }catch (IllegalMonitorStateException imse) {} }
2534
}
2535        }
2536    }
2537
2538    /**
2539     * This component can be traversed using Tab or Shift-Tab keyboard focus traversal,
2540     * although 1.4 replaced this method by <i>isFocusable</i>,
2541     * so 1.4 compilers write deprecation warning
2542     *
2543     * @return true as focus-transverable component, overwrites the default false value
2544     */

2545    public boolean isFocusTraversable() {
2546        return true;
2547    }
2548
2549    /**
2550     * Dispatches mouse, key, focus, and component events occurring on the
2551     * <i>Thinlet</i> component internally
2552     */

2553    protected void processEvent(AWTEvent e) {
2554        // evm (touchscreen) events: entered/moved/pressed -> dragged -> dragged/released/exited
2555
int id = e.getID();
2556        if ((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_MOVED) ||
2557                (id == MouseEvent.MOUSE_EXITED) || (id == MouseEvent.MOUSE_PRESSED) ||
2558                (id == MouseEvent.MOUSE_DRAGGED) || (id == MouseEvent.MOUSE_RELEASED)) {
2559            MouseEvent me = (MouseEvent) e;
2560            int x = me.getX();
2561            int y = me.getY();
2562            int clickcount = me.getClickCount();
2563            boolean shiftdown = me.isShiftDown();
2564            boolean controldown = me.isControlDown();
2565            boolean popuptrigger = me.isPopupTrigger();
2566            if (id == MouseEvent.MOUSE_ENTERED) {
2567                if (mousepressed == null) {
2568                    findComponent(content, x, y);
2569                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2570                        MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2571                }
2572            }
2573            else if (id == MouseEvent.MOUSE_MOVED) {
2574                Object JavaDoc previnside = mouseinside;
2575                Object JavaDoc prevpart = insidepart;
2576                findComponent(content, x, y);
2577                if ((previnside == mouseinside) && (prevpart == insidepart)) {
2578                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2579                        MouseEvent.MOUSE_MOVED, mouseinside, insidepart);
2580                }
2581                else {
2582                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2583                        MouseEvent.MOUSE_EXITED, previnside, prevpart);
2584                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2585                        MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2586                }
2587            }
2588            else if (id == MouseEvent.MOUSE_EXITED) {
2589                if (mousepressed == null) {
2590                    Object JavaDoc mouseexit = mouseinside;
2591                    Object JavaDoc exitpart = insidepart;
2592                    mouseinside = insidepart = null;
2593                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2594                        MouseEvent.MOUSE_EXITED, mouseexit, exitpart);
2595                }
2596            }
2597            else if (id == MouseEvent.MOUSE_PRESSED) {
2598                if (popupowner != null) { // remove popup
2599
String JavaDoc classname = getClass(mouseinside);
2600                    if ((popupowner != mouseinside) &&
2601                            (classname != ":popup") && (classname != ":combolist")) {
2602                        closeup();
2603                    }
2604                }
2605                hideTip(); // remove tooltip
2606
mousepressed = mouseinside;
2607                pressedpart = insidepart;
2608                handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2609                    MouseEvent.MOUSE_PRESSED, mousepressed, pressedpart);
2610            }
2611            else if (id == MouseEvent.MOUSE_DRAGGED) {
2612                hideTip(); // remove tooltip
2613
Object JavaDoc previnside = mouseinside;
2614                Object JavaDoc prevpart = insidepart;
2615                findComponent(content, x, y);
2616                boolean same = (previnside == mouseinside) && (prevpart == insidepart);
2617                boolean isin = (mousepressed == mouseinside) && (pressedpart == insidepart);
2618                boolean wasin = (mousepressed == previnside) && (pressedpart == prevpart);
2619                
2620                if (wasin && !isin) {
2621                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2622                        MouseEvent.MOUSE_EXITED, mousepressed, pressedpart);
2623                }
2624                else if (!same && (popupowner != null) && !wasin) {
2625                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2626                        DRAG_EXITED, previnside, prevpart);
2627                }
2628                if (isin && !wasin) {
2629                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2630                        MouseEvent.MOUSE_ENTERED, mousepressed, pressedpart);
2631                }
2632                else if (!same && (popupowner != null) && !isin) {
2633                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2634                        DRAG_ENTERED, mouseinside, insidepart);
2635                }
2636                if (isin == wasin) {
2637                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2638                        MouseEvent.MOUSE_DRAGGED, mousepressed, pressedpart);
2639                }
2640            }
2641            else if (id == MouseEvent.MOUSE_RELEASED) {
2642                hideTip(); // remove tooltip
2643
Object JavaDoc mouserelease = mousepressed;
2644                Object JavaDoc releasepart = pressedpart;
2645                mousepressed = pressedpart = null;
2646                handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2647                    MouseEvent.MOUSE_RELEASED, mouserelease, releasepart);
2648                if ((mouseinside != null) &&
2649                        ((mouserelease != mouseinside) || (releasepart != insidepart))) {
2650                    handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2651                        MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2652                }
2653            }
2654        }
2655        else if (id == MOUSE_WHEEL) {
2656            Rectangle port = getRectangle(mouseinside, ":port");
2657            if (port != null) { // is scrollable
2658
// TODO hide tooltip?
2659
Rectangle bounds = getRectangle(mouseinside, "bounds");
2660                try { // mouse wheel is supported since 1.4 thus it use reflection
2661
if (wheelrotation == null) {
2662                        wheelrotation = e.getClass().getMethod("getWheelRotation", null);
2663                    }
2664                    int rotation = ((Integer JavaDoc) wheelrotation.invoke(e, null)).intValue();
2665                    
2666                    if (port.x + port.width < bounds.width) { // has vertical scrollbar
2667
processScroll(mouseinside, (rotation > 0) ? "down" : "up"); //TODO scroll panels too
2668
}
2669                    else if (port.y + port.height < bounds.height) { // has horizontal scrollbar
2670
processScroll(mouseinside, (rotation > 0) ? "right" : "left");
2671                    }
2672                } catch (Exception JavaDoc exc) { /* never */ }
2673            }
2674        }
2675        else if ((id == KeyEvent.KEY_PRESSED) || (id == KeyEvent.KEY_TYPED)) {
2676            if (focusinside && ((popupowner != null) || (focusowner != null))) {
2677                hideTip(); // remove tooltip
2678
KeyEvent ke = (KeyEvent) e;
2679                int keychar = ke.getKeyChar();
2680                boolean control = (keychar <= 0x1f) ||
2681                    ((keychar >= 0x7f) && (keychar <= 0x9f)) ||
2682                    (keychar >= 0xffff) || ke.isControlDown();
2683                int keycode = control ? ke.getKeyCode() : 0;
2684                
2685                if ((control == (id == KeyEvent.KEY_PRESSED)) &&
2686                    processKeyPress((popupowner != null) ? popupowner : focusowner,
2687                        ke.isShiftDown(), ke.isControlDown(), ke.getModifiers(),
2688                        control ? 0 : keychar, keycode)) {
2689                    ke.consume();
2690                }
2691                else if ((keycode == KeyEvent.VK_TAB) ||
2692                        ((keycode == KeyEvent.VK_F6) && (ke.isAltDown() || ke.isControlDown()))) {
2693                    boolean outgo = (keycode == KeyEvent.VK_F6);
2694                    if (!ke.isShiftDown() ? setNextFocusable(focusowner, outgo) :
2695                            setPreviousFocusable(focusowner, outgo)) {
2696                        ke.consume();
2697                    } else if (MOUSE_WHEEL != 0) { // 1.4
2698
if (!ke.isShiftDown()) {
2699                            transferFocus();
2700                        }
2701                        else { try {
2702                                getClass().getMethod("transferFocusBackward", null). invoke(this, null);
2703                        } catch (Exception JavaDoc exc) { /* never */ } }
2704                    }
2705                    repaint(focusowner);
2706                    closeup();
2707                }
2708                else if (keycode == KeyEvent.VK_F8) {
2709                    for (Object JavaDoc splitpane = focusowner;
2710                            splitpane != null; splitpane = getParent(splitpane)) {
2711                        if (getClass(splitpane) == "splitpane") {
2712                            setFocus(splitpane); repaint(splitpane); ke.consume(); break; //middle
2713
}
2714                    }
2715                }
2716                else if ((id == KeyEvent.KEY_PRESSED) && ((keychar != 0) || ke.isActionKey()) &&
2717                        checkMnemonic(focusowner, true, null, ke.getKeyCode(), ke.getModifiers())) {
2718                    ke.consume();
2719                }
2720            }
2721        }
2722        else if (id == FocusEvent.FOCUS_LOST) {
2723            focusinside = false;
2724            if (focusowner != null) { repaint(focusowner); }
2725            closeup();
2726        }
2727        else if (id == FocusEvent.FOCUS_GAINED) {
2728            focusinside = true;
2729            if (focusowner == null) { setFocus(content); }
2730                else { repaint(focusowner); }
2731        }
2732        else if ((id == ComponentEvent.COMPONENT_RESIZED) ||
2733                (id == ComponentEvent.COMPONENT_SHOWN)) {
2734            Dimension d = getSize();
2735            setRectangle(content, "bounds", 0, 0, d.width, d.height);
2736            validate(content);
2737            closeup();
2738            if (!focusinside) { requestFocus(); }
2739        }
2740    }
2741    
2742    /**
2743     * Check the previous mouse location again because of a possible layout change
2744     */

2745    private void checkLocation() {
2746        findComponent(content, mousex, mousey);
2747        handleMouseEvent(mousex, mousex, 1, false, false, false,
2748            MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2749    }
2750
2751    /**
2752     *
2753     */

2754    private boolean processKeyPress(Object JavaDoc component,
2755            boolean shiftdown, boolean controldown, int modifiers, int keychar, int keycode) {
2756        String JavaDoc classname = getClass(component);
2757        if ("button" == classname) {
2758            if (keychar == KeyEvent.VK_SPACE ||
2759                    ((keycode == KeyEvent.VK_ENTER) &&
2760                        (get(component, "type") == "default")) ||
2761                    ((keycode == KeyEvent.VK_ESCAPE) && //...
2762
(get(component, "type") == "cancel"))) {
2763                //pressedkey = keychar;
2764
invoke(component, null, "action");
2765                repaint(component);
2766                return true;
2767            }
2768        }
2769        else if (("checkbox" == classname) || ("togglebutton" == classname)) {
2770            if (keychar == KeyEvent.VK_SPACE) {
2771                changeCheck(component, true);
2772                repaint(component);
2773                return true;
2774            }
2775        }
2776        else if ("combobox" == classname) {
2777            Object JavaDoc combolist = get(component, ":combolist");
2778            if (combolist == null) { // the drop down list is not visible
2779
boolean editable = getBoolean(component, "editable", true);
2780                if (editable && processField(component, shiftdown, controldown, modifiers,
2781                            keychar, keycode, false, false, false)) {
2782                    setInteger(component, "selected", -1, -1);
2783                    return true;
2784                }
2785                if ((keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_DOWN)) {
2786                    popupCombo(component);
2787                }
2788                //+findText
2789
else return false;
2790            }
2791            else {
2792                if ((keycode == KeyEvent.VK_UP) ||
2793                        (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
2794                        (keycode == KeyEvent.VK_PAGE_DOWN) ||
2795                        (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
2796                    Object JavaDoc next = getListItem(component, combolist, keycode,
2797                        get(combolist, ":lead"), false);
2798                    if (next != null) {
2799                        setInside(combolist, next, true);
2800                    }
2801                }
2802                else if ((keycode == KeyEvent.VK_ENTER) || (keychar == KeyEvent.VK_SPACE)) {
2803                    closeCombo(component, combolist, get(combolist, ":lead")); //Alt+Up
2804
}
2805                else if (keycode == KeyEvent.VK_ESCAPE) {
2806                    closeCombo(component, combolist, null);
2807                }
2808                else if (!processField(component, shiftdown, controldown, modifiers,
2809                        keychar, keycode, false, false, false)) {
2810                    Object JavaDoc item = findText((char) keychar, component, combolist, false);
2811                    if (item != null) {
2812                        setInside(combolist, item, true);
2813                    }
2814                    else return false;
2815                }
2816            }
2817            return true;
2818        }
2819        else if (("textfield" == classname) || ("passwordfield" == classname)) {
2820            return processField(component, shiftdown, controldown, modifiers,
2821                keychar, keycode, false, ("passwordfield" == classname), false);
2822        }
2823        else if ("textarea" == classname) {
2824            char[] chars = (char[]) get(component, ":text");
2825            int start = getInteger(component, "start", 0);
2826            int end = getInteger(component, "end", 0);
2827
2828            int istart = start;
2829            int iend = end;
2830            String JavaDoc insert = null;
2831            if ((keycode == KeyEvent.VK_HOME) && !controldown) {
2832                while ((iend > 0) && (chars[iend - 1] != '\n')) { iend--; }
2833                if (!shiftdown) { istart = iend; }
2834            }
2835            else if ((keycode == KeyEvent.VK_END) && !controldown) {
2836                while ((iend < chars.length) && (chars[iend] != '\n')) { iend++; }
2837                if (!shiftdown) { istart = iend; }
2838            }
2839            else if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP) ||
2840                    (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) {
2841                Font currentfont = (Font) get(component, "font");
2842                FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
2843                int fh = fm.getHeight();
2844                int y = 0; int linestart = 0;
2845                for (int i = 0; i < iend; i++) {
2846                    if ((chars[i] == '\n') || (chars[i] == '\t')) {
2847                        linestart = i + 1; y += fh;
2848                    }
2849                }
2850                if (keycode == KeyEvent.VK_UP) { y -= fh; }
2851                else if (keycode == KeyEvent.VK_DOWN) { y += fh; }
2852                else {
2853                    int dy = getRectangle(component, ":port").height;
2854                    y += (keycode == KeyEvent.VK_PAGE_UP) ? -dy : dy; // VK_PAGE_DOWN
2855
}
2856                int x = fm.charsWidth(chars, linestart, iend - linestart);
2857                iend = getCaretLocation(component, x, y, true, false);
2858                if (!shiftdown) { istart = iend; }
2859            }
2860            else return processField(component, shiftdown, controldown, modifiers,
2861                    keychar, keycode, true, false, false);
2862            return changeField(component,
2863                getString(component, "text", ""), insert, istart, iend, start, end);
2864        }
2865        else if ("tabbedpane" == classname) {
2866            if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN) ||
2867                    (keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2868                int selected = getInteger(component, "selected", 0);
2869                boolean increase = (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN);
2870                int newvalue = selected;
2871                int n = increase ? getItemCountImpl(component, ":comp") : 0;
2872                int d = (increase ? 1 : -1);
2873                for (int i = selected + d; increase ? (i < n) : (i >= 0); i += d) {
2874                    if (getBoolean(getItem(component, i), "enabled", true)) {
2875                        newvalue = i; break;
2876                    }
2877                }
2878                if (newvalue != selected) {
2879                    setInteger(component, "selected", newvalue, 0);
2880                    checkOffset(component);
2881                    repaint(component);
2882                    invoke(component, getItem(component, newvalue), "action");
2883                }
2884            }
2885        }
2886        else if ("spinbox" == classname) {
2887            if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
2888                processSpin(component, (keycode == KeyEvent.VK_UP)? "up" : "down");
2889                return true;
2890            }
2891            return processField(component, shiftdown, controldown, modifiers,
2892                keychar, keycode, false, false, true);
2893        }
2894        else if ("slider" == classname) {
2895            int value = getInteger(component, "value", 0);
2896            int d = 0;
2897            if ((keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_LEFT) ||
2898                    (keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP)) {
2899                d = getInteger(component, "minimum", 0) - value;
2900                if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2901                    d = Math.max(d, -getInteger(component, "unit", 5));
2902                }
2903                else if (keycode == KeyEvent.VK_PAGE_UP) {
2904                    d = Math.max(d, -getInteger(component, "block", 25));
2905                }
2906            }
2907            else if ((keycode == KeyEvent.VK_END) || (keycode == KeyEvent.VK_RIGHT) ||
2908                    (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) {
2909                d = getInteger(component, "maximum", 100) - value;
2910                if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
2911                    d = Math.min(d, getInteger(component, "unit", 5));
2912                }
2913                else if (keycode == KeyEvent.VK_PAGE_DOWN) {
2914                    d = Math.min(d, getInteger(component, "block", 25));
2915                }
2916            }
2917            if (d != 0) {
2918                setInteger(component, "value", value + d, 0);
2919                repaint(component);
2920                invoke(component, null, "action");
2921            }
2922        }
2923        else if ("splitpane" == classname) {
2924            int divider = getInteger(component, "divider", -1);
2925            int d = 0;
2926            if (keycode == KeyEvent.VK_HOME) {
2927                d = -divider;
2928            }
2929            else if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2930                d = Math.max(-10, -divider);
2931            }
2932            else if ((keycode == KeyEvent.VK_END) ||
2933                    (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
2934                boolean horizontal = ("vertical" != get(component, "orientation"));
2935                Rectangle bounds = getRectangle(component, "bounds");
2936                int max = (horizontal ? bounds.width : bounds.height) - 5;
2937                d = max - divider;
2938                if (keycode != KeyEvent.VK_END) {
2939                    d = Math.min(d, 10);
2940                }
2941            }
2942            if (d != 0) {
2943                setInteger(component, "divider", divider + d, -1);
2944                validate(component);
2945            }
2946        }
2947        else if (("list" == classname) || ("table" == classname)) {
2948            return processList(component, shiftdown, controldown, keychar, keycode, false);
2949        }
2950        else if ("tree" == classname) {
2951            //? clear childs' selection, select this is its subnode was selected
2952
if (keycode == KeyEvent.VK_LEFT) {
2953                Object JavaDoc lead = get(component, ":lead");
2954                if ((get(lead, ":comp") != null) && getBoolean(lead, "expanded", true)) { // collapse
2955
setBoolean(lead, "expanded", false, true);
2956                    selectItem(component, lead, true);
2957                    validate(component);
2958                    invoke(component, lead, "collapse"); //lead
2959
return true;
2960                }
2961                else { // select parent
2962
Object JavaDoc parent = getParent(lead);
2963                    if (parent != component) {
2964                        selectItem(component, parent, true);
2965                        setLead(component, lead, parent);
2966                        return true;
2967                    }
2968                }
2969            }
2970            //? for interval mode select its all subnode or deselect all after
2971
else if (keycode == KeyEvent.VK_RIGHT) {
2972                Object JavaDoc lead = get(component, ":lead");
2973                Object JavaDoc node = get(lead, ":comp");
2974                if (node != null) {
2975                    if (getBoolean(lead, "expanded", true)) { // select its first subnode
2976
selectItem(component, node, true);
2977                        setLead(component, lead, node);
2978                    }
2979                    else { // expand
2980
setBoolean(lead, "expanded", true, true);
2981                        selectItem(component, lead, true);
2982                        validate(component);
2983                        invoke(component, lead, "expand"); //lead
2984
}
2985                    return true;
2986                }
2987            }
2988            return processList(component, shiftdown, controldown, keychar, keycode, true);
2989        }
2990        else if (("menubar" == classname) || ("popupmenu" == classname)) {
2991            // find the last open :popup and the previous one
2992
Object JavaDoc previous = null; Object JavaDoc last = null;
2993            for (Object JavaDoc i = get(component, ":popup");
2994                    i != null; i = get(i, ":popup")) {
2995                previous = last; last = i;
2996            }
2997            //selected is the current item of the last, or the previous :popup, or null
2998
Object JavaDoc selected = get(last, "selected");
2999            Object JavaDoc hotpopup = ((selected != null) || (previous == null)) ?
3000                last : previous;
3001            if ((selected == null) && (previous != null)) {
3002                selected = get(previous, "selected");
3003            }
3004
3005            if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
3006                Object JavaDoc next = getMenu(hotpopup,
3007                    selected, keycode == KeyEvent.VK_DOWN, true);
3008                if (next != null) {
3009                    set(hotpopup, "selected", null);
3010                    popupMenu(hotpopup);
3011                    set(hotpopup, "selected", next);
3012                    repaint(hotpopup);
3013                }
3014            }
3015            else if (keycode == KeyEvent.VK_LEFT) {
3016                if (previous != null) { // close the last :popup
3017
selected = get(previous, "selected");
3018                    set(previous, "selected", null);
3019                    popupMenu(previous);
3020                    set(previous, "selected", selected);
3021                    repaint(previous); // , selected
3022
}
3023                else if ("menubar" == classname) { // select the previous menubar menu
3024
Object JavaDoc next = getMenu(component, get(component, "selected"), false, false);
3025                    if (next != null) {
3026                        set(component, "selected", next);
3027                        Object JavaDoc popup = popupMenu(component);
3028                        set(popup, "selected", getMenu(popup, null, true, true));
3029                        repaint(component); // , selected
3030
}
3031                }
3032            }
3033            else if (keycode == KeyEvent.VK_RIGHT) {
3034                if ((previous != null) && (selected == null)) { // ?
3035
set(last, "selected", get(get(last, "menu"), ":comp"));
3036                    repaint(last); // , selected
3037
}
3038                else if ((selected != null) && (getClass(selected) == "menu")) { // expand menu
3039
Object JavaDoc popup = popupMenu(last);
3040                    set(popup, "selected", getMenu(popup, null, true, true));
3041                }
3042                else if ("menubar" == classname) { // select the next menubar menu
3043
Object JavaDoc next = getMenu(component, get(component, "selected"), true, false);
3044                    if (next != null) {
3045                        set(component, "selected", next);
3046                        Object JavaDoc popup = popupMenu(component);
3047                        set(popup, "selected", getMenu(popup, null, true, true));
3048                        repaint(component); // , selected
3049
}
3050                }
3051            }
3052            else if ((keycode == KeyEvent.VK_ENTER) ||
3053                    (keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_ESCAPE)) {
3054                if ((keycode != KeyEvent.VK_ESCAPE) &&
3055                        getBoolean(selected, "enabled", true)) {
3056                    if ((selected != null) && (getClass(selected) == "checkboxmenuitem")) {
3057                        changeCheck(selected, false);
3058                    }
3059                    else invoke(selected, null, "action");
3060                }
3061                closeup();
3062            }
3063            else return false;
3064            return true;
3065        }
3066        return false;
3067    }
3068
3069    /**
3070     *
3071     */

3072    private boolean changeCheck(Object JavaDoc component, boolean box) {
3073        String JavaDoc group = getString(component, "group", null);
3074        if (group != null) {
3075            if (getBoolean(component, "selected", false)) { return false; }
3076            for (Object JavaDoc comp = get(getParent(component), ":comp");
3077                    comp != null; comp = get(comp, ":next")) {
3078                if (comp == component) {
3079                    setBoolean(component, "selected", true);
3080                }
3081                else if (group.equals(get(comp, "group")) &&
3082                        getBoolean(comp, "selected", false)) {
3083                    setBoolean(comp, "selected", false);
3084                    if (box) { repaint(comp); } //checkbox only
3085
}
3086            }
3087        }
3088        else {
3089            setBoolean(component, "selected",
3090                !getBoolean(component, "selected", false), false);
3091        }
3092        invoke(component, null, "action");
3093        return true;
3094    }
3095
3096    /**
3097     * @param component a :popup or a menubar
3098     * @param part the currently selected item, return the first/last if null
3099     * @param forward find the next item if true, the previous otherwise
3100     * @param popup the given component is :popup if true, menubar otherwise
3101     * @return the next/previous item relative to the current one excluding separators, or null
3102     */

3103    private Object JavaDoc getMenu(Object JavaDoc component, Object JavaDoc part,
3104            boolean forward, boolean popup) {
3105        Object JavaDoc previous = null;
3106        for (int i = 0; i < 2; i++) { // 0: next to last, 1: first to previous
3107
for (Object JavaDoc item = (i == 0) ? get(part, ":next") :
3108                        get(popup ? get(component, "menu") : component, ":comp");
3109                    (i == 0) ? (item != null) : (item != part); item = get(item, ":next")) {
3110                if ((getClass(item) != "separator") && getBoolean(item, "enabled", true)) {
3111                    if (forward) { return item; }
3112                    previous = item;
3113                }
3114            }
3115        }
3116        return previous;
3117    }
3118
3119    /**
3120     * Process keyboard events for textfield, passwordfield, textarea,
3121     * combobox, and spinbox
3122     * @param multiline true for textarea, otherwise false
3123     * @param hidden true for passwordfield, otherwise false
3124     * @param filter true for spinbox, otherwise false
3125     */

3126    private boolean processField(Object JavaDoc component,
3127            boolean shiftdown, boolean controldown, int modifiers,
3128            int keychar, int keycode,
3129            boolean multiline, boolean hidden, boolean filter) {
3130        String JavaDoc text = getString(component, "text", "");
3131        int start = getInteger(component, "start", 0);
3132        int end = getInteger(component, "end", 0);
3133        boolean editable = getBoolean(component, "editable", true);
3134
3135        int istart = start;
3136        int iend = end;
3137        String JavaDoc insert = null;
3138        if (editable && (keychar != 0) &&
3139            //((modifiers == 0) || (modifiers == InputEvent.SHIFT_MASK))) {
3140
(modifiers != InputEvent.ALT_MASK)) {
3141            insert = String.valueOf((char) keychar);
3142        }
3143        else if (editable && (keycode == KeyEvent.VK_ENTER)) {
3144            if (multiline) { insert = "\n"; }
3145                else { return invoke(component, null, "perform"); }
3146        }
3147        else if (editable && (keycode == KeyEvent.VK_BACK_SPACE)) {
3148            insert = "";
3149            if (start == end) { istart -= 1; }
3150        }
3151        else if (keycode == KeyEvent.VK_END) {
3152            iend = text.length();
3153            if (!shiftdown) { istart = iend; }
3154        }
3155        else if (keycode == KeyEvent.VK_HOME) {
3156            iend = 0;
3157            if (!shiftdown) { istart = iend; }
3158        }
3159        else if (keycode == KeyEvent.VK_LEFT) {
3160            if (controldown) {
3161                for (int i = 0; i < 2; i++) {
3162                    while ((iend > 0) && ((i != 0) ==
3163                        Character.isLetterOrDigit(text.charAt(iend - 1)))) { iend--; }
3164                }
3165            } else {
3166                iend -= 1;
3167            }
3168            if (!shiftdown) { istart = iend; }
3169        }
3170        else if (keycode == KeyEvent.VK_RIGHT) {
3171            if (controldown) {
3172                for (int i = 0; i < 2; i++) {
3173                    while ((iend < text.length()) && ((i == 0) ==
3174                        Character.isLetterOrDigit(text.charAt(iend)))) { iend++; }
3175                }
3176            } else {
3177                iend += 1;
3178            }
3179            if (!shiftdown) { istart = iend; }
3180        }
3181        else if (editable && (keycode == KeyEvent.VK_DELETE)) {
3182            insert = "";
3183            if (start == end) { iend += 1; }
3184        }
3185        else if (controldown &&
3186                ((keycode == KeyEvent.VK_A) || (keycode == 0xBF))) {
3187            istart = 0; // KeyEvent.VK_SLASH
3188
iend = text.length();
3189        }
3190        else if (controldown && (keycode == 0xDC)) {
3191            istart = iend = text.length(); // KeyEvent.VK_BACK_SLASH
3192
}
3193        else if ((editable && !hidden && controldown && (keycode == KeyEvent.VK_X)) ||
3194                (!hidden && controldown && (keycode == KeyEvent.VK_C))) {
3195            if (start != end) {
3196                clipboard = text.substring(
3197                    Math.min(start, end), Math.max(start, end));
3198                try {
3199                    getToolkit().getSystemClipboard().setContents(
3200                        new StringSelection(clipboard), null);
3201                } catch (Exception JavaDoc exc) {}
3202                if (keycode == KeyEvent.VK_X) { insert = ""; } else { return true; }
3203            }
3204        }
3205        else if (editable && controldown && (keycode == KeyEvent.VK_V)) {
3206            try {
3207                insert = (String JavaDoc) getToolkit().getSystemClipboard().
3208                    getContents(this).getTransferData(DataFlavor.stringFlavor);
3209            } catch (Exception JavaDoc exc) {
3210                insert = clipboard;
3211            }
3212            if (insert != null) { // no text on system clipboard nor internal clipboard text
3213
insert = filter(insert, multiline);
3214            }
3215        }
3216        if (filter && (insert != null)) { // contributed by Michael Nascimento
3217
for (int i = insert.length() - 1; i >= 0; i--) {
3218                if (!Character.isDigit(insert.charAt(i))) { return false; }
3219            }
3220        }
3221        return changeField(component, text, insert, istart, iend, start, end);
3222    }
3223    
3224    /**
3225     * @param text
3226     * @param multiline
3227     * @return
3228     */

3229    private static String JavaDoc filter(String JavaDoc text, boolean multiline) {
3230        StringBuffer JavaDoc filtered = new StringBuffer JavaDoc(text.length());
3231        for (int i = 0; i < text.length(); i++) {
3232            char ckey = text.charAt(i);
3233            if (((ckey > 0x1f) && (ckey < 0x7f)) ||
3234                    ((ckey > 0x9f) && (ckey < 0xffff)) ||
3235                    (multiline && (ckey == '\n'))) {
3236                filtered.append(ckey);
3237            }
3238        }
3239        return (filtered.length() != text.length()) ? filtered.toString() : text;
3240    }
3241
3242    /**
3243     * @param component a textfield, passwordfield, textarea, combobox, or spinbox
3244     * @param text current text
3245     * @param insert a string to replace thr current selection
3246     * @param movestart new selection start position
3247     * @param moveend new caret (selection end) position
3248     * @param start current selection start position
3249     * @param end current caret position
3250     * @return true if selection, caret location, or text content changed
3251     */

3252    private boolean changeField(Object JavaDoc component, String JavaDoc text, String JavaDoc insert,
3253            int movestart, int moveend, int start, int end) {
3254        movestart = Math.max(0, Math.min(movestart, text.length()));
3255        moveend = Math.max(0, Math.min(moveend, text.length()));
3256        if ((insert == null) && (start == movestart) && (end == moveend)) {
3257            return false;
3258        }
3259        if (insert != null) {
3260            int min = Math.min(movestart, moveend);
3261            set(component, "text", text.substring(0, min) + insert +
3262                text.substring(Math.max(movestart, moveend)));
3263            movestart = moveend = min + insert.length();
3264            invoke(component, null, "action"); // deprecated
3265
}
3266        if (start != movestart) { setInteger(component, "start", movestart, 0); }
3267        if (end != moveend) { setInteger(component, "end", moveend, 0); }
3268        validate(component);
3269        invoke(component, null, (insert != null) ?
3270            ((insert.length() > 0) ? "insert" : "remove") : "caret");
3271        return true;
3272    }
3273
3274    /**
3275     *
3276     */

3277    private boolean processList(Object JavaDoc component, boolean shiftdown, boolean controldown,
3278            int keychar, int keycode, boolean recursive) {
3279        if ((keycode == KeyEvent.VK_UP) || // select previous/next/first/... item
3280
(keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
3281                (keycode == KeyEvent.VK_PAGE_DOWN) ||
3282                (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
3283            Object JavaDoc lead = get(component, ":lead");
3284            Object JavaDoc row = getListItem(component, component, keycode, lead, recursive);
3285            if (row != null) {
3286                String JavaDoc selection = getString(component, "selection", "single");
3287                if (shiftdown && (selection != "single") && (lead != null)) {
3288                    extend(component, lead, row, recursive);
3289                }
3290                else if (!controldown) {
3291                    selectItem(component, row, recursive);
3292                }
3293                setLead(component, lead, row);
3294                return true;
3295            }
3296        }
3297        else if (keycode == KeyEvent.VK_LEFT) {
3298            return processScroll(component, "left");
3299        }
3300        else if (keycode == KeyEvent.VK_RIGHT) {
3301            return processScroll(component, "right");
3302        }
3303        else if (keychar == KeyEvent.VK_SPACE) { // select the current item
3304
select(component, get(component, ":lead"), recursive, shiftdown, controldown); //...
3305
return true;
3306        }
3307        else if (controldown) {
3308            if (((keycode == KeyEvent.VK_A) || (keycode == 0xBF)) && //KeyEvent.VK_SLASH
3309
(getString(component, "selection", "single") != "single")) { // select all
3310
selectAll(component, true, recursive);
3311                return true;
3312            }
3313            else if (keycode == 0xDC) { //KeyEvent.VK_BACK_SLASH // deselect all
3314
selectAll(component, false, recursive);
3315                return true;
3316            }
3317        }
3318        else {
3319            Object JavaDoc item = findText((char) keychar, component, component, recursive);
3320            if (item != null) {
3321                select(component, item, recursive, false, false);
3322                return true;
3323            }
3324        }
3325        return false;
3326    }
3327    
3328    /**
3329     * Search for the next/first appropriate item starting with the collected string
3330     * or the given single character
3331     * @param keychar the last typed character
3332     * @param component a list, tree, table, or combobox
3333     * @param leadowner the list, tree, table, or the combobox's drop down list
3334     * @param recursive if the component is a tree
3335     * @return the appropriate item or null
3336     */

3337    private Object JavaDoc findText(char keychar, Object JavaDoc component,
3338            Object JavaDoc leadowner, boolean recursive) {
3339        if (keychar != 0) {
3340            long current = System.currentTimeMillis();
3341            int i = (current > findtime + 1000) ? 1 : 0; // clear the starting string after a second
3342
findtime = current;
3343            Object JavaDoc lead = get(leadowner, ":lead");
3344            for (; i < 2; i++) { // 0: find the long text, 1: the stating character only
3345
findprefix = (i == 0) ? (findprefix + keychar) : String.valueOf(keychar);
3346                for (int j = 0; j < 2; j++) { // 0: lead to last, 1: first to lead
3347
for (Object JavaDoc item = (j == 0) ? ((i == 0) ? lead : getNextItem(component, lead, recursive)) :
3348                            get(component, ":comp"); (j == 0) ? (item != null) : (item != lead);
3349                            item = getNextItem(component, item, recursive)) {
3350                        if (getString(item, "text", "").regionMatches(true,
3351                                0, findprefix, 0, findprefix.length())) { //table first column...
3352
return item;
3353                        }
3354                    }
3355                }
3356            }
3357        }
3358        return null;
3359    }
3360
3361    /**
3362     *
3363     */

3364    private Object JavaDoc getListItem(Object JavaDoc component, Object JavaDoc scrollpane,
3365            int keycode, Object JavaDoc lead, boolean recursive) {
3366        Object JavaDoc row = null;
3367        if (keycode == KeyEvent.VK_UP) {
3368            for (Object JavaDoc prev = get(component, ":comp"); prev != lead;
3369                    prev = getNextItem(component, prev, recursive)) {
3370                row = prev; // component -> getParent(lead)
3371
}
3372        }
3373        else if (keycode == KeyEvent.VK_DOWN) {
3374            row = (lead == null) ? get(component, ":comp") :
3375                getNextItem(component, lead, recursive);
3376        }
3377        else if ((keycode == KeyEvent.VK_PAGE_UP) ||
3378                (keycode == KeyEvent.VK_PAGE_DOWN)) {
3379            Rectangle view = getRectangle(scrollpane, ":view");
3380            Rectangle port = getRectangle(scrollpane, ":port");
3381            Rectangle rl = (lead != null) ? getRectangle(lead, "bounds") : null;
3382            int vy = (keycode == KeyEvent.VK_PAGE_UP) ?
3383                view.y : (view.y + port.height);
3384            if ((keycode == KeyEvent.VK_PAGE_UP) &&
3385                    (rl != null) && (rl.y <= view.y)) {
3386                vy -= port.height;
3387            }
3388            if ((keycode == KeyEvent.VK_PAGE_DOWN) &&
3389                    (rl != null) && (rl.y + rl.height >= view.y + port.height)) {
3390                vy += port.height;
3391            }
3392            for (Object JavaDoc item = get(component, ":comp"); item != null;
3393                    item = getNextItem(component, item, recursive)) {
3394                Rectangle r = getRectangle(item, "bounds");
3395                if (keycode == KeyEvent.VK_PAGE_UP) {
3396                    row = item;
3397                    if (r.y + r.height > vy) { break; }
3398                } else {
3399                    if (r.y > vy) { break; }
3400                    row = item;
3401                }
3402            }
3403        }
3404        else if (keycode == KeyEvent.VK_HOME) {
3405            row = get(component, ":comp");
3406        }
3407        else if (keycode == KeyEvent.VK_END) {
3408            for (Object JavaDoc last = lead; last != null;
3409                    last = getNextItem(component, last, recursive)) {
3410                row = last;
3411            }
3412        }
3413        return row;
3414    }
3415
3416    /**
3417     * Select all the items
3418     * @param component a list/tree/table
3419     * @param selected selects or deselects items
3420     * @param recursive true for tree
3421     */

3422    private void selectAll(Object JavaDoc component,
3423            boolean selected, boolean recursive) {
3424        boolean changed = false;
3425        for (Object JavaDoc item = get(component, ":comp");
3426                item != null; item = getNextItem(component, item, recursive)) {
3427            if (setBoolean(item, "selected", selected, false)) {
3428                repaint(component, null, item); changed = true;
3429            }
3430        }
3431        set(component, ":anchor", null);
3432        if (changed) { invoke(component, null, "action"); }
3433    }
3434
3435    /**
3436     * Select a single given item, deselect others
3437     * @param component a list/tree/table
3438     * @param row the item/node/row to select
3439     * @param recursive true for tree
3440     */

3441    private void selectItem(Object JavaDoc component, Object JavaDoc row, boolean recursive) {
3442        boolean changed = false;
3443        for (Object JavaDoc item = get(component, ":comp");
3444                item != null; item = getNextItem(component, item, recursive)) {
3445            if (setBoolean(item, "selected", (item == row), false)) {
3446                repaint(component, null, item); changed = true;
3447            }
3448        }
3449        set(component, ":anchor", null);
3450        if (changed) { invoke(component, row, "action"); }
3451    }
3452
3453    /**
3454     *
3455     */

3456    private void extend(Object JavaDoc component, Object JavaDoc lead,
3457            Object JavaDoc row, boolean recursive) {
3458        Object JavaDoc anchor = get(component, ":anchor");
3459        if (anchor == null) { set(component, ":anchor", anchor = lead); }
3460        char select = 'n'; boolean changed = false;
3461        for (Object JavaDoc item = get(component, ":comp"); // anchor - row
3462
item != null; item = getNextItem(component, item, recursive)) {
3463            if (item == anchor) select = (select == 'n') ? 'y' : 'r';
3464            if (item == row) select = (select == 'n') ? 'y' : 'r';
3465            if (setBoolean(item, "selected", (select != 'n'), false)) {
3466                repaint(component, null, item); changed = true;
3467            }
3468            if (select == 'r') select = 'n';
3469        }
3470        if (changed) { invoke(component, row, "action"); }
3471    }
3472
3473    /**
3474     * Update the lead item of a list/tree/table, repaint, and scroll
3475     * @param component a list, tree, or table
3476     * @param oldlead the current lead item
3477     * @param lead the new lead item
3478     */

3479    private void setLead(Object JavaDoc component, Object JavaDoc oldlead, Object JavaDoc lead) {
3480        if (oldlead != lead) { //?
3481
if (oldlead != null) { repaint(component, null, oldlead); }
3482            set(component, ":lead", lead);
3483            repaint(component, null, lead);
3484            
3485            Rectangle r = getRectangle(lead, "bounds");
3486            scrollToVisible(component, r.x, r.y, 0, r.height);
3487        }
3488    }
3489
3490    /**
3491     * Update the lead item of a combolist, repaint, and scroll
3492     * @param component a combobox drop down list
3493     * @param part the current hotspot item
3494     * @param scroll scroll to the part if true
3495     */

3496    private void setInside(Object JavaDoc component, Object JavaDoc part, boolean scroll) {
3497            Object JavaDoc previous = get(component, ":lead");
3498            if (previous != null) {
3499                repaint(component, ":combolist", previous);
3500            }
3501            set(component, ":lead", part);
3502            if (part != null) {
3503                repaint(component, ":combolist", part);
3504                if (scroll) {
3505                    Rectangle r = getRectangle(part, "bounds");
3506                    scrollToVisible(component, r.x, r.y, 0, r.height);
3507                }
3508            }
3509    }
3510            
3511    /**
3512     * @param x mouse x position relative to thinlet component
3513     * @param y mouse y position relative to the main desktop
3514     */

3515    private void handleMouseEvent(int x, int y, int clickcount,
3516            boolean shiftdown, boolean controldown, boolean popuptrigger,
3517            int id, Object JavaDoc component, Object JavaDoc part) {
3518        if (id == MouseEvent.MOUSE_ENTERED) {
3519            setTimer(750L);
3520        }
3521        else if (id == MouseEvent.MOUSE_EXITED) {
3522            hideTip();
3523        }
3524        if (!getBoolean(component, "enabled", true)) { return; }
3525        String JavaDoc classname = getClass(component);
3526        if (("button" == classname) ||
3527                ("checkbox" == classname) || ("togglebutton" == classname)) {
3528            if ((id == MouseEvent.MOUSE_ENTERED) ||
3529                    (id == MouseEvent.MOUSE_EXITED) ||
3530                    (id == MouseEvent.MOUSE_PRESSED) ||
3531                    (id == MouseEvent.MOUSE_RELEASED)) {
3532                if (id == MouseEvent.MOUSE_PRESSED) {
3533                    setFocus(component);
3534                }
3535                if (("button" == classname) &&
3536                        ((mousepressed == null) || (mousepressed == component)) &&
3537                        ((id == MouseEvent.MOUSE_ENTERED) ||
3538                            (id == MouseEvent.MOUSE_EXITED)) &&
3539                        (get(component, "type") == "link")) {
3540                    setCursor(Cursor.getPredefinedCursor(
3541                        (id == MouseEvent.MOUSE_ENTERED) ?
3542                            Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR));
3543                }
3544                else if ((id == MouseEvent.MOUSE_RELEASED) &&
3545                        (mouseinside == component)) {
3546                    if ("button" != classname) {
3547                        changeCheck(component, true);
3548                    }
3549                    else invoke(component, null, "action");
3550                }
3551                repaint(component);
3552            }
3553        }
3554        else if ("combobox" == classname) {
3555            boolean editable = getBoolean(component, "editable", true);
3556            if (editable && (part == null)) { // textfield area
3557
Image icon = null;
3558                int left = ((id == MouseEvent.MOUSE_PRESSED) &&
3559                    ((icon = getIcon(component, "icon", null)) != null)) ?
3560                        icon.getWidth(this) : 0;
3561                processField(x, y, clickcount, id, component, false, false, left);
3562            }
3563            else if (part != "icon") { // part = "down"
3564
if (((id == MouseEvent.MOUSE_ENTERED) ||
3565                        (id == MouseEvent.MOUSE_EXITED)) && (mousepressed == null)) {
3566                    if (editable) { repaint(component, "combobox", part); } // hover the arrow button
3567
else { repaint(component); } // hover the whole combobox
3568
}
3569                else if (id == MouseEvent.MOUSE_PRESSED) {
3570                    Object JavaDoc combolist = get(component, ":combolist");
3571                    if (combolist == null) { // combolist is closed
3572
setFocus(component);
3573                        repaint(component);
3574                        popupCombo(component);
3575                    } else { // combolist is visible
3576
closeCombo(component, combolist, null);
3577                    }
3578                }
3579                else if (id == MouseEvent.MOUSE_RELEASED) {
3580                    if (mouseinside != component) {
3581                        Object JavaDoc combolist = get(component, ":combolist");
3582                        closeCombo(component, combolist,
3583                            ((mouseinside == combolist) && (insidepart instanceof Object JavaDoc[])) ? insidepart : null);
3584                    } else {
3585                        repaint(component);
3586                    }
3587                }
3588            }
3589        }
3590        else if (":combolist" == classname) {
3591            if (!processScroll(x, y, id, component, part)) {
3592                if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) {
3593                    if (part != null) { //+ scroll if dragged
3594
setInside(component, part, false);
3595                    }
3596                }
3597                else if (id == MouseEvent.MOUSE_RELEASED) {
3598                    closeCombo(get(component, "combobox"), component, part);
3599                }
3600            }
3601        }
3602        else if (("textfield" == classname) || ("passwordfield" == classname)) {
3603            processField(x, y, clickcount, id, component,
3604                false, ("passwordfield" == classname), 0);
3605        }
3606        else if ("textarea" == classname) {
3607            if (!processScroll(x, y, id, component, part)) {
3608                processField(x, y, clickcount, id, component, true, false, 0);
3609            }
3610        }
3611        else if ("panel" == classname) {
3612            processScroll(x, y, id, component, part);
3613        }
3614        else if ("desktop" == classname) {
3615            if (part == "modal") {
3616                if (id == MouseEvent.MOUSE_ENTERED) {
3617                    setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
3618                }
3619                else if (id == MouseEvent.MOUSE_EXITED) {
3620                    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3621                }
3622            }
3623        }
3624        else if ("spinbox" == classname) {
3625            if (part == null) {
3626                processField(x, y, clickcount, id, component, false, false, 0);
3627            }
3628            else { // part = "up" || "down"
3629
if ((id == MouseEvent.MOUSE_ENTERED) ||
3630                        (id == MouseEvent.MOUSE_EXITED) ||
3631                        (id == MouseEvent.MOUSE_PRESSED) ||
3632                        (id == MouseEvent.MOUSE_RELEASED)) {
3633                    if (id == MouseEvent.MOUSE_PRESSED) {
3634                        setFocus(component);
3635                        if (processSpin(component, part)) { setTimer(375L); }
3636                        //settext: start end selection, parse exception...
3637
}
3638                    else {
3639                        if (id == MouseEvent.MOUSE_RELEASED) {
3640                            setTimer(0L);
3641                        }
3642                    }
3643                    repaint(component, classname, part);
3644                }
3645            }
3646        }
3647        else if ("tabbedpane" == classname) {
3648            if ((id == MouseEvent.MOUSE_ENTERED) ||
3649                    (id == MouseEvent.MOUSE_EXITED)) {
3650                if ((part != null) && getBoolean(part, "enabled", true) &&
3651                        (getInteger(component, "selected", 0) != getIndex(component, part))) {
3652                    repaint(component, "tabbedpane", part);
3653                }
3654            }
3655            else if ((part != null) && (id == MouseEvent.MOUSE_PRESSED) &&
3656                    getBoolean(part, "enabled", true)) {
3657                int selected = getInteger(component, "selected", 0);
3658                int current = getIndex(component, part);
3659                if (selected == current) {
3660                    setFocus(component);
3661                    repaint(component, "tabbedpane", part);
3662                }
3663                else {
3664                    setInteger(component, "selected", current, 0);
3665                    //Object tabcontent = getItem(component, current);
3666
//setFocus((tabcontent != null) ? tabcontent : component);
3667
setNextFocusable(component, false);
3668                    checkOffset(component);
3669                    repaint(component);
3670                    invoke(component, part, "action");
3671                }
3672            }
3673        }
3674        else if ("slider" == classname) {
3675            if ((id == MouseEvent.MOUSE_PRESSED) ||
3676                    (id == MouseEvent.MOUSE_DRAGGED)) {
3677                if (id == MouseEvent.MOUSE_PRESSED) {
3678                    setReference(component, block / 2, block / 2);
3679                    setFocus(component);
3680                }
3681                int minimum = getInteger(component, "minimum", 0);
3682                int maximum = getInteger(component, "maximum", 100);
3683                int value = getInteger(component, "value", 50);
3684                Rectangle bounds = getRectangle(component, "bounds");
3685                boolean horizontal = ("vertical" != get(component, "orientation"));
3686                int newvalue = minimum +
3687                    (horizontal ? (x - referencex) : (y - referencey)) *
3688                    (maximum - minimum) /
3689                    ((horizontal ? bounds.width : bounds.height) - block); //... +0.5
3690
newvalue = Math.max(minimum, Math.min(newvalue, maximum));
3691                if (value != newvalue) {
3692                    setInteger(component, "value", newvalue, 50);
3693                    invoke(component, null, "action");
3694                }
3695                if ((value != newvalue) || (id == MouseEvent.MOUSE_PRESSED)) {
3696                    repaint(component);
3697                }
3698            }
3699        }
3700        else if ("splitpane" == classname) {
3701            if (id == MouseEvent.MOUSE_PRESSED) {
3702                setReference(component, 2, 2);
3703            }
3704            else if (id == MouseEvent.MOUSE_DRAGGED) {
3705                int divider = getInteger(component, "divider", -1);
3706                boolean horizontal = ("vertical" != get(component, "orientation"));
3707                int moveto = horizontal ? (x - referencex) : (y - referencey);
3708                Rectangle bounds = getRectangle(component, "bounds");
3709                moveto = Math.max(0, Math.min(moveto,
3710                    Math.abs(horizontal ? bounds.width : bounds.height) - 5));
3711                if (divider != moveto) {
3712                    setInteger(component, "divider", moveto, -1);
3713                    validate(component);
3714                }
3715            }
3716            else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
3717                boolean horizontal = ("vertical" != get(component, "orientation"));
3718                setCursor(Cursor.getPredefinedCursor(horizontal ?
3719                    Cursor.E_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR));
3720            }
3721            else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
3722                    ((id == MouseEvent.MOUSE_RELEASED) && (mouseinside != component))) {
3723                setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3724            }
3725        }
3726        else if (("list" == classname) ||
3727                ("table" == classname) || ("tree" == classname)) {
3728            if (!processScroll(x, y, id, component, part)) {
3729                if (((id == MouseEvent.MOUSE_PRESSED)||
3730                        ((id == MouseEvent.MOUSE_DRAGGED) &&
3731                            !shiftdown && !controldown))) {
3732                    //Rectangle view = getRectangle(component, ":view");
3733
Rectangle port = getRectangle(component, ":port");
3734                    int my = y + port.y - referencey;
3735                    for (Object JavaDoc item = get(component, ":comp"); item != null;) {
3736                        Rectangle r = getRectangle(item, "bounds");
3737                        if (my < r.y + r.height) {
3738                            if (id == MouseEvent.MOUSE_DRAGGED) { //!!!
3739
scrollToVisible(component, r.x, r.y, 0, r.height);
3740                            }
3741                            else if ("tree" == classname) {
3742                                int mx = x + port.x - referencex;
3743                                if (mx < r.x) {
3744                                    if ((mx >= r.x - block) && (get(item, ":comp") != null)) {
3745                                        boolean expanded = getBoolean(item, "expanded", true);
3746                                        setBoolean(item, "expanded", !expanded, true);
3747                                        selectItem(component, item, true);
3748                                        setLead(component, get(component, ":lead"), item);
3749                                        setFocus(component);
3750                                        validate(component);
3751                                        invoke(component, item, expanded ? "collapse" : "expand"); //item
3752
}
3753                                    break;
3754                                }
3755                            }
3756                            if ((id != MouseEvent.MOUSE_DRAGGED) ||
3757                                    !getBoolean(item, "selected", false)) {
3758                                if (id != MouseEvent.MOUSE_DRAGGED) {
3759                                    if (setFocus(component)) { repaint(component, classname, item); } //?
3760
}
3761                                select(component, item, ("tree" == classname), shiftdown, controldown);
3762                                if (clickcount == 2) { invoke(component, item, "perform"); }
3763                            }
3764                            break;
3765                        }
3766                        item = getNextItem(component, item, ("tree" == classname));
3767                    }
3768                }
3769            }
3770        }
3771        else if ("menubar" == classname) {
3772            Object JavaDoc selected = get(component, "selected");
3773            if (((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_EXITED)) &&
3774                    (part != null) && (selected == null) && getBoolean(part, "enabled", true)) {
3775                repaint(component, classname, part);
3776            }
3777            else if ((part != null) && ((selected == null) ?
3778                    (id == MouseEvent.MOUSE_PRESSED) :
3779                        ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED))) &&
3780                    getBoolean(part, "enabled", true)) {
3781                    // || ((id == MouseEvent.MOUSE_PRESSED) && (insidepart != part))
3782
set(component, "selected", part);
3783                popupMenu(component);
3784                repaint(component, classname, part);
3785            }
3786            else if ((id == MouseEvent.MOUSE_PRESSED) && (selected != null)) {
3787                closeup();
3788            }
3789            else if (id == MouseEvent.MOUSE_RELEASED) {
3790                if ((part != insidepart) && ((insidepart == null) ||
3791                        ((insidepart instanceof Object JavaDoc[]) && (getClass(insidepart) != "menu")))) {
3792                    if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
3793                        if (getClass(insidepart) == "checkboxmenuitem") {
3794                            changeCheck(insidepart, false);
3795                        }
3796                        else invoke(insidepart, null, "action");
3797                    }
3798                    closeup();
3799                }
3800            }
3801        }
3802        else if (":popup" == classname) {
3803            if (part != null) {
3804                if (((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) &&
3805                        getBoolean(part, "enabled", true)) {
3806                    set(component, "selected", part);
3807                    popupMenu(component);
3808                    repaint(component, classname, part);
3809                }
3810                else if (id == MouseEvent.MOUSE_RELEASED) {
3811                    if ((insidepart == null) || (getClass(insidepart) != "menu")) {
3812                        if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
3813                            if (getClass(insidepart) == "checkboxmenuitem") {
3814                                changeCheck(insidepart, false);
3815                            }
3816                            else invoke(insidepart, null, "action");
3817                        }
3818                        closeup();
3819                    }
3820                }
3821                else if (((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) &&
3822                        getBoolean(part, "enabled", true)) {
3823                    if (getClass(part) != "menu") {
3824                        set(component, "selected", null);
3825                    }
3826                    repaint(component, classname, part);
3827                }
3828            }
3829        }
3830        else if ("dialog" == classname) {
3831            if (part == "header") {
3832                if (id == MouseEvent.MOUSE_PRESSED) {
3833                    Rectangle bounds = getRectangle(component, "bounds");
3834                    referencex = x - bounds.x; referencey = y - bounds.y;
3835                    Object JavaDoc parent = getParent(component);
3836                    if (get(parent, ":comp") != component) { // to front
3837
removeItemImpl(parent, component);
3838                        insertItem(parent, ":comp", component, 0);
3839                        set(component, ":parent", parent);
3840                        repaint(component); // to front always...
3841
setNextFocusable(component, false);
3842                    }
3843                }
3844                else if (id == MouseEvent.MOUSE_DRAGGED) {
3845                    Rectangle bounds = getRectangle(component, "bounds");
3846                    Rectangle parents = getRectangle(getParent(component), "bounds");
3847                    int mx = Math.max(0, Math.min(x - referencex, parents.width - bounds.width));
3848                    int my = Math.max(0, Math.min(y - referencey, parents.height - bounds.height));
3849                    if ((bounds.x != mx) || (bounds.y != my)) {
3850                        // repaint the union of the previous and next bounds
3851
repaint(component, Math.min(bounds.x, mx), Math.min(bounds.y, my),
3852                            bounds.width + Math.abs(mx - bounds.x), bounds.height + Math.abs(my - bounds.y));
3853                        bounds.x = mx; bounds.y = my;
3854                    }
3855                }
3856            }
3857            else if (!processScroll(x, y, id, component, part) && (part != null)) {
3858                if (id == MouseEvent.MOUSE_PRESSED) {
3859                    referencex = x; referencey = y;
3860                }
3861                else if (id == MouseEvent.MOUSE_DRAGGED) {
3862                    repaint(component);
3863                    Rectangle bounds = getRectangle(component, "bounds");
3864                    if ((part == ":nw") || (part == ":n") || (part == ":ne")) {
3865                        bounds.y += y - referencey; bounds.height -= y - referencey;
3866                    }
3867                    if ((part == ":ne") || (part == ":e") || (part == ":se")) {
3868                        bounds.width += x - referencex;
3869                    }
3870                    if ((part == ":sw") || (part == ":s") || (part == ":se")) {
3871                        bounds.height += y - referencey;
3872                    }
3873                    if ((part == ":nw") || (part == ":w") || (part == ":sw")) {
3874                        bounds.x += x - referencex; bounds.width -= x - referencex;
3875                    }
3876                    referencex = x; referencey = y;
3877                    doLayout(component); repaint(component);
3878                }
3879                else if (id == MouseEvent.MOUSE_ENTERED) {
3880                    setCursor(Cursor.getPredefinedCursor(
3881                        (part == ":n") ? Cursor.N_RESIZE_CURSOR :
3882                        (part == ":ne") ? Cursor.NE_RESIZE_CURSOR :
3883                        (part == ":e") ? Cursor.E_RESIZE_CURSOR :
3884                        (part == ":se") ? Cursor.SE_RESIZE_CURSOR :
3885                        (part == ":s") ? Cursor.S_RESIZE_CURSOR :
3886                        (part == ":sw") ? Cursor.SW_RESIZE_CURSOR :
3887                        (part == ":w") ? Cursor.W_RESIZE_CURSOR :
3888                            Cursor.NW_RESIZE_CURSOR));
3889                }
3890                else if (id == MouseEvent.MOUSE_EXITED) {
3891                    setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3892                }
3893            }
3894        }
3895        
3896        if (popuptrigger) {// && (id == MouseEvent.MOUSE_RELEASED)) {
3897
Object JavaDoc popupmenu = get(component, "popupmenu");
3898            if (popupmenu != null) {
3899                popupPopup(popupmenu, x, y);
3900            }
3901        }
3902    }
3903
3904    /**
3905     * Calculate the given point in a component relative to the thinlet desktop and
3906     * set as reference value
3907     * @param component a widget
3908     * @param x reference point relative to the component left edge
3909     * @param y relative to the top edge
3910     */

3911    private void setReference(Object JavaDoc component, int x, int y) {
3912        referencex = x; referencey = y;
3913        for (; component != null; component = getParent(component)) {
3914            Rectangle bounds = getRectangle(component, "bounds");
3915            referencex += bounds.x; referencey += bounds.y;
3916            
3917            Rectangle port = getRectangle(component, ":port");
3918            if (port != null) { // content scrolled
3919
Rectangle view = getRectangle(component, ":view");
3920                    referencex -= view.x - port.x; referencey -= view.y - port.y;
3921                }
3922        }
3923    }
3924
3925    /**
3926     *
3927     */

3928    private void select(Object JavaDoc component, Object JavaDoc row,
3929            boolean recursive, boolean shiftdown, boolean controldown) {
3930        String JavaDoc selection = getString(component, "selection", "single");
3931        Object JavaDoc lead = null;
3932        if (shiftdown && (selection != "single") &&
3933                ((lead = get(component, ":lead")) != null)) {
3934            extend(component, lead, row, recursive);
3935        }
3936        else {
3937            if (controldown && (selection == "multiple")) {
3938                setBoolean(row, "selected",
3939                    !getBoolean(row, "selected", false), false);
3940                repaint(component, null, row);
3941                invoke(component, row, "action");
3942                set(component, ":anchor", null);
3943            }
3944            else if (controldown && getBoolean(row, "selected", false)) {
3945                for (Object JavaDoc item = row;
3946                        item != null; item = getNextItem(component, item, recursive)) {
3947                    if (setBoolean(item, "selected", false, false)) {
3948                        repaint(component, null, item);
3949                    }
3950                }
3951                invoke(component, row, "action");
3952                set(component, ":anchor", null);
3953            }
3954            else {
3955                selectItem(component, row, recursive);
3956            }
3957        }
3958        setLead(component, (lead != null) ? lead : get(component, ":lead"), row);
3959    }
3960
3961    /**
3962     * Find the next item after the given
3963     * @param component a list/tree/table widget
3964     * @param item the next item after this, or the first if null
3965     * @param recursive true if tree
3966     * @return next (or first) item
3967     */

3968    private Object JavaDoc getNextItem(Object JavaDoc component,
3969            Object JavaDoc item, boolean recursive) {
3970        if (!recursive) { return get(item, ":next"); }
3971        Object JavaDoc next = get(item, ":comp");
3972        if ((next == null) || !getBoolean(item, "expanded", true)) {
3973            while ((item != component) && ((next = get(item, ":next")) == null)) {
3974                item = getParent(item);
3975            }
3976        }
3977        return next;
3978    }
3979    
3980    /**
3981     *
3982     */

3983    private void processField(int x, int y, int clickcount,
3984            int id, Object JavaDoc component,
3985            boolean multiline, boolean hidden, int left) {
3986        if (id == MouseEvent.MOUSE_PRESSED) {
3987            //+ middle=alt paste clipboard content
3988
setReference(component, 2 + left, 2);
3989            int mx = x - referencex;
3990            int my = 0;
3991            if (!multiline) {
3992                mx += getInteger(component, ":offset", 0);
3993            } else {
3994                Rectangle port = getRectangle(component, ":port");
3995                mx += port.x - 1;
3996                my = y - referencey + port.y - 1;
3997            }
3998            int caretstart = getCaretLocation(component, mx, my, multiline, hidden);
3999            int caretend = caretstart;
4000            if (clickcount > 1) {
4001                String JavaDoc text = getString(component, "text", "");
4002                while ((caretstart > 0) && ((clickcount == 2) ?
4003                    Character.isLetterOrDigit(text.charAt(caretstart - 1)) :
4004                        (text.charAt(caretstart - 1) != '\n'))) { caretstart--; }
4005                while ((caretend < text.length()) && ((clickcount == 2) ?
4006                    Character.isLetterOrDigit(text.charAt(caretend)) :
4007                        (text.charAt(caretend) != '\n'))) { caretend++; }
4008            }
4009            setInteger(component, "start", caretstart, 0);
4010            setInteger(component, "end", caretend, 0);
4011            setFocus(component);
4012            validate(component); // caret check only
4013
}
4014        else if (id == MouseEvent.MOUSE_DRAGGED) {
4015            int mx = x - referencex;
4016            int my = 0;
4017            if (!multiline) {
4018                mx += getInteger(component, ":offset", 0);
4019            } else {
4020                Rectangle port = getRectangle(component, ":port");
4021                mx += port.x - 1;
4022                my = y - referencey + port.y - 1;
4023            }
4024            int dragcaret = getCaretLocation(component, mx, my, multiline, hidden);
4025            if (dragcaret != getInteger(component, "end", 0)) {
4026                setInteger(component, "end", dragcaret, 0);
4027                validate(component); // caret check only
4028
}
4029        }
4030        else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
4031            setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
4032        }
4033        else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
4034            ((id == MouseEvent.MOUSE_RELEASED) &&
4035                ((mouseinside != component) || (insidepart != null)))) {
4036            setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
4037        }
4038    }
4039
4040    /**
4041     *
4042     */

4043    private int getCaretLocation(Object JavaDoc component,
4044            int x, int y, boolean multiline, boolean hidden) {
4045        Font currentfont = (Font) get(component, "font");
4046        FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
4047        char[] chars = multiline ? ((char[]) get(component, ":text")) :
4048            getString(component, "text", "").toCharArray(); // update it
4049
int linestart = 0;
4050        if (multiline) {
4051            int height = fm.getHeight(); // find the line start by y value
4052
for (int i = 0; (y >= height) && (i < chars.length); i++) {
4053                if ((chars[i] == '\n') || (chars[i] == '\t')) {
4054                    linestart = i + 1; y -= height;
4055                }
4056            }
4057        }
4058        for (int i = linestart; i < chars.length; i++) {
4059            if ((chars[i] == '\n') || (chars[i] == '\t')) { return i; }
4060            int charwidth = fm.charWidth(hidden ? '*' : chars[i]);
4061            if (x <= (charwidth / 2)) { return i; }
4062            x -= charwidth;
4063        }
4064        return chars.length;
4065    }
4066
4067    /**
4068     *
4069     */

4070    private boolean processScroll(int x, int y,
4071            int id, Object JavaDoc component, Object JavaDoc part) {
4072        if ((part == "up") || (part == "down") ||
4073                (part == "left") || (part == "right")) {
4074            if ((id == MouseEvent.MOUSE_ENTERED) ||
4075                    (id == MouseEvent.MOUSE_EXITED) ||
4076                    (id == MouseEvent.MOUSE_PRESSED) ||
4077                    (id == MouseEvent.MOUSE_RELEASED)) {
4078                if (id == MouseEvent.MOUSE_PRESSED) {
4079                    if (processScroll(component, part)) {
4080                        setTimer(300L); return true;
4081                    }
4082                }
4083                else {
4084                    if (id == MouseEvent.MOUSE_RELEASED) { setTimer(0L); }
4085                    repaint(component, null, part);
4086                }
4087            }
4088        }
4089        else if ((part == "uptrack") || (part == "downtrack") ||
4090                (part == "lefttrack") || (part == "righttrack")) {
4091            if (id == MouseEvent.MOUSE_PRESSED) {
4092                if (processScroll(component, part)) {
4093                    setTimer(300L);
4094                }
4095            }
4096            else if (id == MouseEvent.MOUSE_RELEASED) {
4097                setTimer(0L);
4098            }
4099        }
4100        else if ((part == "vknob") || (part == "hknob")) {
4101            if (id == MouseEvent.MOUSE_PRESSED) {
4102                Rectangle port = getRectangle(component, ":port");
4103                Rectangle view = getRectangle(component, ":view");
4104                if (part == "hknob") {
4105                    referencex = x - view.x * (port.width - 2 * block) / view.width;
4106                } else {
4107                    referencey = y - view.y * (port.height - 2 * block) / view.height;
4108                }
4109            }
4110            else if (id == MouseEvent.MOUSE_DRAGGED) {
4111                Rectangle port = getRectangle(component, ":port");
4112                Rectangle view = getRectangle(component, ":view");
4113                if (part == "hknob") {
4114                    int viewx = (x - referencex) * view.width / (port.width - 2 * block);
4115                    viewx = Math.max(0, Math.min(viewx, view.width - port.width));
4116                    if (view.x != viewx) {
4117                        view.x = viewx;
4118                        repaint(component, null, "horizontal");
4119                    }
4120                }
4121                else { // (part == "vknob")
4122
int viewy = (y - referencey) * view.height / (port.height - 2 * block);
4123                    viewy = Math.max(0, Math.min(viewy, view.height - port.height));
4124                    if (view.y != viewy) {
4125                        view.y = viewy;
4126                        repaint(component, null, "vertical");
4127                    }
4128                }
4129            }
4130        }
4131        else if (part == "corner") {
4132                part = "corner"; // compiler bug
4133
}
4134        else { //?
4135
if (id == MouseEvent.MOUSE_PRESSED) {
4136                Rectangle port = getRectangle(component, ":port");
4137                if (port != null) { setReference(component, port.x, port.y); }
4138            }
4139            return false;
4140        }
4141        return true;
4142    }
4143
4144    /**
4145     *
4146     */

4147    private boolean processScroll(Object JavaDoc component, Object JavaDoc part) {
4148        Rectangle view = getRectangle(component, ":view");
4149        Rectangle port = ((part == "left") || (part == "up")) ? null :
4150            getRectangle(component, ":port");
4151        int dx = 0; int dy = 0;
4152        if (part == "left") { dx = -10; }
4153        else if (part == "lefttrack") { dx = -port.width; }
4154        else if (part == "right") { dx = 10; }
4155        else if (part == "righttrack") { dx = port.width; }
4156        else if (part == "up") { dy = -10; }
4157        else if (part == "uptrack") { dy = -port.height; }
4158        else if (part == "down") { dy = 10; }
4159        else if (part == "downtrack") { dy = port.height; }
4160        if (dx != 0) {
4161            dx = (dx < 0) ? Math.max(-view.x, dx) :
4162                Math.min(dx, view.width - port.width - view.x);
4163        }
4164        else if (dy != 0) {
4165            dy = (dy < 0) ? Math.max(-view.y, dy) :
4166                Math.min(dy, view.height - port.height - view.y);
4167        }
4168        else return false;
4169        if ((dx == 0) && (dy == 0)) { return false; }
4170        view.x += dx; view.y += dy;
4171        repaint(component, null, (dx != 0) ? "horizontal" : "vertical");
4172        return (((part == "left") || (part == "lefttrack")) && (view.x > 0)) ||
4173            (((part == "right") || (part == "righttrack")) &&
4174                (view.x < view.width - port.width)) ||
4175            (((part == "up") || (part == "uptrack")) && (view.y > 0)) ||
4176            (((part == "down") || (part == "downtrack")) &&
4177                (view.y < view.height - port.height));
4178    }
4179
4180    /**
4181     *
4182     */

4183    private boolean processSpin(Object JavaDoc component, Object JavaDoc part) {
4184        String JavaDoc text = getString(component, "text", "");
4185        try {
4186            int itext = Integer.parseInt(text);
4187            int step = getInteger(component, "step", 1);
4188            if ((part == "up") ?
4189                    (itext + step <= getInteger(component, "maximum", Integer.MAX_VALUE)) :
4190                    (itext - step >= getInteger(component, "minimum", Integer.MIN_VALUE))) {
4191                String JavaDoc value = String.valueOf((part == "up") ? (itext + step) : (itext - step));
4192                setString(component, "text", value, null);
4193                setInteger(component, "start", value.length(), 0);
4194                setInteger(component, "end", 0, 0);
4195                repaint(component, "spinbox", "text");
4196                invoke(component, null, "action");
4197                return true;
4198            }
4199        } catch (NumberFormatException JavaDoc nfe) {}
4200        return false;
4201    }
4202
4203    /**
4204     *
4205     */

4206    private boolean invoke(Object JavaDoc component, Object JavaDoc part, String JavaDoc event) {
4207        Object JavaDoc method = get(component, event);
4208        if (method != null) {
4209            invokeImpl(method, part);
4210            return true;
4211        }
4212        return false;
4213    }
4214    
4215    /**
4216     *
4217     */

4218    private void invokeImpl(Object JavaDoc method, Object JavaDoc part) {
4219        Object JavaDoc[] data = (Object JavaDoc[]) method;
4220        Object JavaDoc[] args = (data.length > 2) ? new Object JavaDoc[(data.length - 2) / 3] : null;
4221        if (args != null) for (int i = 0; i < args.length; i++) {
4222            Object JavaDoc target = data[2 + 3 * i];
4223            if ("thinlet" == target) {
4224                args[i] = this;
4225            }
4226            else {
4227                if ("item" == target) { target = part; }
4228                Object JavaDoc parametername = data[2 + 3 * i + 1];
4229                if (parametername == null) {
4230                    args[i] = target;
4231                    //args[i] = new Widget(this, target);
4232
}
4233                else {
4234                    args[i] = (target != null) ? get(target, parametername) : null;
4235                    if (args[i] == null) { args[i] = data[2 + 3 * i + 2]; }
4236                }
4237            }
4238        }
4239        try {
4240            ((Method) data[1]).invoke(data[0], args);
4241        } catch (InvocationTargetException ite) {
4242            handleException(ite.getTargetException());
4243        } catch (Throwable JavaDoc throwable) {
4244            handleException(throwable);
4245        }
4246    }
4247    
4248    /**
4249     * Overwrite this method to handle exceptions thrown
4250     * by the invoked custom methods
4251     *
4252     * @param throwable the thrown exception by the bussiness logic
4253     */

4254    protected void handleException(Throwable JavaDoc throwable) {
4255        throwable.printStackTrace();
4256    }
4257
4258    /**
4259     *
4260     */

4261    private boolean findComponent(Object JavaDoc component, int x, int y) {
4262        if (component == content) {
4263            mouseinside = insidepart = null;
4264            mousex = x; mousey = y;
4265        }
4266        if (!getBoolean(component, "visible", true)) { return false; }
4267        Rectangle bounds = getRectangle(component, "bounds");
4268        if ((bounds == null) || !(bounds.contains(x, y))) { return false; }
4269        mouseinside = component;
4270        x -= bounds.x; y -= bounds.y;
4271        String JavaDoc classname = getClass(component);
4272
4273        if ("combobox" == classname) {
4274            if (getBoolean(component, "editable", true) && (x <= bounds.width - block)) {
4275                Image icon = getIcon(component, "icon", null);
4276                insidepart = ((icon != null) && (x <= 2 + icon.getWidth(this))) ?
4277                    "icon" : null;
4278            } else {
4279                insidepart = "down";
4280            }
4281        }
4282        else if (":combolist" == classname) {
4283            if (!findScroll(component, x, y)) {
4284                y += getRectangle(component, ":view").y;
4285                for (Object JavaDoc choice = get(get(component, "combobox"), ":comp");
4286                        choice != null; choice = get(choice, ":next")) {
4287                    Rectangle r = getRectangle(choice, "bounds");
4288                    if ((y >= r.y) && (y < r.y + r.height)) {
4289                        insidepart = choice; break;
4290                    }
4291                }
4292            }
4293        }
4294        else if ("textarea" == classname) {
4295            findScroll(component, x, y);
4296        }
4297        else if ("tabbedpane" == classname) {
4298            int selected = getInteger(component, "selected", 0);
4299            int i = 0;
4300            for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
4301                Rectangle r = getRectangle(tab, "bounds");
4302                if (i == selected) {
4303                    Object JavaDoc tabcontent = get(tab, ":comp");
4304                    if ((tabcontent != null) && findComponent(tabcontent, x - r.x, y - r.y)) {
4305                        break;
4306                    }
4307                }
4308                if (r.contains(x, y)) {
4309                    insidepart = tab; break;
4310                }
4311                i++;
4312            }
4313        }
4314        else if (("panel" == classname) || ("desktop" == classname) ||
4315                ("dialog" == classname)) {
4316            if ("dialog" == classname) {
4317                boolean resizable = getBoolean(component, "resizable", false);
4318                if (resizable && (x < 4)) {
4319                    insidepart = (y < block) ? ":nw" :
4320                        (y >= bounds.height - block) ? ":sw" : ":w";
4321                }
4322                else if (resizable && (y < 4)) {
4323                    insidepart = (x < block) ? ":nw" :
4324                        (x >= bounds.width - block) ? ":ne" : ":n";
4325                }
4326                else if (resizable && (x >= bounds.width - 4)) {
4327                    insidepart = (y < block) ? ":ne" :
4328                        (y >= bounds.height - block) ? ":se" : ":e";
4329                }
4330                else if (resizable && (y >= bounds.height - 4)) {
4331                    insidepart = (x < block) ? ":sw" :
4332                        (x >= bounds.width - block) ? ":se" : ":s";
4333                }
4334                else {
4335                    int titleheight = getInteger(component, ":titleheight", 0);
4336                    if (y < 4 + titleheight) {
4337                        insidepart = "header";
4338                    }
4339                }
4340            }
4341            if ((insidepart == null) && !findScroll(component, x, y)) {
4342                Rectangle port = getRectangle(component, ":port");
4343                if (port != null) { // content scrolled
4344
Rectangle view = getRectangle(component, ":view");
4345                    x += view.x - port.x; y += view.y - port.y;
4346                }
4347                for (Object JavaDoc comp = get(component, ":comp");
4348                        comp != null; comp = get(comp, ":next")) {
4349                    if (findComponent(comp, x, y)) { break; }
4350                    if (("desktop" == classname) &&
4351                            getBoolean(comp, "modal", false)) { insidepart = "modal"; break; } // && dialog
4352
}
4353            }
4354        }
4355        else if ("spinbox" == classname) {
4356            insidepart = (x <= bounds.width - block) ? null :
4357                ((y <= bounds.height / 2) ? "up" : "down");
4358        }
4359        else if ("splitpane" == classname) {
4360            Object JavaDoc comp1 = get(component, ":comp");
4361            if (comp1 != null) {
4362                if (!findComponent(comp1, x, y)) {
4363                    Object JavaDoc comp2 = get(comp1, ":next");
4364                    if (comp2 != null) {
4365                        findComponent(comp2, x, y);
4366                    }
4367                }
4368            }
4369        }
4370        else if ("list" == classname) {
4371            findScroll(component, x, y);
4372        }
4373        else if ("table" == classname) {
4374            if (!findScroll(component, x, y)) {
4375            }
4376        }
4377        else if ("tree" == classname) {
4378            findScroll(component, x, y);
4379        }
4380        else if ("menubar" == classname) {
4381            for (Object JavaDoc menu = get(component, ":comp");
4382                    menu != null; menu = get(menu, ":next")) {
4383                Rectangle r = getRectangle(menu, "bounds");
4384                if ((x >= r.x) && (x < r.x + r.width)) {
4385                    insidepart = menu; break;
4386                }
4387            }
4388        }
4389        else if (":popup" == classname) {
4390            for (Object JavaDoc menu = get(get(component, "menu"), ":comp");
4391                    menu != null; menu = get(menu, ":next")) {
4392                Rectangle r = getRectangle(menu, "bounds");
4393                if ((y >= r.y) && (y < r.y + r.height)) {
4394                    insidepart = menu; break;
4395                }
4396            }
4397        }
4398        return true;
4399    }
4400
4401    /**
4402     * @param component a scrollable widget
4403     * @param x point x location
4404     * @param y point y location
4405     * @return true if the point (x, y) is inside scroll-control area
4406     * (scrollbars, corners, borders), false otherwise (vievport, header, or no scrollpane)
4407     */

4408    private boolean findScroll(Object JavaDoc component, int x, int y) {
4409        Rectangle port = getRectangle(component, ":port");
4410        if ((port == null) || port.contains(x, y)) { return false; }
4411        Rectangle view = getRectangle(component, ":view");
4412        Rectangle horizontal = getRectangle(component, ":horizontal");
4413        Rectangle vertical = getRectangle(component, ":vertical");
4414        if ((horizontal != null) && horizontal.contains(x, y)) {
4415            findScroll(x - horizontal.x, horizontal.width, port.width, view.x, view.width, true);
4416        }
4417        else if ((vertical != null) && vertical.contains(x, y)) {
4418            findScroll(y - vertical.y, vertical.height, port.height, view.y, view.height, false);
4419        }
4420        else { insidepart = "corner"; }
4421        return true;
4422    }
4423    
4424    /**
4425     * @param p x or y relative to the scrollbar begin
4426     * @param size scrollbar width or height
4427     * @param portsize viewport width or height
4428     * @param viewp view x or y
4429     * @param viewsize view width or height
4430     * @param horizontal if true horizontal, vertical otherwise
4431     */

4432    private void findScroll(int p, int size, int portsize, int viewp, int viewsize, boolean horizontal) {
4433        if (p < block) { insidepart = horizontal ? "left" : "up"; }
4434        else if (p > size - block) { insidepart = horizontal ? "right" : "down"; }
4435        else {
4436            int track = size - 2 * block;
4437            if (track < 10) { insidepart = "corner"; return; } // too small
4438
int knob = Math.max(track * portsize / viewsize, 10);
4439            int decrease = viewp * (track - knob) / (viewsize - portsize);
4440            if (p < block + decrease) { insidepart = horizontal ? "lefttrack" : "uptrack"; }
4441            else if (p < block + decrease + knob) { insidepart = horizontal ? "hknob" : "vknob"; }
4442            else { insidepart = horizontal ? "righttrack" : "downtrack"; }
4443        }
4444    }
4445
4446    /**
4447     *
4448     */

4449    private void repaint(Object JavaDoc component, Object JavaDoc classname, Object JavaDoc part) {
4450        Rectangle b = getRectangle(component, "bounds");
4451        if (classname == "combobox") { // combobox down arrow
4452
repaint(component, b.x + b.width - block, b.y, block, b.height); // icon?+
4453
}
4454        else if (classname == "spinbox") {
4455            if (part == "text") { // spinbox textfield content
4456
repaint(component, b.x, b.y, b.width - block, b.height);
4457            }
4458            else { // spinbox increase or decrease button
4459
repaint(component, b.x + b.width - block,
4460                    (part == "up") ? b.y : (b.y + b.height - b.height / 2), block, b.height / 2);
4461            }
4462        }
4463        //else if (classname == "dialog") {}
4464
//int titleheight = getInteger(component, ":titleheight", 0);
4465
//else if (classname == "splitpane") {}
4466
else if ((classname == "tabbedpane") || // tab
4467
(classname == "menubar") || (classname == ":popup")) { // menuitem
4468
Rectangle r = getRectangle(part, "bounds");
4469            repaint(component, b.x + r.x, b.y + r.y,
4470                (classname == ":popup") ? b.width : r.width, r.height);
4471        }
4472        // classname: ":combolist" "textarea" "list" "table" "tree"
4473
else if ((part == "left") || (part == "right")) { // horizontal scrollbar button
4474
Rectangle r = getRectangle(component, ":horizontal");
4475            repaint(component, b.x + ((part == "left") ? r.x : (r.x + r.width - block)), b.y + r.y, block, r.height);
4476        }
4477        else if ((part == "up") || (part == "down")) { // vertical scrollbar button
4478
Rectangle r = getRectangle(component, ":vertical");
4479            repaint(component, b.x + r.x, b.y + ((part == "up") ? r.y : (r.y + r.height - block)), r.width, block);
4480        }
4481        else if ((part == "text") || (part == "horizontal") || (part == "vertical")) {
4482            Rectangle port = getRectangle(component, ":port"); // textarea or content
4483
repaint(component, b.x + port.x, b.y + port.y, port.width, port.height);
4484            if (part == "horizontal") {
4485                Rectangle r = getRectangle(component, ":horizontal");
4486                repaint(component, b.x + r.x, b.y + r.y, r.width, r.height);
4487                repaint(component, b.x + r.x, b.y, r.width, port.y); // paint header too
4488
}
4489            else if (part == "vertical") {
4490                Rectangle r = getRectangle(component, ":vertical");
4491                repaint(component, b.x + r.x, b.y + r.y, r.width, r.height);
4492            }
4493        }
4494        else { // repaint the whole line of its subcomponent
4495
Rectangle port = getRectangle(component, ":port");
4496            Rectangle view = getRectangle(component, ":view");
4497            Rectangle r = getRectangle(part, "bounds");
4498            if ((r.y + r.height >= view.y) && (r.y <= view.y + port.height)) {
4499                repaint(component, b.x + port.x, b.y + port.y - view.y + r.y,
4500                    port.width, r.height);
4501                //? need cut item rectangle above/bellow viewport
4502
}
4503        }
4504    }
4505    
4506    /**
4507     * Layout and paint the given component later
4508     * @param component
4509     */

4510    private void validate(Object JavaDoc component) {
4511        repaint(component);
4512        Rectangle bounds = getRectangle(component, "bounds");
4513        if (bounds != null) { bounds.width = -1 * Math.abs(bounds.width); }
4514    }
4515    
4516    /**
4517     * Repaint the given component's area later
4518     * @param component a visible widget inside thinlet desktop
4519     */

4520    public void repaint(Object JavaDoc component) {
4521        Rectangle bounds = getRectangle(component, "bounds");
4522        if (bounds != null) {
4523            repaint(component, bounds.x, bounds.y, bounds.width, bounds.height);
4524        }
4525    }
4526
4527    /**
4528     * Repaint the given component's area later
4529     * @param component
4530     * @param x
4531     * @param y
4532     * @param width
4533     * @param height
4534     */

4535    private void repaint(Object JavaDoc component, int x, int y, int width, int height) {
4536        while ((component = getParent(component)) != null) {
4537            Rectangle bounds = getRectangle(component, "bounds");
4538            x += bounds.x; y += bounds.y;
4539            Rectangle view = getRectangle(component, ":view");
4540            if (view != null) {
4541                Rectangle port = getRectangle(component, ":port");
4542                x += -view.x + port.x; y += -view.y + port.y; //+ clip :port
4543
}
4544        }
4545        repaint(x, y, width, height);
4546    }
4547
4548    /**
4549     * Requests that both the <i>Thinlet</i> component,
4550     * and the given widget get the input focus
4551     *
4552     * @param component a focusable widget inside
4553     * visible and enabled parents, and tabbedpane's selected tab
4554     * @return true, if the given component was focusable
4555     */

4556    public boolean requestFocus(Object JavaDoc component) { //#
4557
if (isFocusable(component, true)) {
4558            setFocus(component);
4559            repaint(component);
4560            return true;
4561        }
4562        return false;
4563    }
4564
4565    /**
4566     * Request focus for the given component
4567     * @param component a focusable component
4568     * @return true if the focusowner was changed, otherwise false
4569     */

4570    private boolean setFocus(Object JavaDoc component) {
4571        if (!focusinside) { // request focus for the thinlet component
4572
requestFocus();
4573        }
4574        if (focusowner != component) {
4575            Object JavaDoc focused = focusowner;
4576            if (focusowner != null) {
4577                focusowner = null; // clear focusowner
4578
repaint(focused);
4579                // invoke the focus listener of the previously focused component
4580
invoke(focused, null, "focuslost");
4581            }
4582            if(focusowner == null) { // it won't be null, if refocused
4583
focusowner = component;
4584                // invoke the focus listener of the new focused component
4585
invoke(component, null, "focusgained");
4586            }
4587            return true;
4588        }
4589        return false;
4590    }
4591    
4592    /**
4593     * @return next focusable component is found (not the first of the desktop/dialog)
4594     */

4595    private boolean setNextFocusable(Object JavaDoc current, boolean outgo) {
4596        boolean consumed = true;
4597        for (Object JavaDoc next = null, component = current; true; component = next) {
4598            next = get(component, ":comp"); // check first subcomponent
4599
if (next == null) { next = get(component, ":next"); } // check next component
4600
while (next == null) { // find the next of the parents, or the topmost
4601
component = getParent(component); // current is not on the desktop
4602
if (component == null) { return false; }
4603                if ((component == content) || ((getClass(component) == "dialog") &&
4604                        (!outgo || getBoolean(component, "modal", false)))) {
4605                    consumed = false; // find next focusable but does not consume event
4606
next = component; // the topmost (desktop or modal dialog)
4607
}
4608                else {
4609                    next = get(component, ":next");
4610                }
4611            }
4612            if (next == current) { return false; } // one fucusable, no loop
4613
if (isFocusable(next, false)) {
4614                setFocus(next);
4615                return consumed;
4616            }
4617        }
4618    }
4619
4620    /**
4621     * @return previous focusable component is found (not the last of the desktop/dialog)
4622     */

4623    private boolean setPreviousFocusable(Object JavaDoc component, boolean outgo) {
4624        for (int i = 0; i < 2; i++) { // 0 is backward direction
4625
Object JavaDoc previous = getPreviousFocusable(component, null, true, false, (i == 0), outgo);
4626            if (previous != null) {
4627                setFocus(previous);
4628                return (i == 0);
4629            }
4630        }
4631        return false;
4632    }
4633    
4634    /**
4635     * For the starting component search its parent direction for a focusable component, and then
4636     * its next component (if not search backward from the component).<br />
4637     * For its parent components check its first component, the current one, and its parent direction
4638     * (backward search), or its parent, then next component (forward direction).<br />
4639     * For the rest components check the next, then the first subcomponent direction, and finally
4640     * check whether the component is focusable.
4641     */

4642    private Object JavaDoc getPreviousFocusable(Object JavaDoc component,
4643            Object JavaDoc block, boolean start, boolean upward, boolean backward, boolean outgo) {
4644        Object JavaDoc previous = null;
4645        if ((component != null) && (component != block)) {
4646            boolean go = ((getClass(component) != "dialog") ||
4647                (outgo && !getBoolean(component, "modal", false)));
4648            if (!start && !upward && go) {
4649                previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
4650            }
4651            if ((previous == null) && ((upward && backward) || (!start && !upward))) {
4652                previous = getPreviousFocusable(get(component, ":comp"), block, false, false, backward, outgo);
4653                if ((previous == null) && isFocusable(component, false)) {
4654                    previous = component;
4655                }
4656            }
4657            if ((previous == null) && (start || upward) && go) {
4658                previous = getPreviousFocusable(getParent(component), component, false, true, backward, outgo);
4659            }
4660            if ((previous == null) && (start || upward) && !backward && go) {
4661                previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
4662            }
4663        }
4664        return previous;
4665    }
4666    
4667    /**
4668     * Check whether the given widget can become focusowner
4669     * @param component check this widget
4670     * @param forced splitpane is also checked
4671     * (e.g. false for tab navigating, and true for mouse selection or application request)
4672     * @return true if focusable, otherwise false
4673     */

4674    private boolean isFocusable(Object JavaDoc component, boolean forced) {
4675        String JavaDoc classname = getClass(component);
4676        if ((classname == "button") || (classname == "checkbox") || ("togglebutton" == classname) ||
4677                (classname == "combobox") || (classname == "textfield") ||
4678                (classname == "passwordfield") || (classname == "textarea") ||
4679                (classname == "spinbox") || (classname == "slider") ||
4680                (classname == "list") || (classname == "table") || (classname == "tree") ||
4681                (classname == "tabbedpane") || (forced && (classname == "splitpane"))) {
4682            for (Object JavaDoc comp = component; comp != null;) {
4683                // component and parents are enabled and visible
4684
if (!getBoolean(comp, "enabled", true) || !getBoolean(comp, "visible", true)) {
4685                    return false;
4686                }
4687                Object JavaDoc parent = getParent(comp);
4688                // inside the selected tabbedpane tab
4689
if ((getClass(comp) == "tab") && (getItem(parent,
4690                    getInteger(parent, "selected", 0)) != comp)) { return false; }
4691                comp = parent;
4692            }
4693            return true;
4694        }
4695        return false;
4696    }
4697
4698    // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
4699

4700    /**
4701     * Creates a new component
4702     *
4703     * @param classname the widget type (e.g. <i>button</i>)
4704     * @return a new component, every component is simply an <i>Object</i>
4705     * @throws java.lang.IllegalArgumentException for unknown widget type
4706     */

4707    public static Object JavaDoc create(String JavaDoc classname) { //#
4708
for (int i = 0; i < dtd.length; i += 3) {
4709            if (dtd[i].equals(classname)) {
4710                return createImpl((String JavaDoc) dtd[i]);
4711            }
4712        }
4713        throw new IllegalArgumentException JavaDoc("unknown " + classname);
4714    }
4715    
4716    /**
4717     * Gets the type of the given component
4718     *
4719     * @param component a widget
4720     * @return the class name of the component (e.g. <i>button</i>)
4721     */

4722    public static String JavaDoc getClass(Object JavaDoc component) { //#
4723
return (String JavaDoc) get(component, ":class");
4724    }
4725
4726    /**
4727     * Get the topmost component
4728     *
4729     * @return the root object (it is a <i>desktop</i>), never <i>null</i>
4730     */

4731    public Object JavaDoc getDesktop() {//#
4732
return content;
4733    }
4734
4735    /**
4736     *
4737     */

4738    private static Object JavaDoc createImpl(String JavaDoc classname) {
4739        return new Object JavaDoc[] { ":class", classname, null };
4740    }
4741    
4742    /**
4743     *
4744     */

4745    private static boolean set(Object JavaDoc component, Object JavaDoc key, Object JavaDoc value) {
4746        Object JavaDoc[] previous = (Object JavaDoc[]) component;
4747        for (Object JavaDoc[] entry = previous; entry != null;
4748                entry = (Object JavaDoc[]) entry[2]) {
4749            if (entry[0] == key) {
4750                if (value != null) { // set the row's value
4751
Object JavaDoc oldvalue = entry[1];
4752                    entry[1] = value;
4753                    return !value.equals(oldvalue);
4754                }
4755                else { // remove the row
4756
previous[2] = entry[2];
4757                    entry[2] = null;
4758                    return true;
4759                }
4760            }
4761            previous = entry;
4762        }
4763        if (value != null) { // append a new row
4764
previous[2] = new Object JavaDoc[] { key, value, null };
4765            return true;
4766        }
4767        return false;
4768    }
4769
4770    /**
4771     *
4772     */

4773    private static Object JavaDoc get(Object JavaDoc component, Object JavaDoc key) {
4774        for (Object JavaDoc[] entry = (Object JavaDoc[]) component; entry != null;
4775                entry = (Object JavaDoc[]) entry[2]) {
4776            if (entry[0] == key) {
4777                return entry[1];
4778            }
4779        }
4780        return null;
4781    }
4782
4783    /**
4784     * Gets the count of subcomponents in the list of the given component
4785     *
4786     * @param component a widget
4787     * @return the number of components in this component
4788     */

4789    public int getCount(Object JavaDoc component) {
4790        return getItemCountImpl(component, ":comp");
4791    }
4792    
4793    /**
4794     * Gets the parent of this component
4795     *
4796     * @param component a widget
4797     * @return the parent container of this component or item
4798     */

4799    public Object JavaDoc getParent(Object JavaDoc component) {
4800        return get(component, ":parent");
4801    }
4802
4803    /**
4804     * Gets the index of the first selected item in the given component
4805     *
4806     * @param component a widget (combobox, tabbedpane, list, table, or tree)
4807     * @return the first selected index or -1
4808     */

4809    public int getSelectedIndex(Object JavaDoc component) {
4810        String JavaDoc classname = getClass(component);
4811        if ((classname == "combobox") || (classname == "tabbedpane")) {
4812            return getInteger(component, "selected", (classname == "combobox") ? -1 : 0);
4813        }
4814        if ((classname == "list") || (classname == "table") || (classname == "tree")) {
4815            Object JavaDoc item = get(component, ":comp");
4816            for (int i = 0; item != null; i++) {
4817                if (getBoolean(item, "selected", false)) { return i; }
4818                item = get(item, ":next");
4819            }
4820            return -1;
4821        }
4822        throw new IllegalArgumentException JavaDoc(classname);
4823    }
4824    
4825    /**
4826     * Gets the first selected item of the given component
4827     *
4828     * @param component a widget (combobox, tabbedpane, list, table, or tree)
4829     * @return the first selected item or null
4830     */

4831    public Object JavaDoc getSelectedItem(Object JavaDoc component) {
4832        String JavaDoc classname = getClass(component);
4833        if ((classname == "combobox") || (classname == "tabbedpane")) {
4834            int index = getInteger(component, "selected",
4835                (classname == "combobox") ? -1 : 0);
4836            return (index != -1) ? getItemImpl(component, ":comp", index) : null;
4837        }
4838        if ((classname == "list") || (classname == "table") || (classname == "tree")) {
4839            for (Object JavaDoc item = findNextItem(component, classname, null); item != null;
4840                    item = findNextItem(component, classname, item)) {
4841                if (getBoolean(item, "selected", false)) { return item; }
4842            }
4843            return null;
4844        }
4845        throw new IllegalArgumentException JavaDoc(classname);
4846    }
4847
4848    /**
4849     * Gets the selected item of the given component (list, table, or tree)
4850     * when multiple selection is allowed
4851     *
4852     * @param component a widget
4853     * @return the array of selected items, or a 0 length array
4854     */

4855    public Object JavaDoc[] getSelectedItems(Object JavaDoc component) {
4856        String JavaDoc classname = getClass(component);
4857        Object JavaDoc[] selecteds = new Object JavaDoc[0];
4858        for (Object JavaDoc item = findNextItem(component, classname, null); item != null;
4859                item = findNextItem(component, classname, item)) {
4860            if (getBoolean(item, "selected", false)) {
4861                Object JavaDoc[] temp = new Object JavaDoc[selecteds.length + 1];
4862                System.arraycopy(selecteds, 0, temp, 0, selecteds.length);
4863                temp[selecteds.length] = item;
4864                selecteds = temp;
4865            }
4866        }
4867        return selecteds;
4868    }
4869
4870    /**
4871     * @return the first or the next item of the (list, table, or tree) component
4872     */

4873    private Object JavaDoc findNextItem(Object JavaDoc component, String JavaDoc classname, Object JavaDoc item) {
4874        if (item == null) { // first item
4875
return get(component, ":comp");
4876        }
4877        else if ("tree" == classname) { // next tree node
4878
Object JavaDoc next = get(item, ":comp");
4879            if ((next == null) || !getBoolean(item, "expanded", true)) { // no subnode or collapsed
4880
while ((item != component) && ((next = get(item, ":next")) == null)) {
4881                    item = getParent(item); //next node of in backward path
4882
}
4883            }
4884            return next;
4885        }
4886        else { //next list or tree item
4887
return get(item, ":next");
4888        }
4889    }
4890
4891    /**
4892     * Removes all the components from this container's specified list
4893     *
4894     * @param component the specified container
4895     */

4896    public void removeAll(Object JavaDoc component) {
4897        if (get(component, ":comp") != null) {
4898            set(component, ":comp", null);
4899            update(component, "validate");
4900        }
4901    }
4902
4903    /**
4904     *
4905     */

4906    private static int getItemCountImpl(Object JavaDoc component, String JavaDoc key) {
4907        int i = 0;
4908        for (Object JavaDoc comp = get(component, key); comp != null; comp = get(comp, ":next")) {
4909            i++;
4910        }
4911        return i;
4912    }
4913
4914    /**
4915     * Returns the subcomponent of the given component's specified list at the given index
4916     *
4917     * @param component a specified container
4918     * @param index the index of the component to get
4919     * @return the index<sup>th</sup> component in this container
4920     */

4921    public Object JavaDoc getItem(Object JavaDoc component, int index) {
4922        return getItemImpl(component, ":comp", index);
4923    }
4924    
4925    /**
4926     * Gets all the components in this container
4927     *
4928     * @param component a specified container
4929     * @return an array of all the components in this container
4930     */

4931    public Object JavaDoc[] getItems(Object JavaDoc component) {
4932        Object JavaDoc[] items = new Object JavaDoc[getItemCountImpl(component, ":comp")];
4933        Object JavaDoc comp = get(component, ":comp");
4934        for (int i = 0; i < items.length; i++) {
4935            items[i] = comp;
4936            comp = get(comp, ":next");
4937        }
4938        return items;
4939    }
4940
4941    /**
4942     * Referenced by DOM, replace by getItem for others
4943     */

4944    private static Object JavaDoc getItemImpl(Object JavaDoc component, Object JavaDoc key, int index) {
4945        int i = 0;
4946        for (Object JavaDoc item = get(component, key); item != null; item = get(item, ":next")) {
4947            if (i == index) { return item; }
4948            i++;
4949        }
4950        return null;
4951    }
4952
4953    /**
4954     *
4955     */

4956    private int getIndex(Object JavaDoc component, Object JavaDoc value) {
4957        int index = 0;
4958        for (Object JavaDoc item = get(component, ":comp"); item != null; item = get(item, ":next")) {
4959            if (value == item) { return index; }
4960            index++;
4961        }
4962        return -1;
4963    }
4964
4965    /**
4966     * Adds the specified component to the root desktop
4967     *
4968     * @param component a widget to be added
4969     */

4970    public void add(Object JavaDoc component) {
4971        add(content, component, 0);
4972    }
4973
4974    /**
4975     * Adds the specified component to the end of the specified container
4976     *
4977     * @param parent a container
4978     * @param component a component to be added
4979     */

4980    public void add(Object JavaDoc parent, Object JavaDoc component) {
4981        add(parent, component, -1);
4982    }
4983
4984    /**
4985     * Adds the specified component to the container at the given position
4986     *
4987     * @param parent a container
4988     * @param component a component to be inserted
4989     * @param index the position at which to insert the component,
4990     * or -1 to insert the component at the end
4991     */

4992    public void add(Object JavaDoc parent, Object JavaDoc component, int index) {
4993        addImpl(parent, component, index);
4994        update(component, "validate");
4995        if (parent == content) {
4996            setNextFocusable(component, false);
4997        }
4998    }
4999
5000    /**
5001     * Referenced by DOM
5002     */

5003    private void insertItem(Object JavaDoc parent,
5004            Object JavaDoc key, Object JavaDoc component, int index) {
5005        Object JavaDoc item = parent, next = get(parent, key);
5006        for (int i = 0;; i++) {
5007            if ((i == index) || (next == null)) {
5008                set(item, key, component);
5009                set(component, ":next", next);
5010                break;
5011            }
5012            next = get(item = next, key = ":next");
5013        }
5014    }
5015
5016    /**
5017     * Remove the specified component from its parent list, or
5018     * delete component's popupmenu or table's header
5019     *
5020     * @param component the component to be removed
5021     */

5022    public void remove(Object JavaDoc component) {
5023        update(component, "validate");
5024        Object JavaDoc parent = getParent(component);
5025        Object JavaDoc classname = getClass(component);
5026        if (("popupmenu" == classname) || ("header" == classname)) {
5027            set(parent, classname, null);
5028        }
5029        else {
5030            removeItemImpl(parent, component);
5031            // reuest focus for its parent if the component (or subcomponent) is currently focused
5032
for (Object JavaDoc comp = focusowner; comp != null; comp = getParent(comp)) {
5033                if (comp == component) {
5034                    setNextFocusable(parent, false); break;
5035                }
5036            }
5037        }
5038    }
5039
5040    /**
5041     * Delete the give component from its parent list
5042     * @param parent
5043     * @param component
5044     */

5045    private void removeItemImpl(Object JavaDoc parent, Object JavaDoc component) {
5046        Object JavaDoc previous = null; // the widget before the given component
5047
for (Object JavaDoc comp = get(parent, ":comp"); comp != null;) {
5048            Object JavaDoc next = get(comp, ":next");
5049            if (next == component) { previous = comp; break; }
5050            comp = next;
5051        }
5052        set((previous != null) ? previous : parent,
5053            (previous != null) ? ":next" : ":comp", get(component, ":next"));
5054        set(component, ":next", null); set(component, ":parent", null); // not required
5055
}
5056
5057    /**
5058     * Finds the first component from the root desktop by a specified name value
5059     *
5060     * @param name parameter value identifies the widget
5061     * @return the first suitable component, or null
5062     */

5063    public Object JavaDoc find(String JavaDoc name) {
5064        return find(content, name);
5065    }
5066
5067    /**
5068     * Finds the first component from the specified component by a name
5069     *
5070     * @param component the widget is searched inside this component
5071     * @param name parameter value identifies the widget
5072     * @return the first suitable component, or null
5073     */

5074    public Object JavaDoc find(Object JavaDoc component, String JavaDoc name) {
5075        if (name.equals(get(component, "name"))) {
5076            return component;
5077        }
5078        // otherwise search in its subcomponents
5079
Object JavaDoc found = null;
5080        for (Object JavaDoc comp = get(component, ":comp"); comp != null; comp = get(comp, ":next")) {
5081            if ((found = find(comp, name)) != null) { return found; }
5082        }
5083        // search in table header
5084
Object JavaDoc header = get(component, "header"); // if ("table" == classname)
5085
if ((header != null) && ((found = find(header, name)) != null)) { return found; }
5086        // search in component's popupmenu
5087
Object JavaDoc popupmenu = get(component, "popupmenu"); // if instance(classname, "component")
5088
if ((popupmenu != null) && ((found = find(popupmenu, name)) != null)) { return found; }
5089        return null;
5090    }
5091    
5092    /**
5093     * mnemonic (e.g. Alt-X):
5094     * - check: label, button, checkbox, togglebutton, menubar menus, tabbedpane tabs
5095     * - path: panel, desktop, dialog, splitpane components, tabbedpane selected component
5096     * accelerator (e.g. Ctrl-Shift-X, F4):
5097     * - check: menuitem, checkboxmenuitem
5098     * - path: see above, and menubar, and menu items
5099     * menubar F10: check menubar only
5100     * button enter, escape: check button only
5101     * @param component
5102     * @param parent check upwards if true
5103     * @param checked this leaf is already checked
5104     * @param mnemonic
5105     * @return true if the char was consumed
5106     */

5107    private boolean checkMnemonic(Object JavaDoc component,
5108            boolean parent, Object JavaDoc checked, int keycode, int modifiers) {
5109        if ((component == null) || !getBoolean(component, "visible", true) ||
5110                !getBoolean(component, "enabled", true)) { //+ enabled comp in disabled parent
5111
return false;
5112        }
5113        String JavaDoc classname = getClass(component);
5114        if ("label" == classname) {
5115            if (hasMnemonic(component, keycode, modifiers)) {
5116                Object JavaDoc labelfor = get(component, "for");
5117                if (labelfor != null) {
5118                    requestFocus(labelfor);
5119                    return true;
5120                }
5121            }
5122        }
5123        else if ("button" == classname) {
5124            if (((modifiers == 0) &&
5125                (((keycode == KeyEvent.VK_ENTER) && (get(component, "type") == "default")) ||
5126                ((keycode == KeyEvent.VK_ESCAPE) && (get(component, "type") == "cancel")))) ||
5127                    hasMnemonic(component, keycode, modifiers)) {
5128                invoke(component, null, "action");
5129                repaint(component);
5130                return true;
5131            }
5132        }
5133        else if (("checkbox" == classname) || ("togglebutton" == classname)) {
5134            if (hasMnemonic(component, keycode, modifiers)) {
5135                changeCheck(component, true);
5136                repaint(component);
5137                return true;
5138            }
5139        }
5140        else if ("menubar" == classname) {
5141            for (Object JavaDoc menu = get(component, ":comp"); menu != null; menu = get(menu, ":next")) {
5142                if (hasMnemonic(menu, keycode, modifiers) ||
5143                        ((modifiers == 0) && (keycode == KeyEvent.VK_F10))) {
5144                    closeup();
5145                    set(component, "selected", menu);
5146                    popupMenu(component);
5147                    repaint(component, "menubar", menu);
5148                    return true;
5149                }
5150            }
5151        }
5152        else if (("menuitem" == classname) || ("checkboxmenuitem" == classname)) {
5153            if (hasAccelerator(component, keycode, modifiers)) {
5154                invoke(component, null, "action");
5155            }
5156        }
5157        else if ("tabbedpane" == classname) {
5158            int selected = getInteger(component, "selected", 0); int i = 0;
5159            for (Object JavaDoc tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
5160                if (hasMnemonic(tab, keycode, modifiers)) {
5161                    if (selected != i) {
5162                        setInteger(component, "selected", i, 0);
5163                        repaint(component);
5164                        invoke(component, getItem(component, i), "action");
5165                    }
5166                    return true;
5167                }
5168                i++;
5169            }
5170            Object JavaDoc comp = get(getItem(component, selected), ":comp");
5171            if ((comp != null) && (comp != checked) &&
5172                    checkMnemonic(comp, false, null, keycode, modifiers)) {
5173                return true;
5174            }
5175        }
5176        // check subcomponents
5177
if (("panel" == classname) || ("desktop" == classname) ||
5178                ("dialog" == classname) || ("splitpane" == classname) ||
5179                ("menubar" == classname) || ("menu" == classname)) {
5180            for (Object JavaDoc comp = get(component, ":comp"); comp != null; comp = get(comp, ":next")) {
5181                if ((comp != checked) && checkMnemonic(comp, false, null, keycode, modifiers)) { return true; }
5182            }
5183        }
5184        // check parent
5185
if (parent && (("dialog" != classname) || !getBoolean(component, "modal", false))) {
5186            if (checkMnemonic(getParent(component), true,
5187                    ("tab" == classname) ? checked : component, keycode, modifiers)) { return true; }
5188        }
5189        return false;
5190    }
5191    
5192    /**
5193     * @param component
5194     * @param keycode
5195     * @param modifiers
5196     * @return true if the component has the given mnemonic
5197     */

5198    private boolean hasMnemonic(Object JavaDoc component, int keycode, int modifiers) {
5199        if (modifiers == InputEvent.ALT_MASK) {
5200            int index = getInteger(component, "mnemonic", -1);
5201            if (index != -1) {
5202                String JavaDoc text = getString(component, "text", null);
5203                return (text != null) && (text.length() > index) &&
5204                    (Character.toUpperCase(text.charAt(index)) == keycode);
5205            }
5206        }
5207        return false;
5208    }
5209    
5210    /**
5211     * @param component
5212     * @param keycode
5213     * @param modifiers
5214     * @return true if the component has the given accelerator
5215     */

5216    private boolean hasAccelerator(Object JavaDoc component, int keycode, int modifiers) {
5217        Object JavaDoc accelerator = get(component, "accelerator");
5218        if (accelerator != null) {
5219            long keystroke = ((Long JavaDoc) accelerator).longValue();
5220            return ((keystroke >> 32) == modifiers) && ((keystroke & 0xffff) == keycode);
5221        }
5222        return false;
5223    }
5224    
5225    /**
5226     * Binds the specified key to the specified value, and stores in this component.
5227     * <i>Null</i> value removes the property. Use the parameter tag in the xml
5228     * resource to bind a string value, the format is: <i>parameter='key=value'</i>
5229     *
5230     * @param component the hashtable is binded to this component
5231     * @param key the client property key
5232     * @param value the new client property value
5233     */

5234    public void putProperty(Object JavaDoc component, Object JavaDoc key, Object JavaDoc value) {
5235        Object JavaDoc table = get(component, ":bind");
5236        if (value != null) {
5237            if (table == null) {
5238                set(component, ":bind", table = new Hashtable());
5239            }
5240            ((Hashtable) table).put(key, value);
5241        }
5242        else if (table != null) {
5243            ((Hashtable) table).remove(key);
5244        }
5245    }
5246    
5247    /**
5248     * Returns the value of the property with the specified key.
5249     *
5250     * @param component searches the hashtable of this component
5251     * @param key the client property key
5252     * @return the value to which the key is mapped or null if the key is not mapped to any value
5253     */

5254    public Object JavaDoc getProperty(Object JavaDoc component, Object JavaDoc key) {
5255        Object JavaDoc table = get(component, ":bind");
5256        return (table != null) ? ((Hashtable) table).get(key) : null;
5257    }
5258
5259    // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
5260

5261    /**
5262     * Creates a component (and its subcomponents, and properties)
5263     * from the given xml resource
5264     *
5265     * @param path is relative to your thinlet instance or the classpath
5266     * (if the path starts with an <i>/</i> character), or a full URL
5267     * @return the root component of the parsed resource
5268     * @throws java.io.IOException
5269     */

5270    public Object JavaDoc parse(String JavaDoc path) throws IOException {
5271        return parse(path, this);
5272    }
5273
5274    /**
5275     * Creates a component from the given xml resource using the
5276     * specified event handler
5277     *
5278     * @param path is relative to your application package or the classpath, or an URL
5279     * @param handler bussiness methods are implemented in this object
5280     * @return the parsed components' root
5281     * @throws java.io.IOException
5282     */

5283    public Object JavaDoc parse(String JavaDoc path, Object JavaDoc handler) throws IOException {
5284        InputStream inputstream = null;
5285        try {
5286            inputstream = getClass().getResourceAsStream(path);
5287            if (inputstream == null) {
5288                try {
5289                    inputstream = new URL(path).openStream();
5290                } catch (MalformedURLException mfe) { /* thows nullpointerexception*/ }
5291            }
5292        } catch (Throwable JavaDoc e) {}
5293        return parse(inputstream, handler);
5294    }
5295
5296    /**
5297     * Creates a component from the given stream
5298     *
5299     * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5300     * @return the root component of the parsed stream
5301     * @throws java.io.IOException
5302     */

5303    public Object JavaDoc parse(InputStream inputstream) throws IOException {
5304        return parse(inputstream, this);
5305    }
5306
5307    /**
5308     * Creates a component from the given stream and event handler
5309     *
5310     * @param inputstream read xml from this stream
5311     * @param handler event handlers are implemented in this object
5312     * @return the parsed components' root
5313     * @throws java.io.IOException
5314     */

5315    public Object JavaDoc parse(InputStream inputstream, Object JavaDoc handler) throws IOException {
5316        return parse(inputstream, true, false, handler);
5317    }
5318
5319    /**
5320     * You can use the internal xml parser as a simple SAX-like parser,
5321     * during the process it calls the <i>startElement</i>, <i>characters</i>,
5322     * and <i>endElement</i> methods
5323     *
5324     * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5325     * @throws java.io.IOException
5326     */

5327    protected void parseXML(InputStream inputstream) throws IOException {
5328        parse(inputstream, false, false, null);
5329    }
5330
5331    /**
5332     * The SAX-like parser calls this method, you have to overwrite it
5333     *
5334     * @param name of the tag
5335     * @param attributelist a list of attributes including keys and value pairs
5336     */

5337    protected void startElement(String JavaDoc name, Hashtable attributelist) {}
5338
5339    /**
5340     * The SAX-like parser calls this method, you have to overwrite it
5341     *
5342     * @param text the content of a tag
5343     */

5344    protected void characters(String JavaDoc text) {}
5345
5346    /**
5347     * The SAX-like parser calls this method, you have to overwrite it
5348     */

5349    protected void endElement() {}
5350    
5351    /**
5352     * You can use the internal xml parser as a simple DOM-like parser,
5353     * use the <i>getDOMAttribute</i>, <i>getDOMText</i>,
5354     * <i>getDOMCount</i>, <i>getDOMNode</i>, <i>getClass</i>,
5355     * and <i>getParent</i> methods to analise the document
5356     *
5357     * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5358     * @return the root tag
5359     * @throws java.io.IOException
5360     */

5361    protected Object JavaDoc parseDOM(InputStream inputstream) throws IOException {
5362        return parse(inputstream, false, true, null);
5363    }
5364    
5365    /**
5366     * Gets the attribute value by the specified key for a DOM tag
5367     *
5368     * @param node a specified tag
5369     * @param key a string to identify the value pair
5370     * @return the value, or null
5371     */

5372    protected static String JavaDoc getDOMAttribute(Object JavaDoc node, String JavaDoc key) {
5373        return (String JavaDoc) get(node, key.intern());
5374    }
5375    
5376    /**
5377     * Gets the content string of a tag
5378     *
5379     * @param node a specified tag
5380     * @return the value, or null
5381     */

5382    protected static String JavaDoc getDOMText(Object JavaDoc node) {
5383        return (String JavaDoc) get(node, ":text");
5384    }
5385    
5386    /**
5387     * Gets the number of tags in a tag by a specified tagname
5388     *
5389     * @param node a specified tag
5390     * @param key the searched tagname
5391     * @return the number of tags
5392     */

5393    protected static int getDOMCount(Object JavaDoc node, String JavaDoc key) {
5394        return getItemCountImpl(node, key.intern());
5395    }
5396    
5397    /**
5398     * Gets the subtag of the specified tag by tagname and index
5399     *
5400     * @param node a specified tag
5401     * @param key the searched tagname
5402     * @param index the index of the requested subtag
5403     * @return the found tag, or null
5404     */

5405    protected static Object JavaDoc getDOMNode(Object JavaDoc node, String JavaDoc key, int index) {
5406        return getItemImpl(node, key.intern(), index);
5407    }
5408    
5409    /**
5410     * Set a bundle used in parse method, it replaces the parameter values starting
5411     * with the 'i18n.' string with a value found in the given bundle
5412     * @param resourcebundle a bundle for the next parsing or null to remove
5413     * the current one
5414     * @throws MissingResourceException if no object for the given key can be found
5415     */

5416    public void setResourceBundle(ResourceBundle resourcebundle) {
5417        this.resourcebundle = resourcebundle;
5418    }
5419
5420    /**
5421     *
5422     * @param inputstream
5423     * @param validate parse GUI from xml if true
5424     * @param dom parse an xml resoource
5425     * @param handler
5426     * @return
5427     * @throws java.io.IOException
5428     * @throws java.lang.IllegalArgumentException
5429     */

5430    private Object JavaDoc parse(InputStream inputstream,
5431            boolean validate, boolean dom, Object JavaDoc handler) throws IOException {
5432        Reader reader = new BufferedReader(new InputStreamReader(inputstream));
5433        try {
5434            Object JavaDoc[] parentlist = null;
5435            Object JavaDoc current = null;
5436            Hashtable attributelist = null;
5437            Vector methods = (validate && !dom) ? new Vector() : null;
5438            StringBuffer JavaDoc text = new StringBuffer JavaDoc();
5439            for (int c = reader.read(); c != -1;) {
5440                if (c == '<') {
5441                    if ((c = reader.read()) == '/') { //endtag
5442
if (text.length() > 0) {
5443                            if (text.charAt(text.length() - 1) == ' ') {
5444                                text.setLength(text.length() - 1);
5445                            }
5446                            if (!validate) {
5447                                if (dom) {
5448                                    set(current, ":text", text.toString());
5449                                } else {
5450                                    characters(text.toString());
5451                                }
5452                            }
5453                            // else {
5454
//addContent(current, text.toString());
5455
//}
5456
text.setLength(0);
5457                        }
5458                        String JavaDoc tagname = (String JavaDoc) parentlist[2]; //getClass(current);
5459
for (int i = 0; i < tagname.length(); i++) { // current-tag
5460
if ((c = reader.read()) != tagname.charAt(i)) {
5461                                throw new IllegalArgumentException JavaDoc(tagname);
5462                            }
5463                        }
5464                        while (" \t\n\r".indexOf(c = reader.read()) != -1); // whitespace
5465
if (c != '>') throw new IllegalArgumentException JavaDoc(); // '>'
5466
c = reader.read();
5467                        if (!validate && !dom) { endElement(); }
5468                        if (parentlist[0] == null) {
5469                            reader.close();
5470                            finishParse(methods, current, handler);
5471                            return current;
5472                        }
5473                        current = parentlist[0];
5474                        parentlist = (Object JavaDoc[]) parentlist[1];
5475                    }
5476                    else if (c == '!') { // DOCTYPE
5477
while ((c = reader.read()) != '>'); //+(-1)
5478
}
5479                    else if (c == '?') { // Processing Instructions
5480
boolean question = false; // read until '?>'
5481
while (((c = reader.read()) != '>') || !question) { question = (c == '?'); }
5482                    }
5483                    else { //start or standalone tag
5484
text.setLength(0);
5485                        boolean iscomment = false;
5486                        while (">/ \t\n\r".indexOf(c) == -1) {
5487                            text.append((char) c);
5488                            if ((text.length() == 3) && (text.charAt(0) == '!') &&
5489                                    (text.charAt(1) == '-') && (text.charAt(2) == '-')) {
5490                                int m = 0;
5491                                while (true) {
5492                                    c = reader.read();
5493                                    if (c == '-') { m++; }
5494                                    else if ((c == '>') && (m >= 2)) { break; }
5495                                    else { m = 0; }
5496                                }
5497                                iscomment = true;
5498                            }
5499                            c = reader.read();
5500                        }
5501                        if (iscomment) { continue; }
5502                        String JavaDoc tagname = text.toString();
5503                        parentlist = new Object JavaDoc[] { current, parentlist, tagname };
5504                        if (validate) {
5505                            current = (current != null) ?
5506                                addElement(current, tagname) : create(tagname);
5507                        } else {
5508                            if (dom) {
5509                                Object JavaDoc parent = current;
5510                                current = createImpl(tagname = tagname.intern());
5511                                if (parent != null) {
5512                                    insertItem(parent, tagname, current, -1);
5513                                    //set(current, ":parent", parent);
5514
}
5515                            } else {
5516                                current = tagname;
5517                            }
5518                        }
5519                        text.setLength(0);
5520                        while (true) {
5521                            boolean whitespace = false;
5522                            while (" \t\n\r".indexOf(c) != -1) {
5523                                c = reader.read();
5524                                whitespace = true;
5525                            }
5526                            if (c == '>') {
5527                                if (!validate && !dom) {
5528                                    startElement((String JavaDoc) current, attributelist); attributelist = null;
5529                                }
5530                                c = reader.read();
5531                                break;
5532                            }
5533                            else if (c == '/') {
5534                                if ((c = reader.read()) != '>') {
5535                                    throw new IllegalArgumentException JavaDoc(); // '>'
5536
}
5537                                if (!validate && !dom) {
5538                                    startElement((String JavaDoc) current, attributelist); attributelist = null;
5539                                    endElement();
5540                                }
5541                                if (parentlist[0] == null) {
5542                                    reader.close();
5543                                    finishParse(methods, current, handler);
5544                                    return current;
5545                                }
5546                                current = parentlist[0];
5547                                parentlist = (Object JavaDoc[]) parentlist[1];
5548                                c = reader.read();
5549                                break;
5550                            }
5551                            else if (whitespace) {
5552                                while ("= \t\n\r".indexOf(c) == -1) {
5553                                    text.append((char) c);
5554                                    c = reader.read();
5555                                }
5556                                String JavaDoc key = text.toString();
5557                                text.setLength(0);
5558                                while (" \t\n\r".indexOf(c) != -1) c = reader.read();
5559                                if (c != '=') throw new IllegalArgumentException JavaDoc();
5560                                while (" \t\n\r".indexOf(c = reader.read()) != -1);
5561                                char quote = (char) c;
5562                                if ((c != '\"') && (c != '\'')) throw new IllegalArgumentException JavaDoc();
5563                                while (quote != (c = reader.read())) {
5564                                    if (c == '&') {
5565                                        StringBuffer JavaDoc eb = new StringBuffer JavaDoc();
5566                                        while (';' != (c = reader.read())) { eb.append((char) c); }
5567                                        String JavaDoc entity = eb.toString();
5568                                        if ("lt".equals(entity)) { text.append('<'); }
5569                                        else if ("gt".equals(entity)) { text.append('>'); }
5570                                        else if ("amp".equals(entity)) { text.append('&'); }
5571                                        else if ("quot".equals(entity)) { text.append('"'); }
5572                                        else if ("apos".equals(entity)) { text.append('\''); }
5573                                        else if (entity.startsWith("#")) {
5574                                            boolean hexa = (entity.charAt(1) == 'x');
5575                                            text.append((char) Integer.parseInt(entity.substring(hexa ? 2 : 1), hexa ? 16 : 10));
5576                                        }
5577                                        else throw new IllegalArgumentException JavaDoc("unknown " + "entity " + entity);
5578                                    }
5579                                    else text.append((char) c);
5580                                }
5581                                if (validate) {
5582                                    addAttribute(current, key, text.toString(), methods);
5583                                } else {
5584                                    if (dom) {
5585                                        set(current, key.intern(), text.toString());
5586                                    } else {
5587                                        if (attributelist == null) { attributelist = new Hashtable(); }
5588                                        attributelist.put(key, text.toString());
5589                                    }
5590                                }
5591                                //'<![CDATA[' ']]>'
5592
text.setLength(0);
5593                                c = reader.read();
5594                            }
5595                            else throw new IllegalArgumentException JavaDoc();
5596                        }
5597                    }
5598                }
5599                else {
5600                    if (" \t\n\r".indexOf(c) != -1) {
5601                        if ((text.length() > 0) && (text.charAt(text.length() - 1) != ' ')) {
5602                            text.append(' ');
5603                        }
5604                    }
5605                    else {
5606                        text.append((char) c);
5607                    }
5608                    c = reader.read();
5609                }
5610            }
5611            throw new IllegalArgumentException JavaDoc();
5612        }
5613        finally {
5614            if (reader != null) { reader.close(); }
5615        }
5616    }
5617    
5618    /**
5619     *
5620     */

5621    private void finishParse(Vector methods, Object JavaDoc root, Object JavaDoc handler) {
5622        if (methods != null) {
5623            for (int i = 0; i < methods.size(); i += 3) {
5624                Object JavaDoc component = methods.elementAt(i);
5625                Object JavaDoc[] definition = (Object JavaDoc[]) methods.elementAt(i + 1);
5626                String JavaDoc value = (String JavaDoc) methods.elementAt(i + 2);
5627                
5628                if ("method" == definition[0]) {
5629                    Object JavaDoc[] method = getMethod(component, value, root, handler);
5630                    if ("init" == definition[1]) {
5631                        invokeImpl(method, null);
5632                    }
5633                    else {
5634                        set(component, definition[1], method);
5635                    }
5636                }
5637                else { // ("component" == definition[0])
5638
Object JavaDoc reference = find(root, value); //+start find from the component
5639
if (reference == null) throw new IllegalArgumentException JavaDoc(value + " not found");
5640                    set(component, definition[1], reference);
5641                }
5642            }
5643        }
5644    }
5645
5646    /**
5647     * Add the component to the parent's ':comp' list, and set its ':parent'
5648     * or set single components
5649     *
5650     * @param index add at the specified index
5651     * @throws java.lang.IllegalArgumentException
5652     */

5653    private void addImpl(Object JavaDoc parent, Object JavaDoc component, int index) {
5654        String JavaDoc parentclass = getClass(parent);
5655        String JavaDoc classname = getClass(component);
5656        if ((("combobox" == parentclass) && ("choice" == classname)) ||
5657                (("tabbedpane" == parentclass) && ("tab" == classname)) ||
5658                (("list" == parentclass) && ("item" == classname)) ||
5659                (("table" == parentclass) && ("row" == classname)) ||
5660                (("header" == parentclass) && ("column" == classname)) ||
5661                (("row" == parentclass) && ("cell" == classname)) ||
5662                ((("tree" == parentclass) || ("node" == parentclass)) && ("node" == classname)) ||
5663                (("menubar" == parentclass) && ("menu" == classname)) ||
5664                ((("menu" == parentclass) || ("popupmenu" == parentclass)) &&
5665                    (("menu" == classname) || ("menuitem" == classname) ||
5666                    ("checkboxmenuitem" == classname) || ("separator" == classname))) ||
5667                ((("panel" == parentclass) || ("desktop" == parentclass) ||
5668                    ("splitpane" == parentclass) || ("dialog" == parentclass) ||
5669                    ("tab" == parentclass)) && instance(classname, "component") &&
5670                        (classname != "popupmenu"))) {
5671            insertItem(parent, ":comp", component, index);
5672            set(component, ":parent", parent);
5673        }
5674        else if ((("table" == parentclass) && ("header" == classname)) ||
5675                (("popupmenu" == classname) && instance(parentclass, "component"))) {
5676            set(parent, classname, component);
5677            set(component, ":parent", parent);
5678        }
5679        else throw new IllegalArgumentException JavaDoc(classname + " add " + parentclass);
5680    }
5681    
5682    /**
5683     *
5684     */

5685    private boolean instance(Object JavaDoc classname, Object JavaDoc extendclass) {
5686        if (classname == extendclass) { return true; }
5687        for (int i = 0; i < dtd.length; i += 3) {
5688                if (classname == dtd[i]) {
5689                    return instance(dtd[i + 1], extendclass);
5690                }
5691        }
5692        return false;
5693    }
5694
5695    /**
5696     *
5697     */

5698    private Object JavaDoc addElement(Object JavaDoc parent, String JavaDoc name) {
5699        Object JavaDoc component = create(name);
5700        addImpl(parent, component, -1);
5701        return component;
5702    }
5703
5704    /**
5705     *
5706     * @throws java.lang.IllegalArgumentException
5707     */

5708    private void addAttribute(Object JavaDoc component, String JavaDoc key, String JavaDoc value, Vector lasts) {
5709        // replace value found in the bundle
5710
if ((resourcebundle != null) && value.startsWith("i18n.")) {
5711            value = resourcebundle.getString(value.substring(5));
5712        }
5713        
5714        Object JavaDoc[] definition = getDefinition(getClass(component), key, null);
5715        key = (String JavaDoc) definition[1];
5716        if ("string" == definition[0]) {
5717            setString(component, key, value, (String JavaDoc) definition[3]);
5718        }
5719        else if ("choice" == definition[0]) {
5720            String JavaDoc[] values = (String JavaDoc[]) definition[3];
5721            setChoice(component, key, value, values, values[0]);
5722        }
5723        else if ("boolean" == definition[0]) {
5724            if ("true".equals(value)) {
5725                if (definition[3] == Boolean.FALSE) {
5726                    set(component, key, Boolean.TRUE);
5727                }
5728            }
5729            else if ("false".equals(value)) {
5730                if (definition[3] == Boolean.TRUE) {
5731                    set(component, key, Boolean.FALSE);
5732                }
5733            }
5734            else throw new IllegalArgumentException JavaDoc(value);
5735        }
5736        else if ("integer" == definition[0]) {
5737            set(component, key, Integer.valueOf(value));
5738        }
5739        else if ("icon" == definition[0]) {
5740            set(component, key, getIcon(value));
5741        }
5742        else if (("method" == definition[0]) || ("component" == definition[0])) {
5743            lasts.addElement(component);
5744            lasts.addElement(definition);
5745            lasts.addElement(value);
5746        }
5747        else if ("property" == definition[0]) {
5748            StringTokenizer st = new StringTokenizer(value, ";");
5749            while (st.hasMoreTokens()) {
5750                String JavaDoc token = st.nextToken();
5751                int equals = token.indexOf('=');
5752                if (equals == -1) { throw new IllegalArgumentException JavaDoc(token); }
5753                putProperty(component, token.substring(0, equals), token.substring(equals + 1));
5754            }
5755        }
5756        else if ("font" == definition[0]) {
5757            String JavaDoc name = null;
5758            boolean bold = false; boolean italic = false;
5759            int size = 0;
5760            StringTokenizer st = new StringTokenizer(value);
5761            while (st.hasMoreTokens()) {
5762                String JavaDoc token = st.nextToken();
5763                if ("bold".equalsIgnoreCase(token)) { bold = true; }
5764                else if ("italic".equalsIgnoreCase(token)) { italic = true; }
5765                else {
5766                    try {
5767                        size = Integer.parseInt(token);
5768                    } catch (NumberFormatException JavaDoc nfe) {
5769                        name = (name == null) ? token : (name + " " + token);
5770                    }
5771                }
5772            }
5773            if (name == null) { name = font.getName(); }
5774            if (size == 0) { size = font.getSize(); }
5775            set(component, key, new Font(name,
5776                (bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0), size));
5777        }
5778        else if ("color" == definition[0]) {
5779            int color = 0;
5780            if (value.startsWith("#")) { color = Integer.parseInt(value.substring(1), 16); }
5781            else if (value.startsWith("0x")) { color = Integer.parseInt(value.substring(2), 16); }
5782            else { // three separated integer including red, green, and blue
5783
StringTokenizer st = new StringTokenizer(value, " \r\n\t,");
5784                color = 0xff000000 | ((Integer.parseInt(st.nextToken()) & 0xff) << 16) |
5785                    ((Integer.parseInt(st.nextToken()) & 0xff) << 8) |
5786                    (Integer.parseInt(st.nextToken()) & 0xff);
5787            }
5788            set(component, key, new Color(color));
5789        }
5790        else if ("keystroke" == definition[0]) {
5791            setKeystrokeImpl(component, key, value);
5792        }
5793        else if ("bean" == definition[0]) {
5794            try {
5795                Component bean = (Component) Class.forName(value).newInstance();
5796                set(component, key, bean);
5797            } catch (Exception JavaDoc exc) { throw new IllegalArgumentException JavaDoc(value); }
5798        }
5799        else throw new IllegalArgumentException JavaDoc((String JavaDoc) definition[0]);
5800    }
5801
5802    /**
5803     *
5804     * @throws java.lang.IllegalArgumentException
5805     */

5806    private static Object JavaDoc[] getDefinition(Object JavaDoc classname, String JavaDoc key, String JavaDoc type) {
5807        Object JavaDoc currentname = classname;
5808        while (classname != null) {
5809            for (int i = 0; i < dtd.length; i += 3) {
5810                if (dtd[i] == classname) {
5811                    Object JavaDoc[][] attributes = (Object JavaDoc[][]) dtd[i + 2];
5812                    if (attributes != null) {
5813                        for (int j = 0; j < attributes.length; j++) {
5814                            if (attributes[j][1].equals(key)) {
5815                                if ((type != null) && (type != attributes[j][0])) {
5816                                    throw new IllegalArgumentException JavaDoc(attributes[j][0].toString());
5817                                }
5818                                return attributes[j];
5819                            }
5820                        }
5821                    }
5822                    classname = dtd[i + 1];
5823                    break;
5824                }
5825            }
5826        }
5827        throw new IllegalArgumentException JavaDoc("unknown " + key + " " + type +
5828            " for " + currentname);
5829    }
5830
5831    // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
5832

5833    /**
5834     * Sets the given property pair (key and value) for the component
5835     */

5836    public void setString(Object JavaDoc component, String JavaDoc key, String JavaDoc value) {
5837        Object JavaDoc[] definition = getDefinition(getClass(component), key, "string");
5838        if (setString(component, (String JavaDoc) definition[1],
5839                value, (String JavaDoc) definition[3])) {
5840            update(component, definition[2]);
5841        }
5842    }
5843
5844    /**
5845     * Gets the property value of the given component by the property key
5846     */

5847    public String JavaDoc getString(Object JavaDoc component, String JavaDoc key) {
5848        return (String JavaDoc) get(component, key, "string");
5849        
5850    }
5851
5852    /**
5853     * Sets the given property pair (key and value) for the component
5854     */

5855    public void setChoice(Object JavaDoc component, String JavaDoc key, String JavaDoc value) {
5856        Object JavaDoc[] definition = getDefinition(getClass(component), key, "choice");
5857        String JavaDoc[] values = (String JavaDoc[]) definition[3];
5858        if (setChoice(component, (String JavaDoc) definition[1],
5859                value, values, values[0])) {
5860            update(component, definition[2]);
5861        }
5862    }
5863
5864    /**
5865     * Gets the property value of the given component by the property key
5866     */

5867    public String JavaDoc getChoice(Object JavaDoc component, String JavaDoc key) {
5868        Object JavaDoc[] definition = getDefinition(getClass(component), key, "choice");
5869        return getString(component, (String JavaDoc) definition[1],
5870            ((String JavaDoc[]) definition[3])[0]);
5871    }
5872
5873    /**
5874     * Sets the given property pair (key and value) for the component
5875     */

5876    public void setBoolean(Object JavaDoc component, String JavaDoc key, boolean value) {
5877        Object JavaDoc[] definition = getDefinition(getClass(component), key, "boolean");
5878        if (setBoolean(component, (String JavaDoc) definition[1],
5879                value, (definition[3] == Boolean.TRUE))) {
5880            update(component, definition[2]);
5881        }
5882    }
5883
5884    /**
5885     * Gets the property value of the given component by the property key
5886     */

5887    public boolean getBoolean(Object JavaDoc component, String JavaDoc key) {
5888        return get(component, key, "boolean") == Boolean.TRUE;
5889    }
5890
5891    /**
5892     * Sets the given property pair (key and value) for the component
5893     */

5894    public void setInteger(Object JavaDoc component, String JavaDoc key, int value) {
5895        Object JavaDoc[] definition = getDefinition(getClass(component), key, "integer");
5896        if (setInteger(component, (String JavaDoc) definition[1],
5897                value, ((Integer JavaDoc) definition[3]).intValue())) {
5898            update(component, definition[2]);
5899        }
5900    }
5901
5902    /**
5903     * Gets the property value of the given component by the property key
5904     */

5905    public int getInteger(Object JavaDoc component, String JavaDoc key) {
5906        return ((Integer JavaDoc) get(component, key, "integer")).intValue();
5907    }
5908
5909    /**
5910     * Sets the given property pair (key and value) for the component
5911     */

5912    public void setIcon(Object JavaDoc component, String JavaDoc key, Image icon) {
5913        Object JavaDoc[] definition = getDefinition(getClass(component), key, "icon");
5914        if (set(component, definition[1], icon)) {
5915            update(component, definition[2]);
5916        }
5917    }
5918
5919    /**
5920     * Gets the property value of the given component by the property key
5921     */

5922    public Image getIcon(Object JavaDoc component, String JavaDoc key) {
5923        return (Image) get(component, key, "icon");
5924    }
5925    
5926    /**
5927     *
5928     */

5929    public void setKeystroke(Object JavaDoc component, String JavaDoc key, String JavaDoc value) {
5930        Object JavaDoc[] definition = getDefinition(getClass(component), key, "keystroke");
5931        // TODO check if changed
5932
setKeystrokeImpl(component, (String JavaDoc) definition[1], value);
5933        update(component, definition[2]);
5934    }
5935    
5936    /**
5937     * Get the AWT component of the given (currently <i>bean</i>) widget
5938     *
5939     * @param component a <i>bean</i> widget
5940     * @param key the identifier of the parameter
5941     * @return an AWT component, or null
5942     */

5943    public Component getComponent(Object JavaDoc component, String JavaDoc key) {
5944        return (Component) get(component, key, "bean");
5945    }
5946
5947    /**
5948     * Set custom font on a component,
5949     * use the other <code>setFont</code> method instead
5950     */

5951    public void setFont(Object JavaDoc component, Font font) { // deprecated
5952
setFont(component, "font", font);
5953    }
5954    
5955    /**
5956     * Set custom font on a component
5957     *
5958     * @param component component to use the custom font
5959     * @param font custom font to use, or null to reset component to use default font
5960     */

5961    public void setFont(Object JavaDoc component, String JavaDoc key, Font font) {
5962        Object JavaDoc[] definition = getDefinition(getClass(component), key, "font");
5963        if (set(component, definition[1], font)) {
5964            update(component, definition[2]);
5965        }
5966    }
5967
5968    /**
5969     * Set custom color on a component.
5970     * Notes: For "foreground" key, this sets the text color.
5971     * For "background" key, on gradient-filled
5972     * components (such as tabs, buttons etc) this will result in a
5973     * component filled with solid background color, and not a new gradient.
5974     * Also, Color.brighter() will be used for highlight, and Color.darker()
5975     * will be used for pressed or not selected.
5976     *
5977     * @param component component to use for custom color
5978     * @param key currently "background" and "foreground" are supported
5979     * @param color custom color to use, or null to reset component to use default color
5980     */

5981    public void setColor(Object JavaDoc component, String JavaDoc key, Color color) {
5982        Object JavaDoc[] definition = getDefinition(getClass(component), key, "color");
5983        if (set(component, definition[1], color)) {
5984            update(component, definition[2]);
5985        }
5986    }
5987    
5988    /**
5989     * Set the AWT component for the given (currently <i>bean</i>) widget
5990     *
5991     * @param component a <i>bean</i> widget
5992     * @param key the identifier of the parameter
5993     * @param bean an AWT component, or null
5994     */

5995    public void setComponent(Object JavaDoc component, String JavaDoc key, Component bean) {
5996        Object JavaDoc[] definition = getDefinition(getClass(component), key, "bean");
5997        if (set(component, definition[1], font)) {
5998            update(component, definition[2]);
5999        }
6000    }
6001    
6002    /**
6003     *
6004     */

6005    private void setKeystrokeImpl(Object JavaDoc component, String JavaDoc key, String JavaDoc value) {
6006        Long JavaDoc keystroke = null;
6007        if (value != null) {
6008            String JavaDoc token = value;
6009            try {
6010                int keycode = 0, modifiers = 0;
6011                StringTokenizer st = new StringTokenizer(value, " \r\n\t+");
6012                while (st.hasMoreTokens()) {
6013                    token = st.nextToken().toUpperCase();
6014                    try {
6015                            modifiers = modifiers | InputEvent.class.getField(token + "_MASK").getInt(null);
6016                    } catch (Exception JavaDoc exc) { // not mask value
6017
keycode = KeyEvent.class.getField("VK_" + token).getInt(null);
6018                    }
6019                }
6020                keystroke = new Long JavaDoc(((long) modifiers) << 32 | keycode);
6021            } catch (Exception JavaDoc exc) { throw new IllegalArgumentException JavaDoc(token); }
6022        }
6023        set(component, key, keystroke);
6024    }
6025    
6026    //TODO add set/getComponent for popupmenu and header
6027

6028    /**
6029     *
6030     */

6031    public Object JavaDoc getWidget(Object JavaDoc component, String JavaDoc key) {
6032        if ("popupmenu".equals(key)) { return get(component, "popupmenu");}
6033        else if ("header".equals(key)) { return get(component, "header");}
6034        else throw new IllegalArgumentException JavaDoc(key);
6035    }
6036    
6037    /**
6038     *
6039     */

6040    private static Object JavaDoc get(Object JavaDoc component, String JavaDoc key, String JavaDoc type) {
6041        Object JavaDoc[] definition = getDefinition(getClass(component), key, type);
6042        Object JavaDoc value = get(component, definition[1]);
6043        return (value != null) ? value : definition[3];
6044    }
6045    
6046    /**
6047     * Sets a new event handler method for a component
6048     *
6049     * @param component the target component
6050     * @param key the key name of the parameter (e.g. <i>action</i>)
6051     * @param value the method name and parameters
6052     * (e.g. <i>foo(this, this.text, mybutton, mybutton.enabled)</i>
6053     * for <i>public void foo(Object component, String text, Object mybutton, boolean enabled)</i>)
6054     * @param root the search starting component for name components in the arguments
6055     * @param handler the target event handler object including the method
6056     * @throws java.lang.IllegalArgumentException
6057     */

6058    public void setMethod(Object JavaDoc component, String JavaDoc key, String JavaDoc value, Object JavaDoc root, Object JavaDoc handler) {
6059        key = (String JavaDoc) getDefinition(getClass(component), key, "method")[1];
6060        Object JavaDoc[] method = getMethod(component, value, root, handler);
6061        set(component, key, method);
6062    }
6063    
6064    /**
6065     * @return an object list including as follows:
6066     * - handler object,
6067     * - method,
6068     * - list of parameters including 3 values:
6069     * - ("thinlet", null, null) for the single thinlet component,
6070     * - (target component, null, null) for named widget as parameter, e.g. mybutton,
6071     * - (target, parameter name, default value) for a widget's given property, e.g. mylabel.enabled,
6072     * - ("item", null, null) for an item of the target component as parameter, e.g. tree node,
6073     * - ("item", parameter name, default value) for the item's given property e.g. list item's text.
6074     */

6075    private Object JavaDoc[] getMethod(Object JavaDoc component, String JavaDoc value, Object JavaDoc root, Object JavaDoc handler) {
6076        StringTokenizer st = new StringTokenizer(value, "(, \r\n\t)");
6077        String JavaDoc methodname = st.nextToken();
6078        int n = st.countTokens();
6079        Object JavaDoc[] data = new Object JavaDoc[2 + 3 * n];
6080        Class JavaDoc[] parametertypes = (n > 0) ? new Class JavaDoc[n] : null;
6081        for (int i = 0; i < n; i++) {
6082            String JavaDoc arg = st.nextToken();
6083            if ("thinlet".equals(arg)) {
6084                data[2 + 3 * i] = "thinlet"; // the target component
6085
parametertypes[i] = Thinlet.class;
6086            }
6087            else {
6088                int dot = arg.indexOf('.');
6089                String JavaDoc compname = (dot == -1) ? arg : arg.substring(0, dot);
6090                Object JavaDoc comp = null;
6091                String JavaDoc classname = null;
6092                if ("item".equals(compname)) {
6093                    comp = "item";
6094                    String JavaDoc parentclass = getClass(component);
6095                    if ("list" == parentclass) { classname = "item"; }
6096                    else if ("tree" == parentclass) { classname = "node"; }
6097                    else if ("table" == parentclass) { classname = "row"; }
6098                    else if ("combobox" == parentclass) { classname = "choice"; }
6099                    else if ("tabbedpane" == parentclass) { classname = "tab"; }
6100                    else throw new IllegalArgumentException JavaDoc(parentclass + " has no item");
6101                }
6102                else {
6103                    comp = ("this".equals(compname)) ? component : find(root, compname);
6104                    classname = getClass(comp);
6105                }
6106                data[2 + 3 * i] = comp; // the target component
6107
if (dot == -1) {
6108                    parametertypes[i] = Object JavaDoc.class; // Widget.class
6109
}
6110                else {
6111                    Object JavaDoc[] definition = getDefinition(classname, arg.substring(dot + 1), null);
6112                    data[2 + 3 * i + 1] = definition[1]; // parameter name, e.g. enabled
6113
data[2 + 3 * i + 2] = definition[3]; // default value, e.g. Boolean.TRUE
6114
Object JavaDoc fieldclass = definition[0];
6115                    if ((fieldclass == "string") || (fieldclass == "choice")) {
6116                        parametertypes[i] = String JavaDoc.class;
6117                    }
6118                    else if (fieldclass == "boolean") {
6119                        parametertypes[i] = Boolean.TYPE;
6120                    }
6121                    else if (fieldclass == "integer") {
6122                        parametertypes[i] = Integer.TYPE;
6123                    }
6124                    else if (fieldclass == "icon") {
6125                        parametertypes[i] = Image.class;
6126                    }
6127                    else throw new IllegalArgumentException JavaDoc((String JavaDoc) fieldclass);
6128                }
6129            }
6130        }
6131        data[0] = handler;
6132        try {
6133            data[1] = handler.getClass().getMethod(methodname, parametertypes);
6134            return data;
6135        } catch (Exception JavaDoc exc) {
6136            throw new IllegalArgumentException JavaDoc(value + " " + exc.getMessage());
6137        }
6138    }
6139
6140    /**
6141     *
6142     */

6143    private void update(Object JavaDoc component, Object JavaDoc mode) {
6144        if ("parent" == mode) {
6145            component = getParent(component);
6146            mode = "validate";
6147        }
6148        boolean firstpaint = true;
6149        int x = 0; int y = 0; int width = 0; int height = 0;
6150        while (component != null) {
6151            if (!getBoolean(component, "visible", true)) { break; }
6152            if ("paint" == mode) {//|| (firstpaint && (component == content))
6153
Rectangle bounds = getRectangle(component, "bounds");
6154                if (bounds == null) { return; }
6155                if (firstpaint) {
6156                    x = bounds.x; y = bounds.y;
6157                    width = Math.abs(bounds.width); height = bounds.height;
6158                    firstpaint = false;
6159                } else {
6160                    x += bounds.x; y += bounds.y;
6161                }
6162                if (component == content) {
6163                    repaint(x, y, width, height);
6164                }
6165            }
6166            Object JavaDoc parent = getParent(component);
6167            String JavaDoc classname = getClass(parent);
6168            if ("combobox" == classname) {
6169                parent = get(parent, ":combolist");
6170            }
6171            else if ("menu" == classname) {
6172                parent = get(parent, ":popup");
6173            }
6174            else if (("paint" == mode) && ("tabbedpane" == classname)) {
6175                if (getItem(parent, getInteger(parent, "selected", 0)) != component) { break; }
6176            }
6177            if (("layout" == mode) || (("validate" == mode) &&
6178                    (("list" == classname) || ("table" == classname) ||
6179                    ("tree" == classname) || ("dialog" == classname) || (parent == content)))) {
6180                Rectangle bounds = getRectangle(parent, "bounds");
6181                if (bounds == null) { return; }
6182                bounds.width = -1 * Math.abs(bounds.width);
6183                mode = "paint";
6184            }
6185            component = parent;
6186        }
6187    }
6188
6189    // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
6190

6191    /**
6192     *
6193     */

6194    private boolean setString(Object JavaDoc component,
6195            String JavaDoc key, String JavaDoc value, String JavaDoc defaultvalue) {
6196        if (allI18n && (langResource != null) &&
6197                ((key == "text") || (key == "tooltip"))) {
6198            putProperty(component, "i18n." + key, null); // for I18N
6199
}
6200        return set(component, key, value); // use defaultvalue
6201
}
6202
6203    /**
6204     *
6205     */

6206    private String JavaDoc getString(Object JavaDoc component,
6207            String JavaDoc key, String JavaDoc defaultvalue) {
6208        Object JavaDoc value = get(component, key);
6209        return (value == null) ? defaultvalue :
6210            getI18NString(component, key, (String JavaDoc) value);
6211    }
6212    
6213    /**
6214     * Set current language resource bundle. This flushes all cached translated values, performs
6215     * lazy loading of new values, and repaints the desktop. This implementation allows applications to switch
6216     * language resources on the fly, without rebuilding/reloading components that use them.
6217     * <br />The pseudo-code is as follows:
6218     *
6219     * <ul><li>if langResource and langResourceDefault are null, don't
6220     * translate anything, no matter what other settings are. This
6221     * behaviour provides compatibility with previous versions.</li>
6222     * <li>if only langResourceDefault is set, use this when translation is required</li>
6223     * <li>if allI18n is set to true:
6224     * <ul><li>if property "i18n" on a component is missing,
6225     * or set to "true", translate</li>
6226     * <li>if property "i18n" is present, and set to "false",
6227     * do not translate</li></ul></li>
6228     * <li>if allI18n is set to false:
6229     * <ul><li>if property "i18n" on a component is missing,
6230     * or set to "false", do not translate</li>
6231     * <li>if property "i18n" is present, and set to "true", translate</li>
6232     * </ul></li></ul>
6233     *
6234     * The "translate" step is applied only to values from "text"
6235     * and "tooltip" properties (for now), and is applied as follows:
6236     *
6237     * <ul><li>use the value of "text" or "tooltip" as a lookup key</li>
6238     * <li>use langResource to lookup the result value
6239     * <ul><li>if no value is found, use langResourceDefault for lookup
6240     * <ul><li>if no value is found, just return the original value of
6241     * the property. Set a flag on component that prevents
6242     * lookups in the future. This flag is cleared when langResource is changed.</li>
6243     * </ul></li></ul></li>
6244     * <li>cache the result value, if any</li></ul>
6245     *
6246     * If translated value is found successfully, it is cached in the
6247     * component. This cache is gradually flushed when setLangResource
6248     * is called. Cached value is also flushed when setString() is
6249     * called on a component.
6250     *
6251     * @param res resource bundle containing localized texts for "text" and "tooltip"
6252     */

6253    public void setLangResource(ResourceBundle res) { // for I18N
6254
langResource = res;
6255        doLayout(content);
6256        repaint(content);
6257    }
6258
6259    /**
6260     * Returns language resource bundle currently in use, or default bundle, or null.
6261     */

6262    public static ResourceBundle getLangResource() { // for I18N
6263
return langResource;
6264    }
6265
6266    /**
6267     * Set default language resource bundle. Resources from this bundle will be used if
6268     * they are missing in the current bundle.
6269     *
6270     * @param res resource bundle containing default localized texts for "text" and "tooltip"
6271     */

6272    public void setLangResourceDefault(ResourceBundle res) { // for I18N
6273
langResourceDefault = res;
6274        if (langResource == null) setLangResource(res);
6275    }
6276
6277    /**
6278     * Returns default language resource bundle, or null.
6279     */

6280    public static ResourceBundle getLangResourceDefault() { // for I18N
6281
return langResourceDefault;
6282    }
6283
6284    /**
6285     * Sets the default behaviour of internationalization code. If set to "true", try to translate
6286     * all components' "text" and "tooltip" values, unless explicitly prohibited by setting
6287     * <code>i18n="false"</code> on a specific component. If set to "false", do not translate
6288     * unless explicitly requested by setting <code>i18n="true"</code> on a specific component.
6289     * <br />Default value is "false", to provide backwards compatibility.
6290     *
6291     *@param val if "true", translate by default; if "false", do not translate by default.
6292     */

6293    public void setAllI18n(boolean val) { // for I18N
6294
allI18n = val;
6295    }
6296    
6297    /**
6298     *
6299     */

6300    private String JavaDoc getI18NString(Object JavaDoc component, String JavaDoc key, String JavaDoc text) { // for I18N
6301
if (allI18n && (langResource != null) &&
6302                ((key == "text") || (key == "tooltip")) &&
6303                getBoolean(component, "i18n", true)) {
6304            String JavaDoc ikey = (String JavaDoc) getProperty(component, "i18n." + key);
6305            if (!"__NONE__".equals(ikey)) {
6306                if (ikey == null) { // initialize
6307
putProperty(component, "i18n." + key, ikey = text);
6308                }
6309                try {
6310                    return langResource.getString(ikey);
6311                } catch (Exception JavaDoc exc) { // not found. Try default
6312
if (langResourceDefault != null) {
6313                        try {
6314                            return langResourceDefault.getString(ikey);
6315                        } catch (Exception JavaDoc dexc) {
6316                            putProperty(component, "i18n." + key, "__NONE__");
6317                        }
6318                    }
6319                }
6320            }
6321        }
6322        return text;
6323    }
6324    
6325    /**
6326     *
6327     * @throws java.lang.IllegalArgumentException
6328     */

6329    private boolean setChoice(Object JavaDoc component,
6330            String JavaDoc key, String JavaDoc value, String JavaDoc[] values, String JavaDoc defaultvalue) {
6331        if (value == null) {
6332            return set(component, key, defaultvalue);
6333        }
6334        for (int i = 0; i < values.length; i++) {
6335            if (value.equals(values[i])) {
6336                return set(component, key, values[i]);
6337            }
6338        }
6339        throw new IllegalArgumentException JavaDoc("unknown " + value + " for " + key);
6340    }
6341
6342    /**
6343     *
6344     */

6345    private Image getIcon(Object JavaDoc component, String JavaDoc key, Image defaultvalue) {
6346        Object JavaDoc value = get(component, key);
6347        return (value == null) ? defaultvalue : (Image) value;
6348    }
6349
6350    /**
6351     *
6352     */

6353    private boolean setBoolean(Object JavaDoc component,
6354            String JavaDoc key, boolean value, boolean defaultvalue) {
6355        return set(component, key, (value == defaultvalue) ? null :
6356            (value ? Boolean.TRUE : Boolean.FALSE));
6357    }
6358
6359    /**
6360     *
6361     */

6362    private boolean getBoolean(Object JavaDoc component,
6363            String JavaDoc key, boolean defaultvalue) {
6364        Object JavaDoc value = get(component, key);
6365        return (value == null) ? defaultvalue : ((Boolean JavaDoc) value).booleanValue();
6366    }
6367
6368    /**
6369     *
6370     */

6371    private boolean setInteger(Object JavaDoc component,
6372            String JavaDoc key, int value, int defaultvalue) {
6373        return set(component, key, (value == defaultvalue) ? null : new Integer JavaDoc(value));
6374    }
6375
6376    /**
6377     *
6378     */

6379    private int getInteger(Object JavaDoc component, String JavaDoc key, int defaultvalue) {
6380        Object JavaDoc value = get(component, key);
6381        return (value == null) ? defaultvalue : ((Integer JavaDoc) value).intValue();
6382    }
6383
6384    /**
6385     *
6386     */

6387    private void setRectangle(Object JavaDoc component,
6388            String JavaDoc key, int x, int y, int width, int height) {
6389        Rectangle rectangle = getRectangle(component, key);
6390        if (rectangle != null) {
6391            rectangle.x = x; rectangle.y = y;
6392            rectangle.width = width; rectangle.height = height;
6393        }
6394        else {
6395            set(component, key, new Rectangle(x, y, width, height));
6396        }
6397    }
6398
6399    /**
6400     *
6401     */

6402    private Rectangle getRectangle(Object JavaDoc component, String JavaDoc key) {
6403        return (Rectangle) get(component, key);
6404    }
6405
6406    // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
6407

6408    /**
6409     * Creates an image, and loads it immediately by default
6410     *
6411     * @param path is relative to your thinlet instance or the classpath
6412     * (if the path starts with <i>'/'</i> character), or a full URL
6413     * @return the loaded image or null
6414     */

6415    public Image getIcon(String JavaDoc path) {
6416        return getIcon(path, true);
6417    }
6418
6419    /**
6420     * Creates an image from the specified resource.
6421     * To speed up loading the same images use a cache (a simple hashtable).
6422     * And flush the resources being used by an image when you won't use it henceforward
6423     *
6424     * @param path is relative to your thinlet instance or the classpath, or an URL
6425     * @param preload waits for the whole image if true, starts loading
6426     * (and repaints, and updates the layout) only when required (painted, or size requested) if false
6427     * @return the loaded image or null
6428     */

6429    public Image getIcon(String JavaDoc path, boolean preload) {
6430        if ((path == null) || (path.length() == 0)) {
6431            return null;
6432        }
6433        Image image = null; //(Image) imagepool.get(path);
6434
try {
6435            URL url = getClass().getResource(path); //ClassLoader.getSystemResource(path)
6436
if (url != null) { // contributed by Stefan Matthias Aust
6437
image = Toolkit.getDefaultToolkit().getImage(url);
6438            }
6439        } catch (Throwable JavaDoc e) {}
6440        if (image == null) {
6441            try {
6442                InputStream is = getClass().getResourceAsStream(path);
6443                //InputStream is = ClassLoader.getSystemResourceAsStream(path);
6444
if (is != null) {
6445                    byte[] data = new byte[is.available()];
6446                    is.read(data, 0, data.length);
6447                    image = getToolkit().createImage(data);
6448                    is.close();
6449                }
6450                else { // contributed by Wolf Paulus
6451
image = Toolkit.getDefaultToolkit().getImage(new URL(path));
6452                }
6453            } catch (Throwable JavaDoc e) {}
6454        }
6455        if (preload && (image != null)) {
6456            MediaTracker mediatracker = new MediaTracker(this);
6457            mediatracker.addImage(image, 1);
6458            try {
6459                mediatracker.waitForID(1, 5000);
6460            } catch (InterruptedException JavaDoc ie) { }
6461            //imagepool.put(path, image);
6462
}
6463        return image;
6464    }
6465
6466    /**
6467     * This method is called by the FrameLauncher if the window was closing,
6468     * or AppletLauncher's destroy method. Overwrite it to e.g. save the application changes.
6469     *
6470     * @return true to exit, and false to keep the frame and continue the application
6471     */

6472    public boolean destroy() {
6473        return true;
6474    }
6475
6476    private static Object JavaDoc[] dtd;
6477    static {
6478        Integer JavaDoc integer_1 = new Integer JavaDoc(-1);
6479        Integer JavaDoc integer0 = new Integer JavaDoc(0);
6480        Integer JavaDoc integer1 = new Integer JavaDoc(1);
6481        String JavaDoc[] orientation = { "horizontal", "vertical" };
6482        String JavaDoc[] leftcenterright = { "left", "center", "right" };
6483        String JavaDoc[] selections = { "single", "interval", "multiple" }; //+none
6484
dtd = new Object JavaDoc[] {
6485            "component", null, new Object JavaDoc[][] {
6486                { "string", "name", null, null },
6487                { "boolean", "enabled", "paint", Boolean.TRUE },
6488                { "boolean", "visible", "parent", Boolean.TRUE },
6489                { "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
6490
{ "string", "tooltip", null, null },
6491                { "font", "font", "validate", null },
6492                { "color", "foreground", "paint", null },
6493                { "color", "background", "paint", null },
6494                { "integer", "width", "validate", integer0 },
6495                { "integer", "height", "validate", integer0 },
6496                { "integer", "colspan", "validate", integer1 },
6497                { "integer", "rowspan", "validate", integer1 },
6498                { "integer", "weightx", "validate", integer0 },
6499                { "integer", "weighty", "validate", integer0 },
6500                { "choice", "halign", "validate",
6501                    new String JavaDoc[] { "fill", "center", "left", "right" } },
6502                { "choice", "valign", "validate",
6503                new String JavaDoc[] { "fill", "center", "top", "bottom" } },
6504                // component class String null*
6505
// parent Object null
6506
// (bounds) Rectangle 0 0 0 0
6507
{ "property", "property", null, null },
6508                { "method", "init" },
6509                { "method", "focuslost" },
6510                { "method", "focusgained" } },
6511            "label", "component", new Object JavaDoc[][] {
6512                { "string", "text", "validate", null },
6513                { "icon", "icon", "validate", null },
6514                { "choice", "alignment", "validate", leftcenterright },
6515                { "integer", "mnemonic", "paint", integer_1 },
6516                { "component", "for", null, null } },
6517            "button", "label", new Object JavaDoc[][] {
6518                { "choice", "alignment", "validate", new String JavaDoc[] { "center", "left", "right" } },
6519                { "method", "action" },
6520                { "choice", "type", "paint", new String JavaDoc[] { "normal", "default", "cancel", "link" } } },
6521            "checkbox", "label", new Object JavaDoc[][] {
6522                { "boolean", "selected", "paint", Boolean.FALSE }, //...group
6523
{ "string", "group", "paint", null }, //...group
6524
{ "method", "action" } },
6525            "togglebutton", "checkbox", null,
6526            "combobox", "textfield", new Object JavaDoc[][] {
6527                { "icon", "icon", "validate", null },
6528                { "integer", "selected", "layout", integer_1 } },
6529            "choice", null, new Object JavaDoc[][] {
6530                { "string", "name", null, null },
6531                { "boolean", "enabled", "paint", Boolean.TRUE },
6532                { "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
6533
{ "string", "text", "parent", null },
6534                { "icon", "icon", "parent", null },
6535                { "choice", "alignment", "parent", leftcenterright },
6536                { "string", "tooltip", null, null },
6537                { "font", "font", "validate", null },
6538                { "color", "foreground", "paint", null },
6539                { "color", "background", "paint", null },
6540                { "property", "property", null, null } },
6541            "textfield", "component", new Object JavaDoc[][] {
6542                { "string", "text", "layout", "" },
6543                { "integer", "columns", "validate", integer0 },
6544                { "boolean", "editable", "paint", Boolean.TRUE },
6545                { "integer", "start", "layout", integer0 },
6546                { "integer", "end", "layout", integer0 },
6547                { "method", "action" },
6548                { "method", "insert" },
6549                { "method", "remove" },
6550                { "method", "caret" },
6551                { "method", "perform" } },
6552            "passwordfield", "textfield", null,
6553            "textarea", "textfield", new Object JavaDoc[][] {
6554                { "integer", "rows", "validate", integer0 },
6555                { "boolean", "border", "validate", Boolean.TRUE },
6556                { "boolean", "wrap", "layout", Boolean.FALSE } },
6557            "tabbedpane", "component", new Object JavaDoc[][] {
6558                { "choice", "placement", "validate",
6559                    new String JavaDoc[] { "top", "left", "bottom", "right", "stacked" } },
6560                { "integer", "selected", "paint", integer0 },
6561                { "method", "action" } }, //...focus
6562
"tab", "choice", new Object JavaDoc[][] {
6563                { "integer", "mnemonic", "paint", integer_1 } },
6564            "panel", "component", new Object JavaDoc[][] {
6565                { "integer", "columns", "validate", integer0 },
6566                { "integer", "top", "validate", integer0 },
6567                { "integer", "left", "validate", integer0 },
6568                { "integer", "bottom", "validate", integer0 },
6569                { "integer", "right", "validate", integer0 },
6570                { "integer", "gap", "validate", integer0 },
6571                { "string", "text", "validate", null },
6572                { "icon", "icon", "validate", null },
6573                { "boolean", "border", "validate", Boolean.FALSE },
6574                { "boolean", "scrollable", "validate", Boolean.FALSE } },
6575            "desktop", "component", null,
6576            "dialog", "panel", new Object JavaDoc[][] {
6577                { "boolean", "modal", null, Boolean.FALSE },
6578                { "boolean", "resizable", null, Boolean.FALSE },
6579                { "boolean", "closable", "paint", Boolean.FALSE },
6580                { "boolean", "maximizable", "paint", Boolean.FALSE },
6581                { "boolean", "iconifiable", "paint", Boolean.FALSE } },
6582            "spinbox", "textfield", new Object JavaDoc[][] {
6583                { "integer", "minimum", null, new Integer JavaDoc(Integer.MIN_VALUE) },
6584                { "integer", "maximum", null, new Integer JavaDoc(Integer.MAX_VALUE) },
6585                { "integer", "step", null, integer1 },
6586                { "integer", "value", null, integer0 } }, // == text? deprecated
6587
"progressbar", "component", new Object JavaDoc[][] {
6588                { "choice", "orientation", "validate", orientation },
6589                { "integer", "minimum", "paint", integer0 }, //...checkvalue
6590
{ "integer", "maximum", "paint", new Integer JavaDoc(100) },
6591                { "integer", "value", "paint", integer0 } },
6592                // change stringpainted
6593
"slider", "progressbar", new Object JavaDoc[][] {
6594                { "integer", "unit", null, new Integer JavaDoc(5) },
6595                { "integer", "block", null, new Integer JavaDoc(25) },
6596                { "method", "action" } },
6597                // minor/majortickspacing
6598
// inverted
6599
// labelincrement labelstart
6600
"splitpane", "component", new Object JavaDoc[][] {
6601                { "choice", "orientation", "validate", orientation },
6602                { "integer", "divider", "layout", integer_1 } },
6603            "list", "component", new Object JavaDoc[][] {
6604                { "choice", "selection", "paint", selections },
6605                { "method", "action" },
6606                { "method", "perform" },
6607                { "boolean", "line", "validate", Boolean.TRUE } },
6608            "item", "choice", new Object JavaDoc[][] {
6609                { "boolean", "selected", null, Boolean.FALSE } },
6610            "table", "list", new Object JavaDoc[][] {
6611                /*{ "choice", "selection",
6612                    new String[] { "singlerow", "rowinterval", "multiplerow",
6613                        "cell", "cellinterval",
6614                        "singlecolumn", "columninterval", "multiplecolumn" } }*/
},
6615            "header", null, null,
6616                // reordering allowed
6617
// autoresize mode: off next (column boundries) subsequents last all columns
6618
// column row selection
6619
// selection row column cell
6620
// editing row/column
6621
"column", "choice", new Object JavaDoc[][] {
6622                { "integer", "width", null, new Integer JavaDoc(80) },
6623                { "choice", "sort", null, new String JavaDoc[] { "none", "ascent", "descent" } } },
6624            "row", null, new Object JavaDoc[][] {
6625                { "boolean", "selected", null, Boolean.FALSE } },
6626            "cell", "choice", null,
6627            "tree", "list", new Object JavaDoc[][] {
6628                { "boolean", "angle", null, Boolean.FALSE },
6629                { "method", "expand" },
6630                { "method", "collapse" } },
6631            "node", "choice", new Object JavaDoc[][] {
6632                { "boolean", "selected", null, Boolean.FALSE },
6633                { "boolean", "expanded", null, Boolean.TRUE } },
6634            "separator", "component", null,
6635            "menubar", "component", new Object JavaDoc[][] {
6636                { "choice", "placement", "validate", new String JavaDoc[] { "top", "bottom" } } },
6637            "menu", "choice", new Object JavaDoc[][] {
6638                { "integer", "mnemonic", "paint", integer_1 } },
6639            "menuitem", "choice", new Object JavaDoc[][] {
6640                { "keystroke", "accelerator", null, null },
6641                { "method", "action" },
6642                { "integer", "mnemonic", "paint", integer_1 } },
6643            "checkboxmenuitem", "menuitem", new Object JavaDoc[][] {
6644                { "boolean", "selected", "paint", Boolean.FALSE }, //...group
6645
{ "string", "group", "paint", null } }, //...group
6646
"popupmenu", "component", new Object JavaDoc[][] {
6647                { "method", "menushown" } }, // Post menu: Shift+F10
6648
"bean", "component", new Object JavaDoc[][] {
6649                { "bean", "bean", null, null } }
6650        };
6651    }
6652}
Popular Tags