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