Source Code Cross Referenced for Thinlet.java in  » Swing-Library » thinlet » thinlet » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Swing Library » thinlet » thinlet 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:/* Thinlet GUI toolkit - www.thinlet.com
0002: * Copyright (C) 2002-2003 Robert Bajzat (robert.bajzat@thinlet.com)
0003: * 
0004: * This library is free software; you can redistribute it and/or
0005: * modify it under the terms of the GNU Lesser General Public
0006: * License as published by the Free Software Foundation; either
0007: * version 2.1 of the License, or (at your option) any later version.
0008: * 
0009: * This library is distributed in the hope that it will be useful,
0010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
0011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
0012: * Lesser General Public License for more details.
0013: * 
0014: * You should have received a copy of the GNU Lesser General Public
0015: * License along with this  library; if not, write to the Free Software
0016: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */
0017:package thinlet;
0018:
0019:import java.awt.*;
0020:import java.awt.datatransfer.*;
0021:import java.awt.image.*;
0022:import java.awt.event.*;
0023:import java.lang.reflect.*;
0024:import java.io.*;
0025:import java.net.*;
0026:import java.util.*;
0027:
0028:/**
0029: *
0030: */
0031:public class Thinlet extends Container
0032:	implements  Runnable, Serializable {
0033:
0034:	private transient Font font;
0035:	private transient Color c_bg;
0036:	private transient Color c_text;
0037:	private transient Color c_textbg;
0038:	private transient Color c_border;
0039:	private transient Color c_disable;
0040:	private transient Color c_hover;
0041:	private transient Color c_press;
0042:	private transient Color c_focus;
0043:	private transient Color c_select;
0044:	private transient Color c_ctrl = null;
0045:	private transient int block;
0046:	private transient Image hgradient, vgradient;
0047:
0048:	private transient Thread timer;
0049:	private transient long watchdelay;
0050:	private transient long watch;
0051:	private transient String clipboard;
0052:	private transient ResourceBundle resourcebundle; // for internationalization
0053:	
0054:	private static ResourceBundle langResource = null; // for I18N
0055:	private static ResourceBundle langResourceDefault = null; // for I18N
0056:	private transient boolean allI18n = false; // for I18N
0057:	
0058:	// enter the starting characters of a list item text within a short time to select
0059:	private transient String findprefix = "";
0060:	private transient long findtime;
0061:
0062:	private Object content = createImpl("desktop");
0063:	private transient Object mouseinside;
0064:	private transient Object insidepart;
0065:	private transient Object mousepressed;
0066:	private transient Object pressedpart;
0067:	private transient int referencex, referencey;
0068:	private transient int mousex, mousey;
0069:	private transient Object focusowner;
0070:	private transient boolean focusinside;
0071:	private transient Object popupowner;
0072:	private transient Object tooltipowner;
0073:	//private transient int pressedkey;
0074:	
0075:	private static final int DRAG_ENTERED = AWTEvent.RESERVED_ID_MAX + 1;
0076:	private static final int DRAG_EXITED = AWTEvent.RESERVED_ID_MAX + 2;
0077:	
0078:	private static long WHEEL_MASK = 0;
0079:	private static int MOUSE_WHEEL = 0;
0080:	private static Method wheelrotation = null;
0081:	private static int evm = 0;
0082:	static {
0083:		try {
0084:			WHEEL_MASK = AWTEvent.class.getField("MOUSE_WHEEL_EVENT_MASK").getLong(null);
0085:			MOUSE_WHEEL = MouseEvent.class.getField("MOUSE_WHEEL").getInt(null);
0086:		} catch (Exception exc) { /* not 1.4 */ }
0087:	}
0088:	{
0089:		setFont(new Font("SansSerif", Font.PLAIN, 12));
0090:		//setFont((Font) getToolkit().getDesktopProperty("win.messagebox.font"));
0091:		setColors(0xe6e6e6, 0x000000, 0xffffff,
0092:			0x909090, 0xb0b0b0, 0xededed, 0xb9b9b9, 0x89899a, 0xc5c5dd);
0093:			
0094:		// disable global focus-manager for this component in 1.4
0095:		if (MOUSE_WHEEL != 0) {
0096:			try {
0097:				getClass().getMethod("setFocusTraversalKeysEnabled", new Class[] { Boolean.TYPE }).
0098:					invoke(this , new Object[] { Boolean.FALSE });
0099:			} catch (Exception exc) { /* never */ }
0100:		}
0101:		// set listeners flags
0102:		enableEvents(AWTEvent.COMPONENT_EVENT_MASK |
0103:			AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK |
0104:			AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | WHEEL_MASK);
0105:			// EVM has larger fillRect, fillOval, and drawImage(part), others are correct
0106:			// contributed by Ibsen Ramos-Bonilla
0107:			try {
0108:				if (System.getProperty("java.vendor").indexOf("Insignia") != -1) { evm = -1; }
0109:			} catch (Exception exc) { /* never */ }
0110:	}
0111:
0112:	/**
0113:	 * Sets the 9 colors used for components, and repaints the whole UI
0114:	 *
0115:	 * @param background the backround of panels (dialogs, desktops),
0116:	 * and disabled controls, not editable texts, lines between list items
0117:	 * (the default value if <i>#e6e6e6</i>)
0118:	 * @param text for text, arrow foreground (<i>black</i> by default)
0119:	 * @param textbackground the background of text components, and lists
0120:	 * (<i>white</i> by default)
0121:	 * @param border for outer in inner borders of enabled components
0122:	 * (<i>#909090</i> by default)
0123:	 * @param disable for text, border, arrow color in disabled components
0124:	 * (<i>#b0b0b0</i> by default)
0125:	 * @param hover indicates that the mouse is inside a button area
0126:	 * (<i>#ededed</i> by default)
0127:	 * @param press for pressed buttons,
0128:	 * gradient image is calculated using the background and this press color
0129:	 * (<i>#b9b9b9</i> by default)
0130:	 * @param focus for text caret and rectagle color marking the focus owner
0131:	 * (<i>#89899a</i> by default)
0132:	 * @param select used as the background of selected text, and list items,
0133:	 * and in slider (<i>#c5c5dd</i> by default)
0134:	 */
0135:	public void setColors(int background, int text, int textbackground,
0136:			int border, int disable, int hover, int press,
0137:			int focus, int select) {
0138:		c_bg = new Color(background); c_text = new Color(text);
0139:		c_textbg = new Color(textbackground); c_border = new Color(border);
0140:		c_disable = new Color(disable); c_hover = new Color(hover);
0141:		c_press = new Color(press); c_focus = new Color(focus);
0142:		c_select = new Color(select);
0143:		hgradient = vgradient = null;
0144:		repaint();
0145:	}
0146:	
0147:	//setDesktopProperty+
0148:
0149:	/**
0150:	 * Sets the only one font used everywhere, and revalidates the whole UI.
0151:	 * Scrollbar width/height, spinbox, and combobox button width,
0152:	 * and slider size is the same as the font height
0153:	 *
0154:	 * @param font the default font is <i>SansSerif</i>, <i>plain</i>, and <i>12pt</i>
0155:	 */
0156:	public void setFont(Font font) {
0157:		block = getFontMetrics(font).getHeight();
0158:		super .setFont(font);
0159:		this .font = font;
0160:		hgradient = vgradient = null;
0161:		if (content != null) validate(content);
0162:	}
0163:	
0164:	/**
0165:	 *
0166:	 */
0167:	private void doLayout(Object component) {
0168:		String classname = getClass(component);
0169:		if ("combobox" == classname) {
0170:			if (getBoolean(component, "editable", true)) {
0171:				Image icon = getIcon(component, "icon", null);
0172:				layoutField(component, block, false,
0173:					(icon != null) ? icon.getWidth(this ) : 0);
0174:			} // set editable -> validate (overwrite textfield repaint)
0175:			else {
0176:				int selected = getInteger(component, "selected", -1);
0177:				if (selected != -1) { //...
0178:					Object choice = getItem(component, selected);
0179:					set(component, "text", get(choice, "text"));
0180:					set(component, "icon", get(choice, "icon"));
0181:				}
0182:			}
0183:		}
0184:		else if (("textfield" == classname) || ("passwordfield" == classname)) {
0185:			layoutField(component, 0, ("passwordfield" == classname), 0);
0186:		}
0187:		else if ("textarea" == classname) {
0188:			String text = getString(component, "text", "");
0189:			int start = getInteger(component, "start", 0);
0190:			if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); }
0191:			int end = getInteger(component, "end", 0);
0192:			if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); }
0193:			
0194:			boolean wrap = getBoolean(component, "wrap", false);
0195:			char[] chars = null;
0196:			if (wrap) {
0197:				Rectangle bounds = getRectangle(component, "bounds");
0198:				chars = getChars(component, text, true, bounds.width - 4, bounds.height);
0199:				if (chars == null) { // need scrollbars
0200:					chars = getChars(component, text, true, bounds.width - block - 4, 0);
0201:				}
0202:			}
0203:			else {
0204:				chars = getChars(component, text, false, 0, 0);
0205:			}
0206:			
0207:			Font currentfont = (Font) get(component, "font");
0208:			FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
0209:			int width = 0, height = 0;
0210:			int caretx = 0; int carety = 0;
0211:			for (int i = 0, j = 0; j <= chars.length; j++) {
0212:				if ((j == chars.length) || (chars[j] == '\n')) {
0213:					width = Math.max(width, fm.charsWidth(chars, i, j - i));
0214:					if ((end >= i) && (end <= j)) {
0215:						caretx = fm.charsWidth(chars, i, end - i);
0216:						carety = height;
0217:					}
0218:					height += fm.getHeight();
0219:					i = j + 1;
0220:				}
0221:			}
0222:			layoutScroll(component, width + 2, height - fm.getLeading() + 2, 0, 0, 0, 0,
0223:				getBoolean(component, "border", true), 0);
0224:			scrollToVisible(component, caretx, carety, 2, fm.getAscent() + fm.getDescent() + 2); //?
0225:		} 
0226:		else if ("tabbedpane" == classname) {
0227:			// tabbedpane (not selected) tab padding are 1, 3, 1, and 3 pt
0228:			Rectangle bounds = getRectangle(component, "bounds");
0229:			String placement = getString(component, "placement", "top");
0230:			boolean horizontal = ((placement == "top") || (placement == "bottom"));
0231:			boolean stacked = (placement == "stacked");
0232:	
0233:			// draw up tabs in row/column
0234:			int tabd = 0; Rectangle first = null; // x/y location of tab left/top
0235:			int tabsize = 0; // max height/width of tabs
0236:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
0237:				if ((tabd == 0) && ((first = getRectangle(tab, "bounds")) != null)) {
0238:					tabd = horizontal ? first.x : first.y; // restore previous offset
0239:				}
0240:				Dimension d = getSize(tab, stacked ? 8 : horizontal ? 12 : 9,
0241:					stacked ? 3 : horizontal ? 5 : 8);
0242:				setRectangle(tab, "bounds", horizontal ? tabd : 0, horizontal ? 0 : tabd,
0243:					stacked ? bounds.width : d.width, d.height);
0244:				if (stacked) {
0245:					tabd += d.height;
0246:				} else {
0247:					tabd += (horizontal ? d.width : d.height) - 3;
0248:					tabsize = Math.max(tabsize, horizontal ? d.height : d.width);
0249:				}
0250:			}
0251:			
0252:			// match tab height/width, set tab content size
0253:			int cx = (placement == "left") ? (tabsize + 1) : 2;
0254:			int cy = (placement == "top") ? (tabsize + 1) : 2;
0255:			int cwidth = bounds.width - ((horizontal || stacked) ? 4 : (tabsize + 3));
0256:			int cheight = bounds.height - (stacked ? (tabd + 3) :
0257:				(horizontal ? (tabsize + 3) : 4));
0258:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
0259:				Rectangle r = getRectangle(tab, "bounds");
0260:				if (!stacked) {
0261:					if (horizontal) {
0262:						if (placement == "bottom") { r.y = bounds.height - tabsize; }
0263:						r.height = tabsize;
0264:					}
0265:					else {
0266:						if (placement == "right") { r.x = bounds.width - tabsize; }
0267:						r.width = tabsize;
0268:					}
0269:				}
0270:				
0271:				Object comp = get(tab, ":comp"); // relative to the tab location
0272:				if ((comp != null) && getBoolean(comp, "visible", true)) {
0273:					setRectangle(comp, "bounds",
0274:						cx - r.x, stacked ? (r.height + 1) : (cy - r.y), cwidth, cheight);
0275:					doLayout(comp);
0276:				}
0277:			}
0278:			checkOffset(component);
0279:		}
0280:		else if (("panel" == classname) || (classname == "dialog")) {
0281:			int gap = getInteger(component, "gap", 0);
0282:			int[][] grid = getGrid(component, gap);
0283:			int top = 0; int left = 0;
0284:			int contentwidth = 0; int contentheight = 0;
0285:			if (grid != null) { // has subcomponents
0286:				top = getInteger(component, "top", 0);
0287:				left = getInteger(component, "left", 0);
0288:				int bottom = getInteger(component, "bottom", 0);
0289:				int right = getInteger(component, "right", 0);
0290:				// sums the preferred size of cell widths and heights, gaps
0291:				contentwidth = left + getSum(grid[0], 0, grid[0].length, gap, false) + right;
0292:				contentheight = top + getSum(grid[1], 0, grid[1].length, gap, false) + bottom;
0293:			}
0294:			
0295:			int titleheight = getSize(component, 0, 0).height; // title text and icon
0296:			setInteger(component, ":titleheight", titleheight, 0);
0297:			boolean scrollable = getBoolean(component, "scrollable", false);
0298:			boolean border = ("panel" == classname) && getBoolean(component, "border", false);
0299:			int iborder = (border ? 1 : 0);
0300:			if (scrollable) { // set scrollpane areas
0301:				if ("panel" == classname) {
0302:					int head = titleheight / 2;
0303:					int headgap = (titleheight > 0) ? (titleheight - head - iborder) : 0;
0304:					scrollable = layoutScroll(component, contentwidth, contentheight,
0305:						head, 0, 0, 0, border, headgap);
0306:				}
0307:				else { // dialog
0308:					scrollable = layoutScroll(component, contentwidth, contentheight,
0309:						3 + titleheight, 3, 3, 3, true, 0);
0310:				}
0311:			}
0312:			if (!scrollable) { // clear scrollpane bounds //+
0313:				set(component, ":view", null); set(component, ":port", null);
0314:			}
0315:			
0316:			if (grid != null) {
0317:				int areax = 0; int areay = 0; int areawidth = 0; int areaheight = 0;
0318:				if (scrollable) {
0319:					// components are relative to the viewport
0320:					Rectangle view = getRectangle(component, ":view");
0321:					areawidth = view.width; areaheight = view.height;
0322:				}
0323:				else { // scrollpane isn't required
0324:					// components are relative to top/left corner
0325:					Rectangle bounds = getRectangle(component, "bounds");
0326:					areawidth = bounds.width; areaheight = bounds.height;
0327:					if ("panel" == classname) {
0328:						areax = iborder; areay = Math.max(iborder, titleheight);
0329:						areawidth -= 2 * iborder; areaheight -= areay + iborder;
0330:					}
0331:					else { // dialog
0332:						areax = 4; areay = 4 + titleheight;
0333:						areawidth -= 8; areaheight -= areay + 4;
0334:					}
0335:				}
0336:			
0337:				for (int i = 0; i < 2; i++) { // i=0: horizontal, i=1: vertical
0338:					// remaining space
0339:					int d = ((i == 0) ? (areawidth - contentwidth) : (areaheight - contentheight));
0340:					if (d != 0) { //+ > 0
0341:						int w = getSum(grid[2 + i], 0, grid[2 + i].length, 0, false);
0342:						if (w > 0) {
0343:							for (int j = 0; j < grid[i].length; j++) {
0344:								if (grid[2 + i][j] != 0) {
0345:									grid[i][j] += d * grid[2 + i][j] / w;
0346:								}
0347:							}
0348:						}
0349:					}
0350:				}
0351:				
0352:				Object comp = get(component, ":comp");
0353:				for (int i = 0; comp != null; comp = get(comp, ":next")) {
0354:					if (!getBoolean(comp, "visible", true)) { continue; }
0355:					int ix = areax + left + getSum(grid[0], 0, grid[4][i], gap, true);
0356:					int iy = areay + top + getSum(grid[1], 0, grid[5][i], gap, true);
0357:					int iwidth = getSum(grid[0], grid[4][i], grid[6][i], gap, false);
0358:					int iheight = getSum(grid[1], grid[5][i], grid[7][i], gap, false);
0359:					String halign = getString(comp, "halign", "fill");
0360:					String valign = getString(comp, "valign", "fill");
0361:					if ((halign != "fill") || (valign != "fill")) {
0362:						Dimension d = getPreferredSize(comp);
0363:						if (halign != "fill") {
0364:							int dw = Math.max(0, iwidth - d.width);
0365:							if (halign == "center") { ix += dw / 2; }
0366:								else if (halign == "right") { ix += dw; }
0367:							iwidth -= dw;
0368:						}
0369:						if (valign != "fill") {
0370:							int dh = Math.max(0, iheight - d.height);
0371:							if (valign == "center") { iy += dh / 2; }
0372:								else if (valign == "bottom") { iy += dh; }
0373:							iheight -= dh;
0374:						}
0375:					}
0376:					setRectangle(comp, "bounds", ix, iy, iwidth, iheight);
0377:					doLayout(comp);
0378:					i++;
0379:				}
0380:			}
0381:		}
0382:		else if ("desktop" == classname) {
0383:			Rectangle bounds = getRectangle(component, "bounds");
0384:			for (Object comp = get(component, ":comp");
0385:					comp != null; comp = get(comp, ":next")) {
0386:				String iclass = getClass(comp);
0387:				if (iclass == "dialog") {
0388:					Dimension d = getPreferredSize(comp);
0389:					if (get(comp, "bounds") == null)
0390:					setRectangle(comp, "bounds",
0391:						Math.max(0, (bounds.width - d.width) / 2),
0392:						Math.max(0, (bounds.height - d.height) / 2),
0393:						Math.min(d.width, bounds.width), Math.min(d.height, bounds.height));
0394:				} else if ((iclass != ":combolist") && (iclass != ":popup")) {
0395:					setRectangle(comp, "bounds", 0, 0, bounds.width, bounds.height);
0396:				}
0397:				doLayout(comp);
0398:			}
0399:		}
0400:		else if ("spinbox" == classname) {
0401:			layoutField(component, block, false, 0);
0402:		}
0403:		else if ("splitpane" == classname) {
0404:			Rectangle bounds = getRectangle(component, "bounds");
0405:			boolean horizontal = ("vertical" != get(component, "orientation"));
0406:			int divider = getInteger(component, "divider", -1);
0407:			int maxdiv = Math.max(0, (horizontal ? bounds.width : bounds.height) - 5);
0408:
0409:			Object comp1 = get(component, ":comp");
0410:			boolean visible1 = (comp1 != null) && getBoolean(comp1, "visible", true);
0411:			if (divider == -1) {
0412:				int d1 = 0;
0413:				if (visible1) {
0414:					Dimension d = getPreferredSize(comp1);
0415:					d1 = horizontal ? d.width : d.height;
0416:				}
0417:				divider = Math.min(d1, maxdiv);
0418:				setInteger(component, "divider", divider, -1);
0419:			}
0420:			else if (divider > maxdiv) {
0421:				setInteger(component, "divider", divider = maxdiv, -1);
0422:			}
0423:
0424:			if (visible1) {
0425:				setRectangle(comp1, "bounds", 0, 0, horizontal ? divider : bounds.width,
0426:					horizontal ? bounds.height : divider);
0427:				doLayout(comp1);
0428:			}
0429:			Object comp2 = (comp1 != null) ? get(comp1, ":next") : null;
0430:			if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
0431:				setRectangle(comp2, "bounds", horizontal ? (divider + 5) : 0,
0432:					horizontal ? 0 : (divider + 5),
0433:					horizontal ? (bounds.width - 5 - divider) : bounds.width,
0434:					horizontal ? bounds.height : (bounds.height - 5 - divider));
0435:				doLayout(comp2);
0436:			}
0437:		} 
0438:		else if (("list" == classname) ||
0439:				("table" == classname) || ("tree" == classname)) {
0440:			int line = getBoolean(component, "line", true) ? 1 : 0;
0441:			int width = 0;
0442:			int columnheight = 0;
0443:			if ("table" == classname) {
0444:				Object header = get(component, "header");
0445:				int[] columnwidths = null;
0446:				if (header != null) {
0447:					columnwidths = new int[getCount(header)];
0448:					Object column = get(header, ":comp");
0449:					for (int i = 0; i < columnwidths.length; i++) {
0450:						if (i != 0) { column = get(column, ":next"); }
0451:						columnwidths[i] = getInteger(column, "width", 80);
0452:						width += columnwidths[i];
0453:						Dimension d = getSize(column, 2, 2);
0454:						columnheight = Math.max(columnheight, d.height);
0455:					}
0456:				}
0457:				set(component, ":widths", columnwidths);
0458:			}
0459:			int y = 0;
0460:			int level = 0;
0461:			for (Object item = get(component, ":comp"); item != null;) {
0462:				int x = 0;
0463:				int iwidth = 0; int iheight = 0;
0464:				if ("table" == classname) {
0465:					iwidth = width;
0466:					for (Object cell = get(item, ":comp"); cell != null; cell = get(cell, ":next")) {
0467:						Dimension d = getSize(cell, 2, 2);
0468:						iheight = Math.max(iheight, d.height);
0469:					}
0470:				}
0471:				else {
0472:					if ("tree" == classname) {
0473:						x = (level + 1) * block;
0474:					}
0475:					Dimension d = getSize(item, 6, 2);
0476:					iwidth = d.width; iheight = d.height;
0477:					width = Math.max(width, x + d.width);
0478:				}
0479:				setRectangle(item, "bounds", x, y, iwidth, iheight);
0480:				y += iheight + line;
0481:				if ("tree" == classname) {
0482:					Object next = get(item, ":comp");
0483:					if ((next != null) && getBoolean(item, "expanded", true)) {
0484:						level++;
0485:					} else {
0486:						while (((next = get(item, ":next")) == null) && (level > 0)) {
0487:							item = getParent(item);
0488:							level--;
0489:						}
0490:					}
0491:					item = next;
0492:				} else {
0493:					item = get(item, ":next");
0494:				}
0495:			}
0496:			layoutScroll(component, width, y - line, columnheight, 0, 0, 0, true, 0);
0497:		}
0498:		else if ("menubar" == classname) { 
0499:			Rectangle bounds = getRectangle(component, "bounds");
0500:			int x = 0;
0501:			for (Object menu = get(component, ":comp");
0502:					menu != null; menu = get(menu, ":next")) {
0503:				Dimension d = getSize(menu, 8, 4);
0504:				setRectangle(menu, "bounds", x, 0, d.width, bounds.height);
0505:				x += d.width;
0506:			}
0507:		}
0508:		else if ("bean" == classname) {
0509:			Rectangle r = getRectangle(component, "bounds");
0510:			((Component) get(component, "bean")).setBounds(r);
0511:		}
0512:	}
0513:	
0514:	/**
0515:	 * Scroll tabs to make the selected one visible
0516:	 * @param component a tabbedpane
0517:	 */
0518:	private void checkOffset(Object component) {
0519:		String placement = getString(component, "placement", "top");
0520:		int selected = getInteger(component, "selected", 0); int i = 0;
0521:		if (placement == "stacked") {
0522:			int dy = 0;
0523:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
0524:				Rectangle r = getRectangle(tab, "bounds");
0525:				r.y = dy;
0526:				dy += r.height;
0527:				if (i == selected) { dy += getRectangle(get(tab, ":comp"), "bounds").height + 2; }
0528:				i++;
0529:			}
0530:			if (mouseinside == component) { // layout changed, check the hovered tab
0531:				checkLocation();
0532:			}
0533:			return;
0534:		}
0535:		boolean horizontal = ((placement == "top") || (placement == "bottom"));
0536:		Rectangle bounds = getRectangle(component, "bounds");
0537:		int panesize = horizontal ? bounds.width : bounds.height;
0538:		int first = 0; int last = 0; int d = 0;
0539:		for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
0540:			Rectangle r = getRectangle(tab, "bounds");
0541:			if (i == 0) { first = (horizontal ? r.x : r.y); }
0542:			last = (horizontal ? (r.x + r.width) : (r.y + r.height));
0543:			if (i == selected) {
0544:				int ifrom = (horizontal ? r.x : r.y) - 6;
0545:				int ito = (horizontal ? (r.x + r.width) : (r.y + r.height)) + 6;
0546:				if (ifrom < 0) { d = -ifrom; }
0547:				else if (ito > panesize) { d = panesize - ito; }
0548:			}
0549:			i++;
0550:		}
0551:		d = Math.min(-first, Math.max(d, panesize - last));
0552:		if (d != 0) {
0553:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
0554:				Rectangle r = getRectangle(tab, "bounds");
0555:				if (horizontal) { r.x += d; } else { r.y += d; }
0556:				Object comp = get(tab, ":comp"); // relative to the tab location
0557:				if ( (comp != null) && getBoolean(comp, "visible", true)) {
0558:					Rectangle rc = getRectangle(comp, "bounds");
0559:					if (horizontal) { rc.x -= d; } else { rc.y -= d; }
0560:				}
0561:			}
0562:			if (mouseinside == component) { // layout changed, check the hovered tab
0563:				checkLocation();
0564:			}
0565:		}
0566:	}
0567:	
0568:	/**
0569:	 *
0570:	 */
0571:	private char[] getChars(Object component,
0572:			String text, boolean wrap, int width, int height) {
0573:		char[] chars = (char[]) get(component, ":text");
0574:		if ((chars == null) || (chars.length != text.length())) {
0575:			chars = text.toCharArray();
0576:			set(component, ":text", chars);
0577:		}
0578:		else text.getChars(0, chars.length, chars, 0);
0579:		
0580:		if (wrap) {
0581:			Font currentfont = (Font) get(component, "font");
0582:			FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
0583:			int lines = (height - 4 + fm.getLeading()) / fm.getHeight();
0584:			boolean prevletter = false; int n = chars.length; int linecount = 0;
0585:			for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k)
0586:				if (((k == n) || (chars[k] == '\n') || (chars[k] == ' ')) &&
0587:						(j > i) && (fm.charsWidth(chars, i, k - i) > width)) {
0588:					chars[j] = '\n';
0589:					k--; // draw line to the begin of the current word (+ spaces) if it is out of width
0590:				}
0591:				else if ((k == n) || (chars[k] == '\n')) { // draw line to the text/line end
0592:					j = k; prevletter = false;
0593:				}
0594:				else {
0595:					if ((chars[k] == ' ') && (prevletter || (j > i))) { j = k; } // keep spaces starting the line
0596:					prevletter = (chars[k] != ' ');
0597:					continue;
0598:				}
0599:				linecount++;
0600:				if ((lines != 0) && (linecount == lines)) { return null; }
0601:				i = j + 1;
0602:			}
0603:		}
0604:		return chars;
0605:	}
0606:	
0607:	/**
0608:	 *
0609:	 */
0610:	/*private boolean wrap(char[] chars, int width, int lines) {
0611:		FontMetrics fm = getFontMetrics(font);
0612:		boolean prevletter = false; int n = chars.length; int linecount = 0;
0613:		for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k)
0614:			if (((k == n) || (chars[k] == '\n') || (chars[k] == ' ')) &&
0615:					(j > i) && (fm.charsWidth(chars, i, k - i) > width)) {
0616:				chars[j] = '\t';
0617:				k--; // draw line to the begin of the current word (+ spaces) if it is out of width
0618:			}
0619:			else if ((k == n) || (chars[k] == '\n')) { // draw line to the text/line end
0620:				j = k; prevletter = false;
0621:			}
0622:			else {
0623:				if (chars[k] == '\t') { chars[k] = ' '; }
0624:				if ((chars[k] == ' ') && (prevletter || (j > i))) { j = k; } // keep spaces starting the line
0625:				prevletter = (chars[k] != ' ');
0626:				continue;
0627:			}
0628:			linecount++;
0629:			if ((lines != 0) && (linecount == lines)) { return false; }
0630:			i = j + 1;
0631:		}
0632:		return true;
0633:	}*/
0634:	
0635:	/**
0636:	 * @param component a menuitem
0637:	 * @return key modifier strings and key text
0638:	 */
0639:	private String getAccelerator(Object component) {
0640:		Object accelerator = get(component, "accelerator");
0641:		if (accelerator != null) {
0642:			long keystroke = ((Long) accelerator).longValue();
0643:			int keycode = (int) (keystroke >> 32);
0644:			int modifiers = (int) (keystroke & 0xffff);
0645:			return KeyEvent.getKeyModifiersText(keycode) + " " +
0646:				KeyEvent.getKeyText(modifiers);
0647:		}
0648:		return null;
0649:	}
0650:	
0651:	/**
0652:	 * Pop up the list of choices for the given combobox
0653:	 * @param combobox
0654:	 * @return the created combolist
0655:	 */
0656:	private Object popupCombo(Object combobox) {
0657:		// combobox bounds relative to the root desktop
0658:		int combox = 0, comboy = 0, combowidth = 0, comboheight = 0;
0659:		for (Object comp = combobox; comp != content; comp = getParent(comp)) {
0660:			Rectangle r = getRectangle(comp, "bounds");
0661:			combox += r.x; comboy += r.y;
0662:			Rectangle view = getRectangle(comp, ":view");
0663:			if (view != null) {
0664:				combox -= view.x; comboy -= view.y;
0665:				Rectangle port = getRectangle(comp, ":port");
0666:				combox += port.x; comboy+= port.y;
0667:			}
0668:			if (comp == combobox) { combowidth = r.width; comboheight = r.height; }
0669:		}
0670:		// :combolist -> combobox and combobox -> :combolist 
0671:		Object combolist = createImpl(":combolist");
0672:		set(combolist, "combobox", combobox);
0673:		set(combobox, ":combolist", combolist);
0674:		// add :combolist to the root desktop and set the combobox as popupowner
0675:		popupowner = combobox;
0676:		insertItem(content, ":comp", combolist, 0);
0677:		set(combolist, ":parent", content);
0678:		// lay out choices verticaly and calculate max width and height sum
0679:		int pw = 0; int ph = 0;
0680:		for (Object item = get(combobox, ":comp");
0681:				item != null; item = get(item, ":next")) {
0682:			Dimension d = getSize(item, 8 , 4);
0683:			setRectangle(item, "bounds", 0, ph, d.width, d.height);
0684:			pw = Math.max(pw, d.width);
0685:			ph += d.height;
0686:		}
0687:		// set :combolist bounds
0688:		int listy = 0, listheight = 0;
0689:		int bellow = getRectangle(content, "bounds").height - comboy - comboheight - 1;
0690:		if ((ph + 2 > bellow) && (comboy - 1 > bellow)) { // popup above combobox
0691:			listy = Math.max(0, comboy - 1 - ph - 2);
0692:			listheight = Math.min(comboy - 1, ph + 2);
0693:		}
0694:		else { // popup bellow combobox
0695:			listy = comboy + comboheight + 1;
0696:			listheight = Math.min(bellow, ph + 2);
0697:		}
0698:		setRectangle(combolist, "bounds", combox, listy, combowidth, listheight);
0699:		layoutScroll(combolist, pw, ph, 0, 0, 0, 0, true, 0);
0700:		repaint(combolist);
0701:		// hover the selected item
0702:		int selected = getInteger(combobox, "selected", -1);
0703:		setInside(combolist, (selected != -1) ? getItem(combobox, selected) : null, true);
0704:		return combolist;
0705:	}
0706:	
0707:	/**
0708:	 * @param component menubar or :popup
0709:	 * @return the created popupmenu
0710:	 */
0711:	private Object popupMenu(Object component) {
0712:		Object popup = get(component, ":popup"); // first :popup child
0713:		Object selected = get(component, "selected"); // selected menu in of the component
0714:		if (popup != null) { // remove its current :popup
0715:			if (get(popup, "menu") == selected) { return null; } // but the currect one
0716:			set(popup, "selected", null);
0717:			set(popup, "menu", null);
0718:			repaint(popup);
0719:			removeItemImpl(content, popup);
0720:			set(popup, ":parent", null);
0721:			set(component, ":popup", null);
0722:			if (mouseinside == popup) {
0723:				checkLocation();
0724:			}
0725:			popupMenu(popup); // remove recursively
0726:		}
0727:		// pop up the selected menu only 
0728:		if ((selected == null) || (getClass(selected) != "menu")) { return null; }
0729:		// create the :popup, :popup.menu -> menu,
0730:		// menubar|:popup.:popup -> :popup, menubar|:popup.selected -> menu
0731:		popup = createImpl(":popup");
0732:		set(popup, "menu", selected);
0733:		set(component, ":popup", popup);
0734:		insertItem(content, ":comp", popup, 0);
0735:		set(popup, ":parent", content);
0736:		// calculates the bounds of the previous menubar/:popup relative to the root desktop
0737:		int menux = 0, menuy = 0, menuwidth = 0, menuheight = 0;
0738:		for (Object comp = component; comp != content; comp = getParent(comp)) {
0739:			Rectangle r = getRectangle(comp, "bounds");
0740:			menux += r.x; menuy += r.y;
0741:			Rectangle view = getRectangle(comp, ":view");
0742:			if (view != null) {
0743:				menux -= view.x; menuy -= view.y;
0744:				Rectangle port = getRectangle(comp, ":port");
0745:				menux += port.x; menuy+= port.y;
0746:			}
0747:			if (comp == component) { menuwidth = r.width; menuheight = r.height; }
0748:		}
0749:		// set :popup bounds
0750:		Rectangle menubounds = getRectangle(selected, "bounds");
0751:		boolean menubar = ("menubar" == getClass(component));
0752:		if (menubar) { popupowner = component; }
0753:		popup(selected, popup,
0754:			menubar ? (("bottom" != get(component, "placement")) ? 'D' : 'U') : 'R',
0755:			menubar ? (menux + menubounds.x) : menux, menuy + menubounds.y,
0756:			menubar ? menubounds.width : menuwidth,
0757:			menubar ? menuheight : menubounds.height, menubar ? 1 : 3);
0758:		return popup;
0759:	}
0760:	
0761:	/**
0762:	 * @param popupmenu
0763:	 */
0764:	private void popupPopup(Object popupmenu, int x, int y) {
0765:		// :popup.menu -> popupmenu, popupmenu.:popup -> :popup
0766:		Object popup = createImpl(":popup");
0767:		set(popup, "menu", popupmenu);
0768:		set(popupmenu, ":popup", popup);
0769:		// add :popup to the root desktop and set the combobox as popupowner
0770:		popupowner = popupmenu;
0771:		insertItem(content, ":comp", popup, 0);
0772:		set(popup, ":parent", content);
0773:		// lay out
0774:		popup(popupmenu, popup, 'D', x, y, 0, 0, 0);
0775:		// invoke menushown listener
0776:		invoke(popupmenu, null, "menushown"); // TODO before
0777:	}
0778:	
0779:	/**
0780:	 * Lays out a popupmenu
0781:	 * @param menu menubar's menu, menu's menu,
0782:	 * or component's popupmenu including items
0783:	 * @param popup created popupmenu
0784:	 * @param direction 'U' for up, 'D' for down, and 'R' for right
0785:	 * @param x menu's x location relative to the desktop
0786:	 * @param y menu's y location
0787:	 * @param width menu's width, or zero for popupmenu
0788:	 * @param height menu's height
0789:	 * @param offset inner padding relative to the menu's bounds
0790:	 */
0791:	private void popup(Object menu, Object popup,
0792:			char direction, int x, int y, int width, int height, int offset) {
0793:		int pw = 0; int ph = 0;
0794:		for (Object item = get(menu, ":comp"); item != null; item = get(item, ":next")) {
0795:			String itemclass = getClass(item);
0796:			Dimension d = (itemclass == "separator") ? new Dimension(1, 1) :
0797:				getSize(item, 8 , 4);
0798:			if (itemclass == "checkboxmenuitem") {
0799:				d.width = d.width + block + 3;
0800:				d.height = Math.max(block, d.height);
0801:			}
0802:			else if (itemclass == "menu") {
0803:				d.width += block;
0804:			}
0805:			String accelerator = getAccelerator(item); // add accelerator width
0806:			if (accelerator != null) {
0807:				d.width += 4 + getFontMetrics(font).stringWidth(accelerator); //TODO font, height and gap
0808:			}
0809:			setRectangle(item, "bounds", 1, 1 + ph, d.width, d.height);
0810:			pw = Math.max(pw, d.width);
0811:			ph += d.height;
0812:		}
0813:		pw += 2; ph += 2; // add border widths
0814:		// set :popup bounds
0815:		Rectangle desktop = getRectangle(content, "bounds");
0816:		if (direction == 'R') {
0817:			x += ((x + width - offset + pw > desktop.width) &&
0818:				(x >= pw - offset)) ? (offset - pw) : (width - offset);
0819:			if ((y + ph > desktop.height) && (ph <= y + height)) { y -= ph - height; }
0820:		} else {
0821:			boolean topspace = (y >= ph - offset); // sufficient space above
0822:			boolean bottomspace = (desktop.height - y - height >= ph - offset);
0823:			y += ((direction == 'U') ? (topspace || !bottomspace) :
0824:				(!bottomspace && topspace)) ? (offset - ph) : (height - offset);
0825:		}
0826:		setRectangle(popup, "bounds",
0827:			Math.max(0, Math.min(x, desktop.width - pw)),
0828:			Math.max(0, Math.min(y, desktop.height - ph)), pw, ph);
0829:		repaint(popup);
0830:	}
0831:
0832:	/**
0833:	 * @param item //TODO can be scrollbar string
0834:	 */
0835:	private void closeCombo(Object combobox, Object combolist, Object item) {
0836:		if ((item != null) && getBoolean(item, "enabled", true)) {
0837:			String text = getString(item, "text", "");
0838:			set(combobox, "text", text); // if editable
0839:			putProperty(combobox, "i18n.text", null); // for I18N
0840:			setInteger(combobox, "start", text.length(), 0);
0841:			setInteger(combobox, "end", 0, 0);
0842:			set(combobox, "icon", get(item, "icon"));
0843:			validate(combobox);
0844:			setInteger(combobox, "selected", getIndex(combobox, item), -1);
0845:			invoke(combobox, item, "action");
0846:		}
0847:		set(combolist, "combobox", null);
0848:		set(combobox, ":combolist", null);
0849:		removeItemImpl(content, combolist);
0850:		repaint(combolist);
0851:		set(combolist, ":parent", null);
0852:		popupowner = null;
0853:		if (mouseinside == combolist) {
0854:			checkLocation();
0855:		}
0856:	}
0857:
0858:	/**
0859:	 *
0860:	 */
0861:	private void closeup() {
0862:		if (popupowner != null) {
0863:			String classname = getClass(popupowner);
0864:			if ("menubar" == classname) {
0865:				set(popupowner, "selected", null);
0866:				popupMenu(popupowner);
0867:				repaint(popupowner); // , selected
0868:			}
0869:			else if ("combobox" == classname) {
0870:				closeCombo(popupowner, get(popupowner, ":combolist"), null);
0871:			}
0872:			else { // "popupmenu"
0873:				popupMenu(popupowner);
0874:			}
0875:			popupowner = null;
0876:		}
0877:	}
0878:
0879:	/**
0880:	 *
0881:	 */
0882:	private void showTip() {
0883:		String text = null;
0884:		tooltipowner = null;
0885:		String classname = getClass(mouseinside);
0886:		if ((classname == "tabbedpane") || (classname == "menubar") || (classname == ":popup")) {
0887:			if (insidepart != null) {
0888:				text = getString(insidepart, "tooltip", null);
0889:			}
0890:		}
0891:		else if (classname == ":combolist") {
0892:			if (insidepart instanceof  Object[]) {
0893:				text = getString(insidepart, "tooltip", null);
0894:			}
0895:		}
0896:		// TODO list table tree
0897:		if (text == null) { text = getString(mouseinside, "tooltip", null); }
0898:			else { tooltipowner = insidepart; }
0899:		if (text != null) {
0900:			FontMetrics fm = getFontMetrics(font);
0901:			int width = fm.stringWidth(text) + 4;
0902:			int height = fm.getAscent() + fm.getDescent() + 4;
0903:			if (tooltipowner == null) { tooltipowner = mouseinside; }
0904:			Rectangle bounds = getRectangle(content, "bounds");
0905:			int tx = Math.max(0, Math.min(mousex + 10, bounds.width - width));
0906:			int ty = Math.max(0, Math.min(mousey + 10, bounds.height - height));
0907:			setRectangle(tooltipowner, ":tooltipbounds", tx, ty, width, height);
0908:			repaint(tx, ty, width, height);
0909:		}
0910:	}
0911:
0912:	/**
0913:	 *
0914:	 */
0915:	private void hideTip() {
0916:		if (tooltipowner != null) {
0917:			Rectangle bounds = getRectangle(tooltipowner, ":tooltipbounds");
0918:			set(tooltipowner, ":tooltipbounds", null);
0919:			tooltipowner = null;
0920:			repaint(bounds.x, bounds.y, bounds.width, bounds.height);
0921:		}
0922:	}
0923:
0924:	/**
0925:	 *
0926:	 */
0927:	private void layoutField(Object component, int dw, boolean hidden, int left) {
0928:		int width = getRectangle(component, "bounds").width - left -dw;
0929:		String text = getString(component, "text", "");
0930:		int start = getInteger(component, "start", 0);
0931:		if (start > text.length()) { setInteger(component, "start", start = text.length(), 0); }
0932:		int end = getInteger(component, "end", 0);
0933:		if (end > text.length()) { setInteger(component, "end", end = text.length(), 0); }
0934:		int offset = getInteger(component, ":offset", 0);
0935:		int off = offset;
0936:		Font currentfont = (Font) get(component, "font");
0937:		FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
0938:		int caret = hidden ? (fm.charWidth('*') * end) :
0939:			fm.stringWidth(text.substring(0, end));
0940:		if (off > caret) {
0941:			off = caret;
0942:		}
0943:		else if (off < caret - width + 4) {
0944:			off = caret - width + 4;
0945:		}
0946:		off = Math.max(0, Math.min(off, (hidden ? (fm.charWidth('*') *
0947:			text.length()) : fm.stringWidth(text)) - width + 4)); 
0948:		if (off != offset) {
0949:			setInteger(component, ":offset", off, 0);
0950:		}
0951:	}
0952:	
0953:	/**
0954:	 * Set viewport (:port) bounds excluding borders, view position and content
0955:	 * size (:view), horizontal (:horizontal), and vertical (:vertical) scrollbar
0956:	 * bounds
0957:	 *
0958:	 * @param component scrollable widget
0959:	 * @param contentwidth preferred component width
0960:	 * @param contentheight preferred component height
0961:	 * @param top top inset (e.g. table header, dialog title, half of panel title)
0962:	 * @param left left inset (e.g. dialog border)
0963:	 * @param bottom bottom inset (e.g. dialog border)
0964:	 * @param right right inset (e.g. dialog border)
0965:	 * @param topgap (lower half of panel title)
0966:	 * @return true if scrollpane is required, otherwise false
0967:	 *
0968:	 * list: 0, 0, 0, 0, true, 0 | table: header, ... | dialog: header, 3, 3, 3, true, 0
0969:	 * title-border panel: header / 2, 0, 0, 0, true, head
0970:	 */
0971:	private boolean layoutScroll(Object component,
0972:			int contentwidth, int contentheight,
0973:			int top, int left, int bottom, int right, boolean border, int topgap) {
0974:		Rectangle bounds = getRectangle(component, "bounds");
0975:		int iborder = border ? 1 : 0; int iscroll = block + 1 - iborder;
0976:		int portwidth = bounds.width - left - right - 2 * iborder; // available horizontal space
0977:		int portheight = bounds.height - top - topgap - bottom - 2 * iborder; // vertical space
0978:		boolean hneed = contentwidth > portwidth; // horizontal scrollbar required
0979:		boolean vneed = contentheight > portheight - (hneed ? iscroll : 0); // vertical scrollbar needed
0980:		if (vneed) { portwidth -= iscroll; } // subtract by vertical scrollbar width
0981:		hneed = hneed || (vneed && (contentwidth > portwidth));
0982:		if (hneed) { portheight -= iscroll; } // subtract by horizontal scrollbar height
0983:		
0984:		setRectangle(component, ":port", left + iborder, top + iborder + topgap, portwidth, portheight);
0985:		if (hneed) { 
0986:			setRectangle(component, ":horizontal", left, bounds.height - bottom - block - 1,
0987:				bounds.width - left - right - (vneed ? block : 0), block + 1);
0988:		} else { set(component, ":horizontal", null); }
0989:		if (vneed) {
0990:			setRectangle(component, ":vertical", bounds.width - right - block - 1, top,
0991:				block + 1, bounds.height - top - bottom - (hneed ? block : 0));
0992:		} else { set(component, ":vertical", null); }
0993:		
0994:		contentwidth = Math.max(contentwidth, portwidth);
0995:		contentheight = Math.max(contentheight, portheight);
0996:		int viewx = 0, viewy = 0;
0997:		Rectangle view = getRectangle(component, ":view");
0998:		if (view != null) { // check the previous location
0999:			viewx = Math.max(0, Math.min(view.x, contentwidth - portwidth));
1000:			viewy = Math.max(0, Math.min(view.y, contentheight - portheight));
1001:		}
1002:		setRectangle(component, ":view", viewx, viewy, contentwidth, contentheight);
1003:		return vneed || hneed;
1004:	}
1005:
1006:	/**
1007:	 *
1008:	 */
1009:	private void scrollToVisible(Object component,
1010:			int x, int y, int width, int height) {
1011:		Rectangle view = getRectangle(component, ":view");
1012:		Rectangle port = getRectangle(component, ":port");
1013:		int vx = Math.max(x + width - port.width, Math.min(view.x, x));
1014:		int vy = Math.max(y + height - port.height, Math.min(view.y, y));
1015:		if ((view.x != vx) || (view.y != vy)) {
1016:			repaint(component); // horizontal | vertical
1017:			view.x = vx; view.y = vy;
1018:		}
1019:	}
1020:	
1021:	/**
1022:	 * Gets the preferred size of the root component
1023:	 *
1024:	 * @return a dimension object indicating the root component's preferred size 
1025:	 */
1026:	public Dimension getPreferredSize() {
1027:		return getPreferredSize(content);
1028:	}
1029:
1030:	/**
1031:	 *
1032:	 * @throws java.lang.IllegalArgumentException
1033:	 */
1034:	private Dimension getPreferredSize(Object component) {
1035:		int width = getInteger(component, "width", 0);
1036:		int height = getInteger(component, "height", 0);
1037:		if ((width > 0) && (height > 0)) {
1038:			return new Dimension(width, height);
1039:		}
1040:		String classname = getClass(component);
1041:		if ("label" == classname) {
1042:			return getSize(component, 0, 0);
1043:		} 
1044:		if (("button" == classname) || ("togglebutton" == classname)) {
1045:			boolean link = ("button" == classname) && (get(component, "type") == "link");
1046:			return getSize(component, link ? 0 : 12, link ? 0 : 6);
1047:		} 
1048:		if ("checkbox" == classname) {
1049:			Dimension d = getSize(component, 0, 0);
1050:			d.width = d.width + block + 3;
1051:			d.height = Math.max(block, d.height);
1052:			return d;
1053:		}
1054:		if ("combobox" == classname) {
1055:			if (getBoolean(component, "editable", true)) {
1056:				Dimension size = getFieldSize(component);
1057:				Image icon = getIcon(component, "icon", null);
1058:				if (icon != null) {
1059:					size.width += icon.getWidth(this );
1060:					size.height = Math.max(size.height, icon.getHeight(this ) + 2);
1061:				}
1062:				size.width += block;
1063:				return size;
1064:			} else {
1065:				// maximum size of current values and choices including 2-2-2-2 insets
1066:				Dimension size = getSize(component, 4 , 4);
1067:				for (Object item = get(component, ":comp"); item != null; item = get(item, ":next")) {
1068:					Dimension d = getSize(item, 4 , 4);
1069:					size.width = Math.max(d.width, size.width); size.height = Math.max(d.height, size.height);
1070:				}
1071:				size.width += block;
1072:				if (size.height == 4) { // no content nor items, set text height
1073:					Font customfont = (Font) get(component, "font");
1074:					FontMetrics fm = getFontMetrics((customfont != null) ? customfont : font);
1075:					size.height = fm.getAscent() + fm.getDescent() + 4;
1076:				}
1077:				return size;
1078:			}
1079:		}
1080:		if (("textfield" == classname) || ("passwordfield" == classname)) {
1081:			return getFieldSize(component);
1082:		}
1083:		if ("textarea" == classname) {
1084:			int columns = getInteger(component, "columns", 0);
1085:			int rows = getInteger(component, "rows", 0); // 'e' -> 'm' ?
1086:			Font currentfont = (Font) get(component, "font");
1087:			FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
1088:			return new Dimension(
1089:				((columns > 0) ? (columns * fm.charWidth('e') + 2) : 76) + 2 + block,
1090:				((rows > 0) ? (rows * fm.getHeight() - fm.getLeading() + 2) : 76) + 2 + block);
1091:		}
1092:		if ("tabbedpane" == classname) {
1093:			String placement = getString(component, "placement", "top");
1094:			boolean horizontal = ((placement != "left") && (placement != "right"));
1095:			int tabsize = 0; // max tab height (for horizontal),
1096:			// max tabwidth (for vertical), or sum of tab heights for stacked
1097:			int contentwidth = 0; int contentheight = 0; // max content size
1098:			for (Object tab = get(component, ":comp");
1099:					tab != null; tab = get(tab, ":next")) {
1100:				Dimension d = getSize(tab, 0, 0);
1101:				if (placement == "stacked") { tabsize += d.height + 3; }
1102:				else { tabsize = Math.max(tabsize, horizontal ? d.height + 5 : d.width + 9); }
1103:				
1104:				Object comp = get(tab, ":comp");
1105:				if ((comp != null) && getBoolean(comp, "visible", true)) {
1106:					Dimension dc = getPreferredSize(comp);
1107:					contentwidth = Math.max(contentwidth, dc.width);
1108:					contentheight = Math.max(contentheight, dc.height);
1109:				}
1110:			}
1111:			return new Dimension(contentwidth + (horizontal ? 4 : (tabsize + 3)),
1112:				contentheight + (horizontal ? (tabsize + 3) : 4));
1113:		}
1114:		if (("panel" == classname) || (classname == "dialog")) {
1115:			// title text and icon height
1116:			Dimension size = getSize(component, 0, 0);
1117:			// add border size
1118:			if (classname == "dialog") {
1119:				size.width = 8; size.height += 8; // title width neglected
1120:			}
1121:			else if (getBoolean(component, "border", false)) { // bordered panel
1122:				size.width = 2; size.height += (size.height > 0) ? 1 : 2; // title includes line
1123:			}
1124:			else { size.width = 0; } // title width is clipped
1125:			// add paddings
1126:			size.width += getInteger(component, "left", 0) + getInteger(component, "right", 0);
1127:			size.height += getInteger(component, "top", 0) + getInteger(component, "bottom", 0);
1128:			// add content preferred size
1129:			int gap = getInteger(component, "gap", 0);
1130:			int[][] grid = getGrid(component, gap);
1131:			if (grid != null) { // has components
1132:				size.width += getSum(grid[0], 0, grid[0].length, gap, false);
1133:				size.height += getSum(grid[1], 0, grid[1].length, gap, false);
1134:			}
1135:			return size;
1136:		}
1137:		else if ("desktop" == classname) {
1138:			Dimension size = new Dimension();
1139:			for (Object comp = get(component, ":comp");
1140:					comp != null; comp = get(comp, ":next")) {
1141:				String iclass = getClass(comp);
1142:				if ((iclass != "dialog") && (iclass != ":popup") &&
1143:						(iclass != ":combolist")) {
1144:					Dimension d = getPreferredSize(comp);
1145:					size.width = Math.max(d.width, size.width);
1146:					size.height = Math.max(d.height, size.height);
1147:				}
1148:			}
1149:			return size;
1150:		}
1151:		if ("spinbox" == classname) {
1152:			Dimension size = getFieldSize(component);
1153:			size.width += block;
1154:			return size;
1155:		}
1156:		if ("progressbar" == classname) {
1157:			boolean horizontal = ("vertical" != get(component, "orientation"));
1158:			return new Dimension(horizontal ? 76 : 6, horizontal ? 6 : 76);
1159:		}
1160:		if ("slider" == classname) {
1161:			boolean horizontal = ("vertical" != get(component, "orientation"));
1162:			return new Dimension(horizontal ? 76 : 10, horizontal ? 10 : 76);
1163:		}
1164:		if ("splitpane" == classname) {
1165:			boolean horizontal = ("vertical" != get(component, "orientation"));
1166:			Object comp1 = get(component, ":comp");
1167:			Dimension size = ((comp1 == null) || !getBoolean(comp1, "visible", true)) ?
1168:				new Dimension() : getPreferredSize(comp1);
1169:			Object comp2 = get(comp1, ":next");
1170:			if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
1171:				Dimension d = getPreferredSize(comp2);
1172:				size.width = horizontal ? (size.width + d.width) :
1173:					Math.max(size.width, d.width);
1174:				size.height = horizontal ? Math.max(size.height, d.height) :
1175:					(size.height + d.height);
1176:			}
1177:			if (horizontal) { size.width += 5; } else { size.height += 5; }
1178:			return size;
1179:		}
1180:		if (("list" == classname) ||
1181:				("table" == classname) || ("tree" == classname)) {
1182:			return new Dimension(76 + 2 + block, 76 + 2 + block);
1183:		}
1184:		if ("separator" == classname) {
1185:			return new Dimension(1, 1);
1186:		}
1187:		if ("menubar" == classname) { 
1188:			Dimension size = new Dimension(0, 0);
1189:			for (Object menu = get(component, ":comp");
1190:					menu != null; menu = get(menu, ":next")) {
1191:				Dimension d = getSize(menu, 8, 4);
1192:				size.width += d.width;
1193:				size.height = Math.max(size.height, d.height);
1194:			}
1195:			return size;
1196:		}
1197:		if ("bean" == classname) {
1198:				return ((Component) get(component, "bean")).getPreferredSize();
1199:		}
1200:		throw new IllegalArgumentException((String) classname);
1201:	}
1202:
1203:	/**
1204:	 * @param component a container
1205:	 * @param gap space between components
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 component, int gap) {
1217:		int count = 0; // count of the visible subcomponents
1218:		for (Object 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 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 component) {
1367:		String text = getString(component, "text", "");
1368:		int columns = getInteger(component, "columns", 0);
1369:		Font currentfont = (Font) get(component, "font");
1370:		FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
1371:		return new Dimension(((columns > 0) ?
1372:			(columns * fm.charWidth('e')) : 76) + 4,
1373:			fm.getAscent() + fm.getDescent() + 4); // fm.stringWidth(text)
1374:	}
1375:
1376:	/**
1377:	 * @param component a widget including the text and icon parameters
1378:	 * @param dx increase width by this value
1379:	 * @param dy increase height by this value
1380:	 * @return size of the text and the image (plus a gap) including the given offsets
1381:	 */
1382:	private Dimension getSize(Object component, int dx, int dy) {
1383:		String text = getString(component, "text", null);
1384:		int tw = 0; int th = 0;
1385:		if (text != null) {
1386:			Font customfont = (Font) get(component, "font");
1387:			FontMetrics fm = getFontMetrics((customfont != null) ? customfont : font);
1388:			tw = fm.stringWidth(text);
1389:			th = fm.getAscent() + fm.getDescent();
1390:		}
1391:		Image icon = getIcon(component, "icon", null);
1392:		int iw = 0; int ih = 0;
1393:		if (icon != null) {
1394:			iw = icon.getWidth(this );
1395:			ih = icon.getHeight(this );
1396:			if (text != null) { iw += 2; }
1397:		}
1398:		return new Dimension(tw + iw + dx, Math.max(th, ih) + dy);
1399:	}
1400:
1401:	/**
1402:	 * Invokes the paint method
1403:	 */
1404:	public void update(Graphics g) {
1405:		paint(g);
1406:	}
1407:
1408:	/*public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
1409:		if (infoflags == ImageObserver.ALLBITS) {
1410:			validate(content);
1411:		}
1412:		return super.imageUpdate(img, infoflags, x, y, width, height);
1413:	}*/
1414:
1415:	/**
1416:	 * Paints the components inside the graphics clip area
1417:	 */
1418:	public void paint(Graphics g) {
1419:		g.setFont(font);
1420:		if (hgradient == null) {
1421:			int[][] pix = new int[2][block * block];
1422:			int r1 = c_bg.getRed(); int r2 = c_press.getRed();
1423:			int g1 = c_bg.getGreen(); int g2 = c_press.getGreen();
1424:			int b1 = c_bg.getBlue(); int b2 = c_press.getBlue();
1425:			for (int i = 0; i < block; i++) {
1426:				int cr = r1 - (r1 - r2) * i / block;
1427:				int cg = g1 - (g1 - g2) * i / block;
1428:				int cb = b1 - (b1 - b2) * i / block;
1429:				int color = (255 << 24) | (cr << 16) | (cg << 8) | cb;
1430:				for (int j = 0; j < block; j++) {
1431:					pix[0][i * block + j] = color;
1432:					pix[1][j * block + i] = color;
1433:				}
1434:			}
1435:			hgradient = createImage(new MemoryImageSource(block, block, pix[0], 0, block));
1436:			vgradient = createImage(new MemoryImageSource(block, block, pix[1], 0, block));
1437:		}
1438:		//g.setColor(Color.orange);
1439:		//g.fillRect(0, 0, getSize().width, getSize().height);
1440:		//long time = System.currentTimeMillis();
1441:		Rectangle clip = g.getClipBounds();
1442:		///dg.setClip(r.x, r.y, r.width, r.height);
1443:		paint(g, clip.x, clip.y, clip.width, clip.height, content, isEnabled());
1444:		//System.out.println(System.currentTimeMillis() - time);
1445:		//g.setClip(0, 0, getSize().width, getSize().height);
1446:		//g.setColor(Color.red); g.drawRect(clip.x, clip.y, clip.width - 1, clip.height - 1);
1447:	}
1448:	
1449:	/**
1450:	 * Fill a given part of a MemoryImageSource with gradient
1451:	 * @param pix arrays of source pixels
1452:	 * @param offset image width 
1453:	 * @param x gradient left location
1454:	 * @param y fill top location
1455:	 * @param width gradient width size
1456:	 * @param height fill height size
1457:	 * @param color1 top/left color
1458:	 * @param color2 bottom/right color
1459:	 * @param horizontal horizontal if true, vertical otherwise
1460:	 */
1461:	private static void fillGradient(int[] pix, int offset, int x, int y, int width, int height,
1462:			int color1, int color2, boolean horizontal) {
1463:		int r1 = (color1 >> 16) & 0xff; int rd = ((color2 >> 16) & 0xff) - r1;
1464:		int g1 = (color1 >> 8) & 0xff; int gd = ((color2 >> 8) & 0xff) - g1;
1465:		int b1 = color1 & 0xff; int bd = (color2 & 0xff) - b1;
1466:		int gs = horizontal ? width : height; int fs = horizontal ? height : width;
1467:		for (int i = 0; i < gs; i++) {
1468:			int color = 0xff000000 | ((r1 + rd * i / gs) << 16) |
1469:				((g1 + gd * i / gs) << 8) | (b1 + bd * i / gs);
1470:			for (int j = 0; j < fs; j++) {
1471:				pix[x + (horizontal ? i : j) + (y + (horizontal ? j : i)) * offset] = color;
1472:			}
1473:		}
1474:	}
1475:
1476:	/**
1477:	 * @param clipx the cliping rectangle is relative to the component's
1478:	 * parent location similar to the component's bounds rectangle
1479:	 * @param clipy
1480:	 * @param clipwidth
1481:	 * @param clipheight
1482:	 * @throws java.lang.IllegalArgumentException
1483:	 */
1484:	private void paint(Graphics g,
1485:			int clipx, int clipy, int clipwidth, int clipheight,
1486:			Object component, boolean enabled) {
1487:		if (!getBoolean(component, "visible", true)) { return; }
1488:		Rectangle bounds = getRectangle(component, "bounds");
1489:		if (bounds == null) { return; }
1490:		// negative component width indicates invalid component layout
1491:		if (bounds.width < 0) {
1492:			bounds.width = Math.abs(bounds.width);
1493:			doLayout(component);
1494:		}
1495:		// return if the component was out of the cliping rectangle
1496:		if ((clipx + clipwidth < bounds.x) ||
1497:				(clipx > bounds.x + bounds.width) ||
1498:				(clipy + clipheight < bounds.y) ||
1499:				(clipy > bounds.y + bounds.height)) {
1500:			return;
1501:		}
1502:		// set the clip rectangle relative to the component location
1503:		clipx -= bounds.x; clipy -= bounds.y;
1504:		g.translate(bounds.x, bounds.y); 
1505:		//g.setClip(0, 0, bounds.width, bounds.height);
1506:		String classname = getClass(component);
1507:		boolean pressed = (mousepressed == component);
1508:		boolean inside = (mouseinside == component) &&
1509:			((mousepressed == null) || pressed);
1510:		boolean focus = focusinside && (focusowner == component);
1511:		enabled = getBoolean(component, "enabled", true); //enabled &&
1512:
1513:		if ("label" == classname) {
1514:			paint(component, 0, 0, bounds.width, bounds.height,
1515:				g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1516:				0, 0, 0, 0, false, enabled ? 'e' : 'd', "left", true, false);
1517:		}
1518:		else if (("button" == classname) || ("togglebutton" == classname)) {
1519:			boolean toggled = ("togglebutton" == classname) && getBoolean(component, "selected", false);
1520:			boolean link = ("button" == classname) && (get(component, "type") == "link");
1521:			if (link) {
1522:				paint(component, 0, 0, bounds.width, bounds.height,
1523:					g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1524:					0, 0, 0, 0, focus, enabled ? (pressed ? 'e' : 'l') : 'd', "center",
1525:					true, enabled && (inside != pressed));
1526:			} else { // disabled toggled
1527:				char mode = enabled ? ((inside != pressed) ? 'h' : ((pressed || toggled) ? 'p' : 'g')) : 'd';
1528:				paint(component, 0, 0, bounds.width, bounds.height,
1529:					g, clipx, clipy, clipwidth, clipheight, true, true, true, true,
1530:					2, 5, 2, 5, focus, mode, "center", true, false);
1531:				//(enabled && ("button" == classname) && get(component, "type") == "default")...
1532:			}
1533:		}
1534:		else if ("checkbox" == classname) {
1535:			paint(component, 0, 0, bounds.width, bounds.height,
1536:				g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1537:				0, block + 3, 0, 0, false, enabled ? 'e' : 'd', "left", true, false);
1538:
1539:			boolean selected = getBoolean(component, "selected", false);
1540:			String group = getString(component, "group", null);
1541:			Color border = enabled ? c_border : c_disable;
1542:			Color foreground = enabled ? ((inside != pressed) ? c_hover :
1543:				(pressed ? c_press : c_ctrl)) : c_bg;
1544:			int dy = (bounds.height - block + 2) / 2;
1545:			if (group == null) {
1546:				paintRect(g, 1, dy + 1, block - 2, block - 2,
1547:					border, foreground, true, true, true, true, true);
1548:			} else {
1549:				g.setColor((foreground != c_ctrl) ? foreground : c_bg);
1550:				g.fillOval(1, dy + 1, block - 3 + evm, block - 3 + evm);
1551:				g.setColor(border);
1552:				g.drawOval(1, dy + 1, block - 3, block - 3);
1553:			}
1554:			if (focus) {
1555:				g.setColor(c_focus);
1556:				if (group == null) {
1557:					g.drawRect(3, dy + 3, block - 7, block - 7);
1558:				} else {
1559:					g.drawOval(3, dy + 3, block - 7, block - 7);
1560:				}
1561:			}
1562:			if((!selected && inside && pressed) ||
1563:					(selected && (!inside || !pressed))) {
1564:				g.setColor(enabled ? c_text : c_disable);
1565:				if (group == null) {
1566:					g.fillRect(3, dy + block - 9, 2 + evm, 6 + evm);
1567:					g.drawLine(3, dy + block - 4, block - 4, dy + 3);
1568:					g.drawLine(4, dy + block - 4, block - 4, dy + 4);
1569:				} else {
1570:					g.fillOval(5, dy + 5, block - 10 + evm, block - 10 + evm);
1571:					g.drawOval(4, dy + 4, block - 9, block - 9);
1572:				}
1573:			}
1574:		}
1575:		else if ("combobox" == classname) {
1576:			if (getBoolean(component, "editable", true)) {
1577:				Image icon = getIcon(component, "icon", null);
1578:				int left = (icon != null) ? icon.getWidth(this ) : 0;
1579:				paintField(g, clipx, clipy, clipwidth, clipheight, component,
1580:					bounds.width - block, bounds.height,
1581:					inside, pressed, focus, enabled, false, left);
1582:				if (icon != null) {
1583:					g.drawImage(icon, 2, (bounds.height - icon.getHeight(this )) / 2, this );
1584:				}
1585:				paintArrow(g, bounds.width - block, 0, block, bounds.height,
1586:					'S', enabled, inside, pressed, "down", true, false, true, true, true);
1587:			} else {
1588:				paint(component, 0, 0, bounds.width, bounds.height,
1589:					g, clipx, clipy, clipwidth, clipheight,
1590:					true, true, true, true, 1, 1, 1, 1 + block, focus,
1591:					enabled ? ((inside != pressed) ? 'h' : (pressed ? 'p' : 'g')) : 'd',
1592:					"left", false, false);
1593:				g.setColor(enabled ? c_text : c_disable);
1594:				paintArrow(g, bounds.width - block, 0, block, bounds.height, 'S');
1595:			}
1596:		}
1597:		else if (":combolist" == classname) {
1598:			paintScroll(component, classname, bounds, pressed, inside, focus, enabled,
1599:				g, clipx, clipy, clipwidth, clipheight);
1600:		}
1601:		else if (("textfield" == classname) || ("passwordfield" == classname)) {
1602:			paintField(g, clipx, clipy, clipwidth, clipheight, component,
1603:				bounds.width, bounds.height,
1604:				inside, pressed, focus, enabled, ("passwordfield" == classname), 0);
1605:		}
1606:		else if ("textarea" == classname) {
1607:			paintScroll(component, classname, bounds, pressed, inside, focus, enabled,
1608:				g, clipx, clipy, clipwidth, clipheight);
1609:		}
1610:		else if ("tabbedpane" == classname) {
1611:			int i = 0; Object selectedtab = null;
1612:			int selected = getInteger(component, "selected", 0);
1613:			String placement = getString(component, "placement", "top");
1614:			boolean horizontal = ((placement == "top") || (placement == "bottom"));
1615:			boolean stacked = (placement == "stacked");
1616:			int bx = stacked ? 0 : horizontal ? 2 : 1, by = stacked ? 0 : horizontal ? 1 : 2,
1617:				bw = 2 * bx, bh = 2 * by;
1618:			// paint tabs except the selected one
1619:			g.clipRect(0, 0, bounds.width, bounds.height); //+clip
1620:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
1621:				Rectangle r = getRectangle(tab, "bounds");
1622:				if (selected != i) {
1623:					boolean hover = inside && (mousepressed == null) && (insidepart == tab);
1624:					boolean tabenabled = enabled && getBoolean(tab, "enabled", true);
1625:					paint(tab, r.x + bx, r.y + by, r.width - bw, r.height - bh,
1626:						g, clipx, clipy, clipwidth, clipheight,
1627:						(placement != "bottom"), (placement != "right"),
1628:						!stacked && (placement != "top"), (placement != "left"),
1629:						1, 3, 1, 3, false, tabenabled ? (hover ? 'h' : 'g') : 'd', "left", true, false);
1630:										
1631:				} else {
1632:					selectedtab = tab;
1633:					// paint tabbedpane border
1634:					paint(tab, (placement == "left") ? r.width - 1 : 0,
1635:						stacked ? (r.y + r.height - 1) : (placement == "top") ? r.height - 1 : 0,
1636:						(horizontal || stacked) ? bounds.width : (bounds.width - r.width + 1),
1637:						stacked ? (bounds.height - r.y - r.height + 1) :
1638:						horizontal ? (bounds.height - r.height + 1) : bounds.height,
1639:						g, true, true, true, true, enabled ? 'e' : 'd');
1640:					Object comp = get(selectedtab, ":comp");
1641:					if ((comp != null) && getBoolean(comp, "visible", true)) {
1642:						clipx -= r.x; clipy -= r.y; g.translate(r.x, r.y); // relative to tab
1643:						paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
1644:						clipx += r.x; clipy += r.y; g.translate(-r.x, -r.y);
1645:					}
1646:				}
1647:				i++;
1648:			}
1649:			
1650:			// paint selected tab and its content
1651:			if (selectedtab != null) {
1652:				Rectangle r = getRectangle(selectedtab, "bounds");
1653:				// paint selected tab
1654:				int ph = stacked ? 3 : (horizontal ? 5 : 4);
1655:				int pv = stacked ? 1 : (horizontal ? 2 : 3);
1656:				paint(selectedtab, r.x, r.y, r.width, r.height,
1657:					g, clipx, clipy, clipwidth, clipheight,
1658:					(placement != "bottom"), (placement != "right"),
1659:					!stacked && (placement != "top"), (placement != "left"),
1660:					pv, ph, pv, ph, focus, enabled ? 'b' : 'i', "left", true, false);
1661:			}
1662:			g.setClip(clipx, clipy, clipwidth, clipheight); //+clip
1663:		}
1664:		else if (("panel" == classname) || ("dialog" == classname)) {
1665:			int titleheight = getInteger(component, ":titleheight", 0);
1666:			if ("dialog" == classname) {
1667:				paint(component, 0, 0, bounds.width, 3 + titleheight,
1668:					g, clipx, clipy, clipwidth, clipheight, true, true, false, true,
1669:					1, 2, 1, 2, false, 'g', "left", false, false);
1670:				int controlx = bounds.width - titleheight - 1;
1671:				if (getBoolean(component, "closable", false)) {
1672:					paint(component, controlx, 3, titleheight - 2, titleheight - 2,
1673:						g, true, true, true, true, 'g'); controlx -= titleheight;
1674:				}
1675:				if (getBoolean(component, "maximizable", false)) {
1676:					paint(component, controlx, 3, titleheight - 2, titleheight - 2,
1677:						g, true, true, true, true, 'g'); controlx -= titleheight;
1678:				}
1679:				if (getBoolean(component, "iconifiable", false)) {
1680:					paint(component, controlx, 3, titleheight - 2, titleheight - 2,
1681:						g, true, true, true, true, 'g');
1682:				}
1683:				paintRect(g, 0, 3 + titleheight, bounds.width, bounds.height - 3 - titleheight,
1684:					c_border, c_press, false, true, true, true, true); // lower part excluding titlebar
1685:				paint(component, // content area
1686:					3, 3 + titleheight, bounds.width - 6, bounds.height - 6 - titleheight,
1687:					g, true, true, true, true, 'b');
1688:			} else { // panel
1689:				boolean border = getBoolean(component, "border", false);
1690:				paint(component, 0, titleheight / 2, bounds.width, bounds.height - (titleheight / 2),
1691:					g, border, border, border, border, enabled ? 'e' : 'd');
1692:				paint(component, 0, 0, bounds.width, titleheight, // panel title
1693:					g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1694:					0, 3, 0, 3, false, enabled ? 'x' : 'd', "left", false, false);
1695:			}
1696:			
1697:			if (get(component, ":port") != null) {
1698:				paintScroll(component, classname, bounds, pressed, inside, focus, enabled,
1699:					g, clipx, clipy, clipwidth, clipheight);
1700:			}
1701:			else {
1702:				for (Object comp = get(component, ":comp");
1703:						comp != null; comp = get(comp, ":next")) {
1704:					paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
1705:				}
1706:			}
1707:		}
1708:		else if ("desktop" == classname) {
1709:			paintRect(g, 0, 0, bounds.width, bounds.height,
1710:				c_border, c_bg, false, false, false, false, true);
1711:			paintReverse(g, clipx, clipy, clipwidth, clipheight,
1712:				get(component, ":comp"), enabled);
1713:			//g.setColor(Color.red); if (clip != null) g.drawRect(clipx, clipy, clipwidth, clipheight);
1714:			if ((tooltipowner != null) && (component == content)) {
1715:				Rectangle r = getRectangle(tooltipowner, ":tooltipbounds");
1716:				paintRect(g, r.x, r.y, r.width, r.height,
1717:					c_border, c_bg, true, true, true, true, true);
1718:				String text = getString(tooltipowner, "tooltip", null);
1719:				g.setColor(c_text);
1720:				g.drawString(text, r.x + 2, r.y + g.getFontMetrics().getAscent() + 2); //+nullpointerexception
1721:			}			
1722:		}
1723:		else if ("spinbox" == classname) {
1724:			paintField(g, clipx, clipy, clipwidth, clipheight, component,
1725:				bounds.width - block, bounds.height,
1726:				inside, pressed, focus, enabled, false, 0);
1727:			paintArrow(g, bounds.width - block, 0, block, bounds.height / 2,
1728:					'N', enabled, inside, pressed, "up", true, false, false, true, true);
1729:			paintArrow(g, bounds.width - block, bounds.height / 2,
1730:				block, bounds.height - (bounds.height / 2),
1731:				'S', enabled, inside, pressed, "down", true, false, true, true, true);
1732:		}
1733:		else if ("progressbar" == classname) {
1734:			int minimum = getInteger(component, "minimum", 0);
1735:			int maximum = getInteger(component, "maximum", 100);
1736:			int value = getInteger(component, "value", 0);
1737:			boolean horizontal = ("vertical" != get(component, "orientation"));
1738:			int length = (value - minimum) *
1739:				((horizontal ? bounds.width : bounds.height) - 1) / (maximum - minimum);
1740:			paintRect(g, 0, 0, horizontal ? length : bounds.width,
1741:				horizontal ? bounds.height : length, enabled ? c_border : c_disable,
1742:				c_select, true, true, horizontal, !horizontal, true);
1743:			paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
1744:				horizontal ? (bounds.width - length) : bounds.width	,
1745:				horizontal ? bounds.height : (bounds.height - length),
1746:				enabled ? c_border : c_disable, c_bg, true, true, true, true, true);
1747:		}
1748:		else if ("slider" == classname) {
1749:			int minimum = getInteger(component, "minimum", 0);
1750:			int maximum = getInteger(component, "maximum", 100);
1751:			int value = getInteger(component, "value", 0);
1752:			boolean horizontal = ("vertical" != get(component, "orientation"));
1753:			int length = (value - minimum) *
1754:				((horizontal ? bounds.width : bounds.height) - block) /
1755:				(maximum - minimum);
1756:			paintRect(g, horizontal ? 0 : 3, horizontal ? 3 : 0,
1757:				horizontal ? length : (bounds.width - 6),
1758:				horizontal ? (bounds.height - 6) : length,
1759:				enabled ? c_border : c_disable,
1760:				c_bg, true, true, horizontal, !horizontal, true);
1761:			paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
1762:				horizontal ? block : bounds.width, horizontal ? bounds.height : block,
1763:				enabled ? c_border : c_disable,
1764:				enabled ? c_ctrl : c_bg, true, true, true, true, true);
1765:			if (focus) {
1766:				g.setColor(c_focus);
1767:				g.drawRect(horizontal ? (length + 2) : 2, horizontal ? 2 : (length + 2),
1768:					(horizontal ? block : bounds.width) - 5,
1769:					(horizontal ? bounds.height : block) - 5);
1770:				//g.drawRect(length + 1, 1, block - 3, bounds.height - 3);
1771:			}
1772:			paintRect(g, horizontal ? (block + length) : 3,
1773:				horizontal ? 3 : (block + length),
1774:				bounds.width - (horizontal ? (block + length) : 6),
1775:				bounds.height - (horizontal ? 6 : (block + length)),
1776:				enabled ? c_border : c_disable,
1777:				c_bg, horizontal, !horizontal, true, true, true);
1778:		}
1779:		else if ("splitpane" == classname) {
1780:			boolean horizontal = ("vertical" != get(component, "orientation"));
1781:			int divider = getInteger(component, "divider", -1);
1782:			paintRect(g, horizontal ? divider : 0, horizontal ? 0 : divider,
1783:				horizontal ? 5 : bounds.width, horizontal ? bounds.height : 5,
1784:				c_border, c_bg, false, false, false, false, true);
1785:			g.setColor(enabled ? (focus ? c_focus : c_border) : c_disable);
1786:			int xy = horizontal ? bounds.height : bounds.width;
1787:			int xy1 = Math.max(0, xy / 2 - 12);
1788:			int xy2 = Math.min(xy / 2 + 12, xy - 1);
1789:			for (int i = divider + 1; i < divider + 4; i += 2) {
1790:				if (horizontal) { g.drawLine(i, xy1, i, xy2); }
1791:					else { g.drawLine(xy1, i, xy2, i); }
1792:			}
1793:			Object comp1 = get(component, ":comp");
1794:			if (comp1 != null) {
1795:				paint(g, clipx, clipy, clipwidth, clipheight, comp1, enabled);
1796:				Object comp2 = get(comp1, ":next");
1797:				if (comp2 != null) {
1798:					paint(g, clipx, clipy, clipwidth, clipheight, comp2, enabled);
1799:				}
1800:			}
1801:		}
1802:		else if (("list" == classname) ||
1803:				("table" == classname) || ("tree" == classname)) {
1804:			paintScroll(component, classname, bounds, pressed, inside, focus, enabled,
1805:				g, clipx, clipy, clipwidth, clipheight);
1806:		}
1807:		else if ("separator" == classname) {
1808:			g.setColor(enabled ? c_border : c_disable);
1809:			g.fillRect(0, 0, bounds.width + evm, bounds.height + evm);
1810:		}
1811:		else if ("menubar" == classname) {
1812:			Object selected = get(component, "selected");
1813:			boolean bottom = ("bottom" == get(component, "placement"));
1814:			int lastx = 0;
1815:			for (Object menu = get(component, ":comp");
1816:					menu != null; menu = get(menu, ":next")) {
1817:				Rectangle mb = getRectangle(menu, "bounds");
1818:				if (clipx + clipwidth <= mb.x) { break; }
1819:				if (clipx >= mb.x + mb.width) { continue; }
1820:				boolean menuenabled = enabled && getBoolean(menu, "enabled", true);
1821:				boolean armed = (selected == menu);
1822:				boolean hoover = (selected == null) && (insidepart == menu);
1823:				paint(menu, mb.x, 0, mb.width, bounds.height,
1824:					g, clipx, clipy, clipwidth, clipheight, // TODO disabled
1825:					bottom || armed, armed, !bottom || armed, armed, 1, 3, 1, 3, false,
1826:					enabled ? (menuenabled ? (armed ? 's' : (hoover ? 'h' : 'g')) : 'r') : 'd', "left", true, false);
1827:				lastx = mb.x + mb.width;
1828:			}
1829:			paintRect(g, lastx, 0, bounds.width - lastx, bounds.height,
1830:				enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg,
1831:				bottom, false, !bottom, false, true);
1832:		}
1833:		else if (":popup" == classname) {
1834:			paintRect(g, 0, 0, bounds.width, bounds.height,
1835:				c_border, c_textbg, true, true, true, true, true);
1836:			Object selected = get(component, "selected");
1837:			for (Object menu = get(get(component, "menu"), ":comp");
1838:					menu != null; menu = get(menu, ":next")) {
1839:				Rectangle r = getRectangle(menu, "bounds");
1840:				if (clipy + clipheight <= r.y) { break; }
1841:				if (clipy >= r.y + r.height) { continue; }
1842:				String itemclass = getClass(menu);
1843:				if (itemclass == "separator") {
1844:					g.setColor(c_border);
1845:					g.fillRect(r.x, r.y, bounds.width - 2 + evm, r.height + evm);
1846:				} else {
1847:					boolean armed = (selected == menu);
1848:					boolean menuenabled = getBoolean(menu, "enabled", true);
1849:					paint(menu, r.x, r.y, bounds.width - 2, r.height,
1850:						g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
1851:						2, (itemclass == "checkboxmenuitem") ? (block + 7) : 4, 2, 4, false,
1852:						menuenabled ? (armed ? 's' : 't') : 'd', "left", true, false);
1853:					if (itemclass == "checkboxmenuitem") {
1854:						boolean checked = getBoolean(menu, "selected", false);
1855:						String group = getString(menu, "group", null);
1856:						g.translate(r.x + 4, r.y + 2);
1857:						g.setColor(menuenabled ? c_border : c_disable);
1858:						if (group == null) {
1859:							g.drawRect(1, 1, block - 3, block - 3);
1860:						} else {
1861:							g.drawOval(1, 1, block - 3, block - 3);
1862:						}
1863:						if (checked) {
1864:							g.setColor(menuenabled ? c_text : c_disable);
1865:							if (group == null) {
1866:								g.fillRect(3, block - 9, 2 + evm, 6 + evm);
1867:								g.drawLine(3, block - 4, block - 4, 3);
1868:								g.drawLine(4, block - 4, block - 4, 4);
1869:							} else {
1870:								g.fillOval(5, 5, block - 10 + evm, block - 10 + evm);
1871:								g.drawOval(4, 4, block - 9, block - 9);
1872:							}
1873:						}
1874:						g.translate(-r.x - 4, -r.y - 2);
1875:					}
1876:					if (itemclass == "menu") {
1877:						paintArrow(g, r.x + bounds.width - block, r.y, block, r.height, 'E');
1878:					}
1879:					else {
1880:						String accelerator = getAccelerator(menu);
1881:						if (accelerator != null) { //TODO
1882:							g.drawString(accelerator, bounds.width - 4 -
1883:								getFontMetrics(font).stringWidth(accelerator), r.y + 2 + 10);
1884:						}
1885:					}
1886:				}
1887:			}
1888:		}
1889:		else if ("bean" == classname) {
1890:				g.clipRect(0, 0, bounds.width, bounds.height);
1891:				((Component) get(component, "bean")).paint(g);
1892:				g.setClip(clipx, clipy, clipwidth, clipheight);
1893:		}
1894:		else throw new IllegalArgumentException((String) classname);
1895:		g.translate(-bounds.x, -bounds.y);
1896:		clipx += bounds.x; clipy += bounds.y;
1897:	}
1898:
1899:	/**
1900:	 *
1901:	 */
1902:	private void paintReverse(Graphics g,
1903:			int clipx, int clipy, int clipwidth, int clipheight,
1904:			Object component, boolean enabled) {
1905:		if (component != null) {
1906:			Rectangle bounds = getRectangle(component, "bounds");
1907:			if ((clipx < bounds.x) ||
1908:					(clipx + clipwidth > bounds.x + bounds.width) ||
1909:					(clipy < bounds.y) ||
1910:					(clipy + clipheight > bounds.y + bounds.height)) {
1911:				paintReverse(g, clipx, clipy, clipwidth, clipheight,
1912:					get(component, ":next"), enabled);
1913:			}
1914:			paint(g, clipx, clipy, clipwidth, clipheight, component, enabled);
1915:		}
1916:	}
1917:
1918:	/**
1919:	 *
1920:	 */
1921:	private void paintField(Graphics g,
1922:			int clipx, int clipy, int clipwidth, int clipheight, Object component,
1923:			int width, int height, boolean inside, boolean pressed,
1924:			boolean focus, boolean enabled, boolean hidden, int left) {
1925:		boolean editable = getBoolean(component, "editable", true);
1926:		paintRect(g, 0, 0, width, height, enabled ? c_border : c_disable,
1927:			editable ? c_textbg : c_bg, true, true, true, true, true);
1928:		g.clipRect(1 + left, 1, width - left - 2, height - 2);
1929:
1930:		String text = getString(component, "text", "");
1931:		int offset = getInteger(component, ":offset", 0);
1932:		Font currentfont = (Font) get(component, "font");
1933:		if (currentfont != null) { g.setFont(currentfont); }
1934:		FontMetrics fm = g.getFontMetrics();
1935:
1936:		int caret = 0;
1937:		if (focus) { 
1938:			int start = getInteger(component, "start", 0); 
1939:			int end = getInteger(component, "end", 0);
1940:			caret = hidden ? (fm.charWidth('*') * end) :
1941:				fm.stringWidth(text.substring(0, end));
1942:			if (start != end) {
1943:				int is = hidden ? (fm.charWidth('*') * start) :
1944:					fm.stringWidth(text.substring(0, start));
1945:				g.setColor(c_select);
1946:				g.fillRect(2 + left - offset + Math.min(is, caret), 1,
1947:					Math.abs(caret - is) + evm, height - 2 + evm);
1948:			}
1949:		}
1950:
1951:		if (focus) {
1952:			g.setColor(c_focus);
1953:			g.fillRect(1 + left - offset + caret, 1, 1 + evm, height - 2 + evm);
1954:		}
1955:
1956:		g.setColor(enabled ? c_text : c_disable);
1957:		int fx = 2 + left - offset;
1958:		int fy = (height + fm.getAscent() - fm.getDescent()) / 2;
1959:		if (hidden) {
1960:			int fh = fm.charWidth('*');
1961:			for (int i = text.length(); i > 0; i--) {
1962:				g.drawString("*", fx, fy);
1963:				fx += fh;
1964:			}
1965:		} else {
1966:			g.drawString(text, fx, fy);
1967:		}
1968:		if (currentfont != null) { g.setFont(font); }
1969:		g.setClip(clipx, clipy, clipwidth, clipheight);
1970:	}
1971:	
1972:	/**
1973:	 * @param component scrollable widget
1974:	 * @param classname
1975:	 * @param bounds
1976:	 * @param pressed
1977:	 * @param inside
1978:	 * @param focus
1979:	 * @param enabled
1980:	 * @param g grahics context
1981:	 * @param clipx current cliping x location relative to the component
1982:	 * @param clipy y location of the cliping area relative to the component
1983:	 * @param clipwidth width of the cliping area
1984:	 * @param clipheight height of the cliping area
1985:	 * @param header column height
1986:	 * @param topborder bordered on the top if true
1987:	 * @param border define left, bottom, and right border if true
1988:	 */
1989:	private void paintScroll(Object component,
1990:			String classname, Rectangle bounds,
1991:			boolean pressed, boolean inside, boolean focus, boolean enabled,
1992:			Graphics g, int clipx, int clipy, int clipwidth, int clipheight) {
1993:		Rectangle port = getRectangle(component, ":port");
1994:		Rectangle horizontal = getRectangle(component, ":horizontal");
1995:		Rectangle vertical = getRectangle(component, ":vertical");
1996:		Rectangle view = getRectangle(component, ":view");
1997:		
1998:		if (horizontal != null) { // paint horizontal scrollbar
1999:			int x = horizontal.x; int y = horizontal.y; int width = horizontal.width; int height = horizontal.height;
2000:			paintArrow(g, x, y, block, height,
2001:				'W', enabled, inside, pressed, "left", true, true, true, false, true);
2002:			paintArrow(g, x + width - block, y, block, height,
2003:				'E', enabled, inside, pressed, "right", true, false, true, true, true);
2004:				
2005:			int track = width - (2 * block);
2006:			if (track < 10) {
2007:				paintRect(g, x + block, y, track, height,
2008:					enabled ? c_border : c_disable, c_bg, true, true, true, true, true);
2009:			}
2010:			else {
2011:				int knob = Math.max(track * port.width / view.width, 10);
2012:				int decrease = view.x * (track - knob) / (view.width - port.width);
2013:				paintRect(g, x + block, y, decrease, height,
2014:					enabled ? c_border : c_disable, c_bg, false, true, true, false, true);
2015:				paintRect(g, x + block + decrease, y, knob, height,
2016:					enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, true, true, true, true);
2017:				int n = Math.min(5, (knob - 4) / 3);
2018:				g.setColor(enabled ? c_border : c_disable);
2019:				int cx = (x + block + decrease) + (knob + 2 - n * 3) / 2;
2020:				for (int i = 0; i < n; i++ ) {
2021:					g.drawLine(cx + i * 3, y + 3, cx + i * 3, y + height - 5);
2022:				}
2023:				int increase = track - decrease - knob;
2024:				paintRect(g, x + block + decrease + knob, y, increase, height,
2025:					enabled ? c_border : c_disable, c_bg, false, false, true, true, true);
2026:			}
2027:		}
2028:			
2029:		if (vertical != null) { // paint vertical scrollbar
2030:			int x = vertical.x; int y = vertical.y; int width = vertical.width; int height = vertical.height;
2031:			paintArrow(g, x, y, width, block,
2032:				'N', enabled, inside, pressed, "up", true, true, false, true, false);
2033:			paintArrow(g, x, y + height - block, width, block,
2034:				'S', enabled, inside, pressed, "down", false, true, true, true, false);
2035:				
2036:			int track = height - (2 * block);
2037:			if (track < 10) {
2038:				paintRect(g, x, y + block, width, track,
2039:					enabled ? c_border : c_disable, c_bg, true, true, true, true, false);
2040:			}
2041:			else {
2042:				int knob = Math.max(track * port.height / view.height, 10);
2043:				int decrease = view.y * (track - knob) / (view.height - port.height);
2044:				paintRect(g, x, y + block, width, decrease,
2045:					enabled ? c_border : c_disable, c_bg, true, false, false, true, false);
2046:				paintRect(g, x, y + block + decrease, width, knob,
2047:					enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, true, true, true, false);
2048:				int n = Math.min(5, (knob - 4) / 3);
2049:				g.setColor(enabled ? c_border : c_disable);
2050:				int cy = (y + block + decrease) + (knob + 2 - n * 3) / 2;
2051:				for (int i = 0; i < n; i++ ) {
2052:					g.drawLine(x + 3, cy + i * 3, x + width - 5, cy + i * 3);
2053:				}
2054:				int increase = track - decrease - knob;
2055:				paintRect(g, x, y + block + decrease + knob, width, increase,
2056:					enabled ? c_border : c_disable, c_bg, false, false, true, true, false);
2057:			}
2058:		}
2059:		
2060:		boolean hneed = (horizontal != null); boolean vneed = (vertical != null);
2061:		if (("panel" != classname) && ("dialog" != classname) &&
2062:				(("textarea" != classname) || getBoolean(component, "border", true))) {
2063:			paintRect(g, port.x - 1, port.y - 1, port.width + (vneed ? 1 : 2), port.height + (hneed ? 1 : 2),
2064:				enabled ? c_border : c_disable, c_textbg, true, true, !hneed, !vneed, true);
2065:			if ("table" == classname) {
2066:				Object header = get(component, "header");
2067:				if (header != null) {
2068:					int[] columnwidths = (int []) get(component, ":widths");
2069:					Object column = get(header, ":comp"); int x = 0;
2070:					g.clipRect(0, 0, port.width + 2, port.y); // not 2 and decrease clip area...
2071:					for (int i = 0; i < columnwidths.length; i++) {
2072:						if (i != 0) { column = get(column, ":next"); }
2073:						boolean lastcolumn = (i == columnwidths.length - 1);
2074:						int width = lastcolumn ? (view.width - x + 2) : columnwidths[i];
2075:						
2076:						paint(column, x - view.x, 0, width, port.y - 1,
2077:							g, clipx, clipy, clipwidth, clipheight,
2078:							true, true, false, lastcolumn, 1, 1, 0, 0, false,
2079:							enabled ? 'g' : 'd', "left", false, false);
2080:						
2081:						Object sort = get(column, "sort"); // "none", "ascent", "descent"
2082:						if (sort != null) {
2083:							paintArrow(g, x - view.x + width - block, 0, block, port.y,
2084:								(sort == "ascent") ? 'S' : 'N');
2085:						}
2086:						x += width;
2087:					}
2088:					g.setClip(clipx, clipy, clipwidth, clipheight);
2089:				}
2090:			}
2091:		}
2092:		int x1 = Math.max(clipx, port.x);
2093:		int x2 = Math.min(clipx + clipwidth, port.x + port.width);
2094:		int y1 = Math.max(clipy, port.y);
2095:		int y2 = Math.min(clipy + clipheight, port.y + port.height);
2096:		if ((x2 > x1) && (y2 > y1)) {
2097:			g.clipRect(x1, y1, x2 - x1, y2 - y1);
2098:			g.translate(port.x - view.x, port.y - view.y);
2099:			
2100:			paint(component, classname, focus, enabled,
2101:				g, view.x - port.x + x1, view.y - port.y + y1, x2 - x1, y2 - y1, port.width, port.height, view.width);
2102:			
2103:			g.translate(view.x - port.x, view.y - port.y);
2104:			g.setClip(clipx, clipy, clipwidth, clipheight);
2105:		}
2106:	}
2107:	
2108:	/**
2109:	 * Paint scrollable content
2110:	 * @param component a panel
2111:	 */
2112:	private void paint(Object component,
2113:			String classname, boolean focus, boolean enabled,
2114:			Graphics g, int clipx, int clipy, int clipwidth, int clipheight,
2115:			int portwidth, int portheight, int viewwidth) {
2116:		if ("textarea" == classname) {
2117:			char[] chars = (char[]) get(component, ":text");
2118:			int start = focus ? getInteger(component, "start", 0) : 0;
2119:			int end = focus ? getInteger(component, "end", 0) : 0;
2120:			int is = Math.min(start, end); int ie = Math.max(start, end);
2121:			Font customfont = (Font) get(component, "font");
2122:			if (customfont != null) { g.setFont(customfont); }
2123:			FontMetrics fm = g.getFontMetrics();
2124:			int fontascent = fm.getAscent(); int fontheight = fm.getHeight();
2125:			int ascent = 1;
2126:			
2127:			for (int i = 0, j = 0; j <= chars.length; j++) {
2128:				if ((j == chars.length) || (chars[j] == '\n')) {
2129:					if (clipy + clipheight <= ascent) { break; } // the next lines are bellow paint rectangle
2130:					if (clipy < ascent + fontheight) { // this line is not above painting area
2131:						if (focus && (is != ie) && (ie >= i) && (is <= j)) {
2132:							int xs = (is < i) ? -1 : ((is > j) ? (viewwidth - 1) :
2133:								fm.charsWidth(chars, i, is - i));
2134:							int xe = ((j != -1) && (ie > j)) ? (viewwidth - 1) :
2135:								fm.charsWidth(chars, i, ie - i);
2136:							g.setColor(c_select);
2137:							g.fillRect(1 + xs, ascent, xe - xs + evm, fontheight + evm);
2138:						}
2139:						g.setColor(enabled ? c_text : c_disable);
2140:						g.drawChars(chars, i, j - i, 1, ascent + fontascent);
2141:						if (focus && (end >= i) && (end <= j)) {
2142:							int caret = fm.charsWidth(chars, i, end - i);
2143:							g.setColor(c_focus);
2144:							g.fillRect(caret, ascent, 1 + evm, fontheight + evm);
2145:						}
2146:					}
2147:					ascent += fontheight;
2148:					i = j + 1;
2149:				}
2150:			}
2151:			if (customfont != null) { g.setFont(font); } //restore the default font
2152:		}
2153:		else if (":combolist" == classname) {
2154:			Object lead = get(component, ":lead");
2155:			for (Object choice = get(get(component, "combobox"), ":comp");
2156:					choice != null; choice = get(choice, ":next")) {
2157:				Rectangle r = getRectangle(choice, "bounds");
2158:				if (clipy + clipheight <= r.y) { break; }
2159:				if (clipy >= r.y + r.height) { continue; }
2160:				paint(choice, r.x, r.y, portwidth, r.height,
2161:					g, clipx, clipy, clipwidth, clipheight,
2162:					false, false, false, false, 2, 4, 2, 4, false,
2163:					getBoolean(choice, "enabled", true) ? ((lead == choice) ? 's' : 't') : 'd',
2164:					"left", false, false);
2165:			}
2166:		}
2167:		else if (("panel" == classname) || ("dialog" == classname)) {
2168:			for (Object comp = get(component, ":comp");
2169:					comp != null; comp = get(comp, ":next")) {
2170:				paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
2171:			}
2172:		}
2173:		else { //if (("list" == classname) || ("table" == classname) || ("tree" == classname))
2174:			Object lead = get(component, ":lead");
2175:			int[] columnwidths = ("table" == classname) ? ((int []) get(component, ":widths")) : null;
2176:			boolean line = getBoolean(component, "line", true); int iline = line ? 1 : 0;
2177:			boolean angle = ("tree" == classname) && getBoolean(component, "angle", false);
2178:			for (Object item = get(component, ":comp"), next = null; item != null; item = next) {
2179:				if (focus && (lead == null)) {
2180:					set(component, ":lead", lead = item); // draw first item focused when lead is null
2181:				}
2182:				Rectangle r = getRectangle(item, "bounds");
2183:				if (clipy + clipheight <= r.y) { break; } // clip rectangle is above
2184:				boolean subnode = false; boolean expanded = false;
2185:				if ("tree" != classname) {
2186:					next = get(item, ":next");
2187:				}
2188:				else {
2189:					subnode = (next = get(item, ":comp")) != null;
2190:					expanded = subnode && getBoolean(item, "expanded", true);
2191:					if (!expanded) {
2192:						for (Object node = item; (node != component) &&
2193:							((next = get(node, ":next")) == null); node = getParent(node));
2194:					}
2195:				}
2196:				if (clipy >= r.y + r.height + iline) {
2197:					if (angle) {
2198:						Object nodebelow = get(item, ":next");
2199:						if (nodebelow != null) { // and the next node is bellow clipy
2200:							g.setColor(c_bg); int x = r.x - block / 2;
2201:							g.drawLine(x, r.y, x, getRectangle(nodebelow, "bounds").y);
2202:						}
2203:					}
2204:					continue; // clip rectangle is bellow
2205:				}
2206:				
2207:				boolean selected = getBoolean(item, "selected", false);
2208:				boolean focused = focus && (lead == item);
2209:				paintRect(g, ("tree" != classname) ? 0 : r.x, r.y,
2210:					("tree" != classname) ? viewwidth : r.width, r.height, c_focus,
2211:					selected ? c_select : c_textbg, focused, focused, focused, focused, true);
2212:				if (line) {
2213:					g.setColor(c_bg);
2214:					g.drawLine(0, r.y + r.height, viewwidth, r.y + r.height);
2215:				}
2216:				if ("table" != classname) { // list or tree
2217:					boolean itemenabled = enabled && getBoolean(item, "enabled", true);
2218:					paint(item, r.x, r.y, viewwidth, r.height,
2219:						g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
2220:						1, 3, 1, 3, false, itemenabled ? 'e' : 'd', "left", false, false);
2221:					if ("tree" == classname) {
2222:						int x = r.x - block / 2; int y = r.y + (r.height - 1) / 2;
2223:						if (angle) {
2224:							g.setColor(c_bg);
2225:							g.drawLine(x, r.y, x, y); g.drawLine(x, y, r.x - 1, y);
2226:							Object nodebelow = get(item, ":next");
2227:							if (nodebelow != null) {
2228:								g.drawLine(x, y, x, getRectangle(nodebelow, "bounds").y);
2229:							}
2230:						}
2231:						if (subnode) {
2232:							paintRect(g, x - 4, y - 4, 9, 9, itemenabled ? c_border : c_disable,
2233:								itemenabled ? c_ctrl : c_bg, true, true, true, true, true);
2234:							g.setColor(itemenabled ? c_text : c_disable);
2235:							g.drawLine(x - 2, y, x + 2, y);
2236:							if (!expanded) { g.drawLine(x, y - 2, x, y + 2); }
2237:						}
2238:					}
2239:				}
2240:				else { // table
2241:					int i = 0; int x = 0;
2242:					for (Object cell = get(item, ":comp"); cell != null; cell = get(cell, ":next")) {
2243:						if (clipx + clipwidth <= x) { break; }
2244:						//column width is defined by header calculated in layout, otherwise is 80
2245:						int iwidth = 80;
2246:						if ((columnwidths != null) && (columnwidths.length > i)) {
2247:							iwidth = (i != columnwidths.length - 1) ?
2248:								columnwidths[i] : Math.max(iwidth, viewwidth - x);
2249:						}
2250:						if (clipx < x + iwidth) {
2251:							boolean cellenabled = enabled && getBoolean(cell, "enabled", true);
2252:							paint(cell, r.x + x, r.y, iwidth, r.height - 1,
2253:								g, clipx, clipy, clipwidth, clipheight, false, false, false, false,
2254:								1, 1, 1, 1, false, cellenabled ? 'e' : 'd', "left", false, false);
2255:						}
2256:						i++; x += iwidth;
2257:					}
2258:				}
2259:			}
2260:		}
2261:	}
2262:
2263:	/**
2264:	 *
2265:	 */
2266:	private void paintRect(Graphics g, int x, int y, int width, int height,
2267:			Color border, Color bg,
2268:			boolean top, boolean left, boolean bottom, boolean right, boolean horizontal) {
2269:		if ((width <= 0) || (height <= 0)) return;
2270:		g.setColor(border);
2271:		if (top) {
2272:			g.drawLine(x + width - 1, y, x, y);
2273:			y++; height--; if (height <= 0) return;
2274:		}
2275:		if (left) {
2276:			g.drawLine(x, y, x, y + height - 1);
2277:			x++; width--; if (width <= 0) return;
2278:		}
2279:		if (bottom) {
2280:			g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
2281:			height--; if (height <= 0) return;
2282:		}
2283:		if (right) {
2284:			g.drawLine(x + width - 1, y + height - 1, x + width - 1, y);
2285:			width--; if (width <= 0) return;
2286:		}
2287:
2288:		if (bg == c_ctrl) {
2289:			fill(g, x, y, width, height, horizontal);
2290:		}
2291:		else {
2292:			g.setColor(bg);
2293:			g.fillRect(x, y, width + evm, height + evm);
2294:		}
2295:	}
2296:	
2297:	/**
2298:	 * Fill the given rectangle with gradient
2299:	 */
2300:	private void fill(Graphics g, int x, int y, int width, int height, boolean horizontal) {
2301:		if (horizontal) {
2302:			if (height > block) {
2303:				g.setColor(c_bg);
2304:				g.fillRect(x, y, width + evm, height - block + evm);
2305:			}
2306:			for (int i = 0; i < width; i += block) {
2307:				g.drawImage(hgradient, x + i, (height > block) ? (y + height - block) : y,
2308:					x + Math.min(i + block, width) + evm, y + height + evm,
2309:					0, 0, Math.min(block, width - i) + evm, Math.min(block, height) + evm, null);
2310:			}
2311:		}
2312:		else {
2313:			if (width > block) {
2314:				g.setColor(c_bg);
2315:				g.fillRect(x, y, width - block + evm, height + evm);
2316:			}
2317:			for (int i = 0; i < height; i += block) {
2318:				g.drawImage(vgradient, (width > block) ? (x + width - block) : x, y + i,
2319:					x + width + evm, y + Math.min(i + block, height) + evm,
2320:					0, 0, Math.min(block, width) + evm, Math.min(block, height - i) + evm, null);
2321:			}
2322:		}
2323:	}
2324:
2325:	/**
2326:	 *
2327:	 */
2328:	private void paintArrow(Graphics g, int x, int y, int width, int height,
2329:			char dir, boolean enabled, boolean inside, boolean pressed, String part,
2330:			boolean top, boolean left, boolean bottom, boolean right, boolean horizontal) {
2331:		inside = inside && (insidepart == part);
2332:		pressed = pressed && (pressedpart == part);
2333:		paintRect(g, x, y, width, height, enabled ? c_border : c_disable,
2334:			enabled ? ((inside != pressed) ? c_hover :
2335:				(pressed ? c_press : c_ctrl)) : c_bg,
2336:			top, left, bottom, right, horizontal);
2337:		g.setColor(enabled ? c_text : c_disable);
2338:		paintArrow(g, x + (left ? 1 : 0), y + (top ? 1 : 0),
2339:			width - (left ? 1 : 0) - (right ? 1 : 0), height - (top ? 1 : 0) - (bottom ? 1 : 0), dir);
2340:	}
2341:
2342:	/**
2343:	 *
2344:	 */
2345:	private void paintArrow(Graphics g,
2346:			int x, int y, int width, int height, char dir) {
2347:		int cx = x + width / 2 - 2;
2348:		int cy = y + height / 2 - 2;
2349:		for (int i = 0; i < 4; i++) {
2350:			if (dir == 'N') { // north
2351:				g.drawLine(cx + 1 - i, cy + i, cx + 1/*2*/ + i, cy + i);
2352:			}
2353:			else if (dir == 'W') { // west
2354:				g.drawLine(cx + i, cy + 1 - i, cx + i, cy + 1/*2*/ + i);
2355:			}
2356:			else if (dir == 'S') { // south
2357:				g.drawLine(cx + 1 - i, cy + 4 - i, cx + 1/*2*/ + i, cy + 4 - i);
2358:			}
2359:			else { // east
2360:				g.drawLine(cx + 4 - i, cy + 1 - i, cx + 4 - i, cy + 1/*2*/ + i);
2361:			}
2362:		}
2363:	}
2364:	
2365:	/**
2366:	 * Paint component's borders and background
2367:	 */
2368:	private void paint(Object component, int x, int y, int width, int height,
2369:			Graphics g, boolean top, boolean left, boolean bottom, boolean right,
2370:			char mode) {
2371:		if ((width <= 0) || (height <= 0)) { return; }
2372:	
2373:		if (top || left || bottom || right) { // draw border
2374:			g.setColor(((mode != 'd') && (mode != 'i')) ? c_border : c_disable);
2375:			if (top) {
2376:				g.drawLine(x + width - 1, y, x, y);
2377:				y++; height--; if (height <= 0) { return; }
2378:			}
2379:			if (left) {
2380:				g.drawLine(x, y, x, y + height - 1);
2381:				x++; width--; if (width <= 0) { return; }
2382:			}
2383:			if (bottom) {
2384:				g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
2385:				height--; if (height <= 0) { return; }
2386:			}
2387:			if (right) {
2388:				g.drawLine(x + width - 1, y + height - 1, x + width - 1, y);
2389:				width--; if (width <= 0) { return; }
2390:			}
2391:		}
2392:	
2393:		Color background = (Color) get(component, "background");
2394:		switch (mode) {
2395:			case 'e': case 'l': case 'd': case 'g': case 'r': break;
2396:			case 'b': case 'i': case 'x': if (background == null) { background = c_bg; } break;
2397:			case 'h': background = (background != null) ? background.brighter() : c_hover; break;
2398:			case 'p': background = (background != null) ? background.darker() : c_press; break;
2399:			case 't': if (background == null) { background = c_textbg; } break;
2400:			case 's': background = c_select; break;
2401:			default: throw new IllegalArgumentException();
2402:		}
2403:		if (((mode == 'g') || (mode == 'r')) && (background == null)) {
2404:			fill(g, x, y, width, height, true);
2405:		}
2406:		else if (background != null) {
2407:			g.setColor(background);
2408:			if (mode != 'x') { g.fillRect(x, y, width + evm, height + evm); } 
2409:		}
2410:	}
2411:	
2412:	/**
2413:	 * Paint component icon and text (using default or custom font)
2414:	 * @param mnemonic find mnemonic index and underline text
2415:	 */
2416:	private void paint(Object component, int x, int y, int width, int height,
2417:			Graphics g, int clipx, int clipy, int clipwidth, int clipheight,
2418:			boolean top, boolean left, boolean bottom, boolean right,
2419:			int toppadding, int leftpadding, int bottompadding, int rightpadding, boolean focus,
2420:			char mode, String alignment, boolean mnemonic, boolean underline) {
2421:		paint(component, x, y, width, height,
2422:			g, top, left, bottom, right, mode);
2423:		if (top) { y++; height--; } if (left) { x++; width--; }
2424:		if (bottom) { height--; } if (right) { width--; }
2425:		if ((width <= 0) || (height <= 0)) { return; }
2426:		
2427:		if (focus) {
2428:			g.setColor(c_focus);
2429:			g.drawRect(x + 1, y + 1, width - 3, height - 3);
2430:		}
2431:
2432:		String text = getString(component, "text", null);
2433:		Image icon = getIcon(component, "icon", null);
2434:		if ((text == null) && (icon == null)) { return; }
2435:	
2436:		x += leftpadding; y += toppadding;
2437:		width -= leftpadding + rightpadding; height -= toppadding + bottompadding;
2438:
2439:		alignment = getString(component, "alignment", alignment);
2440:		Font customfont = (text != null) ? (Font) get(component, "font") : null;
2441:		if (customfont != null) { g.setFont(customfont); }
2442:
2443:		FontMetrics fm = null;
2444:		int tw = 0, th = 0;
2445:		int ta = 0;
2446:		if (text != null) {
2447:			fm = g.getFontMetrics();
2448:			tw = fm.stringWidth(text);
2449:			ta = fm.getAscent();
2450:			th = fm.getDescent() + ta;
2451:		}
2452:		int iw = 0, ih = 0;
2453:		if (icon != null) {
2454:			iw = icon.getWidth(this );
2455:			ih = icon.getHeight(this );
2456:			if (text != null) { iw += 2; }
2457:		}
2458:
2459:		boolean clipped = (tw + iw > width) || (th > height) || (ih > height);
2460:		int cx = x;
2461:		if ("center" == alignment) { cx += (width - tw - iw) / 2; }
2462:			else if ("right" == alignment) { cx += width - tw - iw; }
2463:
2464:		if (clipped) { g.clipRect(x, y, width, height); }
2465:		if (mode == 'x') { g.drawLine(cx, y + height / 2, cx + iw + tw, y + height / 2); }
2466:		if (icon != null) {
2467:			g.drawImage(icon, cx, y + (height - ih) / 2, this );
2468:			cx += iw;
2469:		}
2470:		if (text != null) { 
2471:			Color foreground = (Color) get(component, "foreground");
2472:			if (foreground == null) {
2473:				foreground = (mode == 'l') ? Color.blue :
2474:					(((mode != 'd') && (mode != 'r')) ? c_text : c_disable);
2475:			}
2476:			g.setColor(foreground);
2477:			int ty = y + (height - th) / 2 + ta;
2478:			g.drawString(text, cx, ty);
2479:			if (mnemonic) {
2480:				int imnemonic = getInteger(component, "mnemonic", -1);
2481:				if ((imnemonic != -1) && (imnemonic < text.length())) {
2482:					int mx = cx + fm.stringWidth(text.substring(0, imnemonic));
2483:					g.drawLine(mx, ty + 1, mx + fm.charWidth(text.charAt(imnemonic)), ty + 1);
2484:				}
2485:			}
2486:			if (underline) { // for link button
2487:				g.drawLine(cx, ty + 1, cx + tw, ty + 1);
2488:			}
2489:		}
2490:		if (clipped) { g.setClip(clipx, clipy, clipwidth, clipheight); }
2491:		
2492:		if (customfont != null) { g.setFont(font); } //restore the default font
2493:	}
2494:
2495:	/**
2496:	 * A second thread is used to repeat value change events for scrollbar or spinbox
2497:	 * during the mouse is pressed, or to pop up tooltip
2498:	 */
2499:	public synchronized void run() {
2500:		while (timer == Thread.currentThread()) {
2501:			try {
2502:				if (watch == 0) {
2503:					wait(0);
2504:				} else {
2505:					long current = System.currentTimeMillis();
2506:					if (watch > current) {
2507:						wait(watch - current);
2508:					} else {
2509:						watch = 0;
2510:						if ((watchdelay == 300L) || (watchdelay == 60L)) {
2511:							if (processScroll(mousepressed, pressedpart)) { setTimer(60L); }
2512:						} else if ((watchdelay == 375L) || (watchdelay == 75L)) {
2513:							if (processSpin(mousepressed, pressedpart)) { setTimer(75L); }
2514:						} else if (watchdelay == 750L) {
2515:							showTip();
2516:						}
2517:					}
2518:				}
2519:			} catch (InterruptedException ie) {} //ie.printStackTrace();
2520:		}
2521:	}
2522:
2523:	/**
2524:	 *
2525:	 */
2526:	private void setTimer(long delay) {
2527:		watchdelay = delay;
2528:		if (delay == 0) {
2529:			watch = 0;
2530:		} else {
2531:			long prev = watch;
2532:			watch = System.currentTimeMillis() + delay;
2533:			if (timer == null) {
2534:				timer = new Thread(this );
2535:				timer.setPriority(Thread.MIN_PRIORITY);
2536:				timer.setDaemon(true);
2537:				timer.start();
2538:			}
2539:			if ((prev == 0) || (watch < prev)) {
2540:				synchronized (this ) { notify(); }
2541:				//synchronized (this) { try { notify(); }catch (IllegalMonitorStateException imse) {} }
2542:			}
2543:		}
2544:	}
2545:
2546:	/**
2547:	 * This component can be traversed using Tab or Shift-Tab keyboard focus traversal,
2548:	 * although 1.4 replaced this method by <i>isFocusable</i>,
2549:	 * so 1.4 compilers write deprecation warning
2550:	 *
2551:	 * @return true as focus-transverable component, overwrites the default false value
2552:	 */
2553:	public boolean isFocusTraversable() {
2554:		return true;
2555:	}
2556:
2557:	/**
2558:	 * Dispatches mouse, key, focus, and component events occurring on the
2559:	 * <i>Thinlet</i> component internally
2560:	 */
2561:	protected void processEvent(AWTEvent e) {
2562:		// evm (touchscreen) events: entered/moved/pressed -> dragged -> dragged/released/exited
2563:		int id = e.getID();
2564:		if ((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_MOVED) ||
2565:				(id == MouseEvent.MOUSE_EXITED) || (id == MouseEvent.MOUSE_PRESSED) ||
2566:				(id == MouseEvent.MOUSE_DRAGGED) || (id == MouseEvent.MOUSE_RELEASED)) {
2567:			MouseEvent me = (MouseEvent) e;
2568:			int x = me.getX();
2569:			int y = me.getY();
2570:			int clickcount = me.getClickCount();
2571:			boolean shiftdown = me.isShiftDown();
2572:			boolean controldown = me.isControlDown();
2573:			boolean popuptrigger = me.isPopupTrigger();
2574:			if (id == MouseEvent.MOUSE_ENTERED) {
2575:				if (mousepressed == null) {
2576:					findComponent(content, x, y);
2577:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2578:						MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2579:				}
2580:			}
2581:			else if (id == MouseEvent.MOUSE_MOVED) {
2582:				Object previnside = mouseinside;
2583:				Object prevpart = insidepart;
2584:				findComponent(content, x, y);
2585:				if ((previnside == mouseinside) && (prevpart == insidepart)) {
2586:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2587:						MouseEvent.MOUSE_MOVED, mouseinside, insidepart);
2588:				}
2589:				else {
2590:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2591:						MouseEvent.MOUSE_EXITED, previnside, prevpart);
2592:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2593:						MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2594:				}
2595:			}
2596:			else if (id == MouseEvent.MOUSE_EXITED) {
2597:				if (mousepressed == null) {
2598:					Object mouseexit = mouseinside;
2599:					Object exitpart = insidepart;
2600:					mouseinside = insidepart = null;
2601:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2602:						MouseEvent.MOUSE_EXITED, mouseexit, exitpart);
2603:				}
2604:			}
2605:			else if (id == MouseEvent.MOUSE_PRESSED) {
2606:				if (popupowner != null) { // remove popup
2607:					String classname = getClass(mouseinside);
2608:					if ((popupowner != mouseinside) &&
2609:							(classname != ":popup") && (classname != ":combolist")) {
2610:						closeup();
2611:					}
2612:				}
2613:				hideTip(); // remove tooltip
2614:				mousepressed = mouseinside;
2615:				pressedpart = insidepart;
2616:				handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2617:					MouseEvent.MOUSE_PRESSED, mousepressed, pressedpart);
2618:			}
2619:			else if (id == MouseEvent.MOUSE_DRAGGED) {
2620:				hideTip(); // remove tooltip
2621:				Object previnside = mouseinside;
2622:				Object prevpart = insidepart;
2623:				findComponent(content, x, y);
2624:				boolean same = (previnside == mouseinside) && (prevpart == insidepart);
2625:				boolean isin = (mousepressed == mouseinside) && (pressedpart == insidepart);
2626:				boolean wasin = (mousepressed == previnside) && (pressedpart == prevpart);
2627:				
2628:				if (wasin && !isin) {
2629:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2630:						MouseEvent.MOUSE_EXITED, mousepressed, pressedpart);
2631:				}
2632:				else if (!same && (popupowner != null) && !wasin) {
2633:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2634:						DRAG_EXITED, previnside, prevpart);
2635:				}
2636:				if (isin && !wasin) {
2637:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2638:						MouseEvent.MOUSE_ENTERED, mousepressed, pressedpart);
2639:				}
2640:				else if (!same && (popupowner != null) && !isin) {
2641:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2642:						DRAG_ENTERED, mouseinside, insidepart);
2643:				}
2644:				if (isin == wasin) {
2645:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2646:						MouseEvent.MOUSE_DRAGGED, mousepressed, pressedpart);
2647:				}
2648:			}
2649:			else if (id == MouseEvent.MOUSE_RELEASED) {
2650:				hideTip(); // remove tooltip
2651:				Object mouserelease = mousepressed;
2652:				Object releasepart = pressedpart;
2653:				mousepressed = pressedpart = null;
2654:				handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2655:					MouseEvent.MOUSE_RELEASED, mouserelease, releasepart);
2656:				if ((mouseinside != null) &&
2657:						((mouserelease != mouseinside) || (releasepart != insidepart))) {
2658:					handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
2659:						MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2660:				}
2661:			}
2662:		}
2663:		else if (id == MOUSE_WHEEL) {
2664:			Rectangle port = getRectangle(mouseinside, ":port");
2665:			if (port != null) { // is scrollable
2666:				// TODO hide tooltip?
2667:				Rectangle bounds = getRectangle(mouseinside, "bounds");	
2668:				try { // mouse wheel is supported since 1.4 thus it use reflection
2669:					if (wheelrotation == null) {
2670:						wheelrotation = e.getClass().getMethod("getWheelRotation", null);
2671:					}
2672:					int rotation = ((Integer) wheelrotation.invoke(e, null)).intValue();
2673:					
2674:					if (port.x + port.width < bounds.width) { // has vertical scrollbar
2675:						processScroll(mouseinside, (rotation > 0) ? "down" : "up"); //TODO scroll panels too
2676:					}
2677:					else if (port.y + port.height < bounds.height) { // has horizontal scrollbar
2678:						processScroll(mouseinside, (rotation > 0) ? "right" : "left");
2679:					}
2680:				} catch (Exception exc) { /* never */ }
2681:			}
2682:		}
2683:		else if ((id == KeyEvent.KEY_PRESSED) || (id == KeyEvent.KEY_TYPED)) {
2684:			if (focusinside && ((popupowner != null) || (focusowner != null))) {
2685:				hideTip(); // remove tooltip
2686:				KeyEvent ke = (KeyEvent) e;
2687:				int keychar = ke.getKeyChar();
2688:				boolean control = (keychar <= 0x1f) ||
2689:					((keychar >= 0x7f) && (keychar <= 0x9f)) ||
2690:					(keychar >= 0xffff) || ke.isControlDown();
2691:				int keycode = control ? ke.getKeyCode() : 0;
2692:				
2693:				if ((control == (id == KeyEvent.KEY_PRESSED)) &&
2694:					processKeyPress((popupowner != null) ? popupowner : focusowner,
2695:						ke.isShiftDown(), ke.isControlDown(), ke.getModifiers(),
2696:						control ? 0 : keychar, keycode)) {
2697:					ke.consume();
2698:				}
2699:				else if ((keycode == KeyEvent.VK_TAB) ||
2700:						((keycode == KeyEvent.VK_F6) && (ke.isAltDown() || ke.isControlDown()))) {
2701:					boolean outgo = (keycode == KeyEvent.VK_F6);
2702:					if (!ke.isShiftDown() ? setNextFocusable(focusowner, outgo) :
2703:							setPreviousFocusable(focusowner, outgo)) {
2704:						ke.consume();
2705:					} else if (MOUSE_WHEEL != 0) { // 1.4
2706:						if (!ke.isShiftDown()) {
2707:							transferFocus();
2708:						}
2709:						else { try {
2710:								getClass().getMethod("transferFocusBackward", null). invoke(this , null);
2711:						} catch (Exception exc) { /* never */ } }
2712:					}
2713:					repaint(focusowner);
2714:					closeup();
2715:				}
2716:				else if (keycode == KeyEvent.VK_F8) {
2717:					for (Object splitpane = focusowner;
2718:							splitpane != null; splitpane = getParent(splitpane)) {
2719:						if (getClass(splitpane) == "splitpane") {
2720:							setFocus(splitpane); repaint(splitpane); ke.consume(); break; //middle
2721:						}
2722:					}
2723:				}
2724:				else if ((id == KeyEvent.KEY_PRESSED) && ((keychar != 0) || ke.isActionKey()) &&
2725:						checkMnemonic(focusowner, true, null, ke.getKeyCode(), ke.getModifiers())) {
2726:					ke.consume();
2727:				}
2728:			}
2729:		}
2730:		else if (id == FocusEvent.FOCUS_LOST) {
2731:			focusinside = false;
2732:			if (focusowner != null) { repaint(focusowner); }
2733:			closeup();
2734:		}
2735:		else if (id == FocusEvent.FOCUS_GAINED) {
2736:			focusinside = true;
2737:			if (focusowner == null) { setFocus(content); }
2738:				else { repaint(focusowner); }
2739:		}
2740:		else if ((id == ComponentEvent.COMPONENT_RESIZED) ||
2741:				(id == ComponentEvent.COMPONENT_SHOWN)) {
2742:			Dimension d = getSize();
2743:			setRectangle(content, "bounds", 0, 0, d.width, d.height);
2744:			validate(content);
2745:			closeup();
2746:			if (!focusinside) { requestFocus(); }
2747:		}
2748:	}
2749:	
2750:	/**
2751:	 * Check the previous mouse location again because of a possible layout change
2752:	 */
2753:	private void checkLocation() {
2754:		findComponent(content, mousex, mousey);
2755:		handleMouseEvent(mousex, mousex, 1, false, false, false,
2756:			MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
2757:	}
2758:
2759:	/**
2760:	 *
2761:	 */
2762:	private boolean processKeyPress(Object component,
2763:			boolean shiftdown, boolean controldown, int modifiers, int keychar, int keycode) {
2764:		String classname = getClass(component);
2765:		if ("button" == classname) {
2766:			if (keychar == KeyEvent.VK_SPACE ||
2767:					((keycode == KeyEvent.VK_ENTER) &&
2768:						(get(component, "type") == "default")) ||
2769:					((keycode == KeyEvent.VK_ESCAPE) && //...
2770:						(get(component, "type") == "cancel"))) {
2771:				//pressedkey = keychar;
2772:				invoke(component, null, "action");
2773:				repaint(component);
2774:				return true;
2775:			}
2776:		}
2777:		else if (("checkbox" == classname) || ("togglebutton" == classname)) {
2778:			if (keychar == KeyEvent.VK_SPACE) {
2779:				changeCheck(component, true);
2780:				repaint(component);
2781:				return true;
2782:			}
2783:		}
2784:		else if ("combobox" == classname) {
2785:			Object combolist = get(component, ":combolist");
2786:			if (combolist == null) { // the drop down list is not visible
2787:				boolean editable = getBoolean(component, "editable", true);
2788:				if (editable && processField(component, shiftdown, controldown, modifiers,
2789:							keychar, keycode, false, false, false)) {
2790:					setInteger(component, "selected", -1, -1);
2791:					return true;
2792:				}
2793:				if ((keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_DOWN)) {
2794:					popupCombo(component);
2795:				}
2796:				//+findText
2797:				else return false;
2798:			}
2799:			else {
2800:				if ((keycode == KeyEvent.VK_UP) ||
2801:						(keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
2802:						(keycode == KeyEvent.VK_PAGE_DOWN) ||
2803:						(keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
2804:					Object next = getListItem(component, combolist, keycode,
2805:						get(combolist, ":lead"), false);
2806:					if (next != null) {
2807:						setInside(combolist, next, true);
2808:					}
2809:				}
2810:				else if ((keycode == KeyEvent.VK_ENTER) || (keychar == KeyEvent.VK_SPACE)) {
2811:					closeCombo(component, combolist, get(combolist, ":lead")); //Alt+Up
2812:				}
2813:				else if (keycode == KeyEvent.VK_ESCAPE) {
2814:					closeCombo(component, combolist, null);
2815:				}
2816:				else if (!processField(component, shiftdown, controldown, modifiers,
2817:						keychar, keycode, false, false, false)) {
2818:					Object item = findText((char) keychar, component, combolist, false);
2819:					if (item != null) {
2820:						setInside(combolist, item, true);
2821:					}
2822:					else return false;
2823:				}
2824:			}
2825:			return true;
2826:		}
2827:		else if (("textfield" == classname) || ("passwordfield" == classname)) {
2828:			return processField(component, shiftdown, controldown, modifiers,
2829:				keychar, keycode, false, ("passwordfield" == classname), false);
2830:		}
2831:		else if ("textarea" == classname) {
2832:			char[] chars = (char[]) get(component, ":text");
2833:			int start = getInteger(component, "start", 0);
2834:			int end = getInteger(component, "end", 0);
2835:
2836:			int istart = start;
2837:			int iend = end;
2838:			String insert = null;
2839:			if ((keycode == KeyEvent.VK_HOME) && !controldown) {
2840:				while ((iend > 0) && (chars[iend - 1] != '\n')) { iend--; }
2841:				if (!shiftdown) { istart = iend; }
2842:			}
2843:			else if ((keycode == KeyEvent.VK_END) && !controldown) {
2844:				while ((iend < chars.length) && (chars[iend] != '\n')) { iend++; }
2845:				if (!shiftdown) { istart = iend; }
2846:			}
2847:			else if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP) ||
2848:					(keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) {
2849:				Font currentfont = (Font) get(component, "font");
2850:				FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
2851:				int fh = fm.getHeight();
2852:				int y = 0; int linestart = 0;
2853:				for (int i = 0; i < iend; i++) {
2854:					if ((chars[i] == '\n') || (chars[i] == '\t')) {
2855:						linestart = i + 1; y += fh;
2856:					}
2857:				}
2858:				if (keycode == KeyEvent.VK_UP) { y -= fh; }
2859:				else if (keycode == KeyEvent.VK_DOWN) { y += fh; }
2860:				else {
2861:					int dy = getRectangle(component, ":port").height;
2862:					y += (keycode == KeyEvent.VK_PAGE_UP) ? -dy : dy; // VK_PAGE_DOWN
2863:				}
2864:				int x = fm.charsWidth(chars, linestart, iend - linestart);
2865:				iend = getCaretLocation(component, x, y, true, false);
2866:				if (!shiftdown) { istart = iend; }
2867:			}
2868:			else return processField(component, shiftdown, controldown, modifiers,
2869:					keychar, keycode, true, false, false);
2870:			return changeField(component,
2871:				getString(component, "text", ""), insert, istart, iend, start, end);
2872:		}
2873:		else if ("tabbedpane" == classname) {
2874:			if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN) ||
2875:					(keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2876:				int selected = getInteger(component, "selected", 0);
2877:				boolean increase = (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN);
2878:				int newvalue = selected;
2879:				int n = increase ? getItemCountImpl(component, ":comp") : 0;
2880:				int d = (increase ? 1 : -1);						
2881:				for (int i = selected + d; increase ? (i < n) : (i >= 0); i += d) {
2882:					if (getBoolean(getItem(component, i), "enabled", true)) {
2883:						newvalue = i; break;
2884:					}	
2885:				}
2886:				if (newvalue != selected) {
2887:					setInteger(component, "selected", newvalue, 0);
2888:					checkOffset(component);
2889:					repaint(component);
2890:					invoke(component, getItem(component, newvalue), "action");
2891:				}
2892:			}
2893:		}
2894:		else if ("spinbox" == classname) {
2895:			if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
2896:				processSpin(component, (keycode == KeyEvent.VK_UP)? "up" : "down");
2897:				return true;
2898:			}
2899:			return processField(component, shiftdown, controldown, modifiers,
2900:				keychar, keycode, false, false, true);
2901:		}
2902:		else if ("slider" == classname) {
2903:			int value = getInteger(component, "value", 0);
2904:			int d = 0;
2905:			if ((keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_LEFT) ||
2906:					(keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP)) {
2907:				d = getInteger(component, "minimum", 0) - value;
2908:				if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2909:					d = Math.max(d, -getInteger(component, "unit", 5));
2910:				}
2911:				else if (keycode == KeyEvent.VK_PAGE_UP) {
2912:					d = Math.max(d, -getInteger(component, "block", 25));
2913:				}
2914:			}
2915:			else if ((keycode == KeyEvent.VK_END) || (keycode == KeyEvent.VK_RIGHT) ||
2916:					(keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) {
2917:				d = getInteger(component, "maximum", 100) - value;
2918:				if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
2919:					d = Math.min(d, getInteger(component, "unit", 5));
2920:				}
2921:				else if (keycode == KeyEvent.VK_PAGE_DOWN) {
2922:					d = Math.min(d, getInteger(component, "block", 25));
2923:				}
2924:			}
2925:			if (d != 0) {
2926:				setInteger(component, "value", value + d, 0);
2927:				repaint(component);
2928:				invoke(component, null, "action");
2929:			}
2930:		}
2931:		else if ("splitpane" == classname) {
2932:			int divider = getInteger(component, "divider", -1);
2933:			int d = 0;
2934:			if (keycode == KeyEvent.VK_HOME) {
2935:				d = -divider;
2936:			}
2937:			else if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
2938:				d = Math.max(-10, -divider);
2939:			}
2940:			else if ((keycode == KeyEvent.VK_END) ||
2941:					(keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
2942:				boolean horizontal = ("vertical" != get(component, "orientation"));
2943:				Rectangle bounds = getRectangle(component, "bounds");
2944:				int max = (horizontal ? bounds.width : bounds.height) - 5;				
2945:				d = max - divider;
2946:				if (keycode != KeyEvent.VK_END) {
2947:					d = Math.min(d, 10);
2948:				}
2949:			}
2950:			if (d != 0) {
2951:				setInteger(component, "divider", divider + d, -1);
2952:				validate(component);
2953:			}
2954:		}
2955:		else if (("list" == classname) || ("table" == classname)) {
2956:			return processList(component, shiftdown, controldown, keychar, keycode, modifiers, false);
2957:		}
2958:		else if ("tree" == classname) {
2959:			//? clear childs' selection, select this is its 	subnode was selected
2960:			if (keycode == KeyEvent.VK_LEFT) {
2961:				Object lead = get(component, ":lead");
2962:				if ((get(lead, ":comp") != null) && getBoolean(lead, "expanded", true)) { // collapse
2963:					setBoolean(lead, "expanded", false, true);
2964:					selectItem(component, lead, true);
2965:					validate(component);
2966:					invoke(component, lead, "collapse"); //lead
2967:					return true;
2968:				}
2969:				else { // select parent
2970:					Object parent = getParent(lead);
2971:					if (parent != component) {
2972:						selectItem(component, parent, true);
2973:						setLead(component, lead, parent);
2974:						return true;
2975:					}
2976:				}
2977:			}
2978:			//? for interval mode select its all subnode or deselect all after
2979:			else if (keycode == KeyEvent.VK_RIGHT) {
2980:				Object lead = get(component, ":lead");
2981:				Object node = get(lead, ":comp");
2982:				if (node != null) {
2983:					if (getBoolean(lead, "expanded", true)) { // select its first subnode
2984:						selectItem(component, node, true);
2985:						setLead(component, lead, node);
2986:					}
2987:					else { // expand
2988:						setBoolean(lead, "expanded", true, true);
2989:						selectItem(component, lead, true);
2990:						validate(component);
2991:						invoke(component, lead, "expand"); //lead
2992:					}
2993:					return true;
2994:				}
2995:			}
2996:			return processList(component, shiftdown, controldown, keychar, keycode, modifiers, true);
2997:		}
2998:		else if (("menubar" == classname) || ("popupmenu" == classname)) {
2999:			// find the last open :popup and the previous one
3000:			Object previous = null; Object last = null;
3001:			for (Object i = get(component, ":popup");
3002:					i != null; i = get(i, ":popup")) {
3003:				previous = last; last = i;
3004:			}
3005:			//selected is the current item of the last, or the previous :popup, or null
3006:			Object selected = get(last, "selected");
3007:			Object hotpopup = ((selected != null) || (previous == null)) ?
3008:				last : previous;
3009:			if ((selected == null) && (previous != null)) {
3010:				selected = get(previous, "selected");
3011:			}
3012:
3013:			if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
3014:				Object next = getMenu(hotpopup,
3015:					selected, keycode == KeyEvent.VK_DOWN, true);
3016:				if (next != null) {
3017:					set(hotpopup, "selected", null);
3018:					popupMenu(hotpopup);
3019:					set(hotpopup, "selected", next);
3020:					repaint(hotpopup);
3021:				}
3022:			}
3023:			else if (keycode == KeyEvent.VK_LEFT) {
3024:				if (previous != null) { // close the last :popup
3025:					selected = get(previous, "selected");
3026:					set(previous, "selected", null);
3027:					popupMenu(previous);
3028:					set(previous, "selected", selected);
3029:					repaint(previous); // , selected
3030:				}
3031:				else if ("menubar" == classname) { // select the previous menubar menu
3032:					Object next = getMenu(component, get(component, "selected"), false, false);
3033:					if (next != null) {
3034:						set(component, "selected", next);
3035:						Object popup = popupMenu(component);
3036:						set(popup, "selected", getMenu(popup, null, true, true));
3037:						repaint(component); // , selected
3038:					}
3039:				}
3040:			}
3041:			else if (keycode == KeyEvent.VK_RIGHT) {
3042:				if ((previous != null) && (selected == null)) { // ?
3043:					set(last, "selected", get(get(last, "menu"), ":comp"));
3044:					repaint(last); // , selected
3045:				}
3046:				else if ((selected != null) && (getClass(selected) == "menu")) { // expand menu
3047:					Object popup = popupMenu(last);
3048:					set(popup, "selected", getMenu(popup, null, true, true));
3049:				}
3050:				else if ("menubar" == classname) { // select the next menubar menu
3051:					Object next = getMenu(component, get(component, "selected"), true, false);
3052:					if (next != null) {
3053:						set(component, "selected", next);
3054:						Object popup = popupMenu(component);
3055:						set(popup, "selected", getMenu(popup, null, true, true));
3056:						repaint(component); // , selected
3057:					}
3058:				}
3059:			}
3060:			else if ((keycode == KeyEvent.VK_ENTER) ||
3061:					(keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_ESCAPE)) {
3062:				if ((keycode != KeyEvent.VK_ESCAPE) &&
3063:						getBoolean(selected, "enabled", true)) {
3064:					if ((selected != null) && (getClass(selected) == "checkboxmenuitem")) {
3065:						changeCheck(selected, false);
3066:					}
3067:					else invoke(selected, null, "action");
3068:				}
3069:				closeup();
3070:			}
3071:			else return false;
3072:			return true;
3073:		}
3074:		return false;
3075:	}
3076:
3077:	/**
3078:	 *
3079:	 */
3080:	private boolean changeCheck(Object component, boolean box) {
3081:		String group = getString(component, "group", null);
3082:		if (group != null) {
3083:			if (getBoolean(component, "selected", false)) { return false; }
3084:			for (Object comp = get(getParent(component), ":comp");
3085:					comp != null; comp = get(comp, ":next")) {
3086:				if (comp == component) {
3087:					setBoolean(component, "selected", true);
3088:				}
3089:				else if (group.equals(get(comp, "group")) &&
3090:						getBoolean(comp, "selected", false)) {
3091:					setBoolean(comp, "selected", false);
3092:					if (box) { repaint(comp); } //checkbox only
3093:				}
3094:			}
3095:		}
3096:		else {
3097:			setBoolean(component, "selected",
3098:				!getBoolean(component, "selected", false), false);
3099:		}
3100:		invoke(component, null, "action");
3101:		return true;
3102:	}
3103:
3104:	/**
3105:	 * @param component a :popup or a menubar
3106:	 * @param part the currently selected item, return the first/last if null
3107:	 * @param forward find the next item if true, the previous otherwise
3108:	 * @param popup the given component is :popup if true, menubar otherwise
3109:	 * @return the next/previous item relative to the current one excluding separators, or null
3110:	 */
3111:	private Object getMenu(Object component, Object part,
3112:			boolean forward, boolean popup) {
3113:		Object previous = null;
3114:		for (int i = 0; i < 2; i++) { // 0: next to last, 1: first to previous
3115:			for (Object item = (i == 0) ? get(part, ":next") :
3116:						get(popup ? get(component, "menu") : component, ":comp");
3117:					(i == 0) ? (item != null) : (item != part); item = get(item, ":next")) {
3118:				if ((getClass(item) != "separator") && getBoolean(item, "enabled", true)) {
3119:					if (forward) { return item; }
3120:					previous = item;
3121:				}
3122:			}
3123:		}
3124:		return previous;
3125:	}
3126:
3127:	/**
3128:	 * Process keyboard events for textfield, passwordfield, textarea,
3129:	 * combobox, and spinbox
3130:	 * @param multiline true for textarea, otherwise false
3131:	 * @param hidden true for passwordfield, otherwise false
3132:	 * @param filter true for spinbox, otherwise false
3133:	 */
3134:	private boolean processField(Object component,
3135:			boolean shiftdown, boolean controldown, int modifiers,
3136:			int keychar, int keycode,
3137:			boolean multiline, boolean hidden, boolean filter) {
3138:		String text = getString(component, "text", ""); 
3139:		int start = getInteger(component, "start", 0);
3140:		int end = getInteger(component, "end", 0);
3141:		boolean editable = getBoolean(component, "editable", true);
3142:
3143:		int istart = start;
3144:		int iend = end;
3145:		String insert = null;
3146:		if (editable && (keychar != 0) &&
3147:			//((modifiers == 0) || (modifiers == InputEvent.SHIFT_MASK))) {
3148:			(modifiers != InputEvent.ALT_MASK)) {
3149:			insert = String.valueOf((char) keychar);
3150:		}
3151:		else if (editable && (keycode == KeyEvent.VK_ENTER)) {
3152:			if (multiline) { insert = "\n"; }
3153:				else { return invoke(component, null, "perform"); }
3154:		}
3155:		else if (editable && (keycode == KeyEvent.VK_BACK_SPACE)) {
3156:			insert = "";
3157:			if (start == end) { istart -= 1; }
3158:		}
3159:		else if (keycode == KeyEvent.VK_END) {
3160:			iend = text.length();
3161:			if (!shiftdown) { istart = iend; }
3162:		}
3163:		else if (keycode == KeyEvent.VK_HOME) {
3164:			iend = 0;
3165:			if (!shiftdown) { istart = iend; }
3166:		}
3167:		else if (keycode == KeyEvent.VK_LEFT) {
3168:			if (controldown) {
3169:				for (int i = 0; i < 2; i++) {
3170:					while ((iend > 0) && ((i != 0) ==
3171:						Character.isLetterOrDigit(text.charAt(iend - 1)))) { iend--; }	
3172:				}
3173:			} else {
3174:				iend -= 1;
3175:			}
3176:			if (!shiftdown) { istart = iend; }
3177:		}
3178:		else if (keycode == KeyEvent.VK_RIGHT) {
3179:			if (controldown) {
3180:				for (int i = 0; i < 2; i++) {
3181:					while ((iend < text.length()) && ((i == 0) ==
3182:						Character.isLetterOrDigit(text.charAt(iend)))) { iend++; }
3183:				}
3184:			} else {
3185:				iend += 1;
3186:			}
3187:			if (!shiftdown) { istart = iend; }
3188:		}
3189:		else if (editable && (keycode == KeyEvent.VK_DELETE)) {
3190:			insert = "";
3191:			if (start == end) { iend += 1; }
3192:		}
3193:		else if (controldown &&
3194:				((keycode == KeyEvent.VK_A) || (keycode == 0xBF))) {
3195:			istart = 0; // KeyEvent.VK_SLASH
3196:			iend = text.length();
3197:		}
3198:		else if (controldown && (keycode == 0xDC)) {
3199:			istart = iend = text.length(); // KeyEvent.VK_BACK_SLASH
3200:		}
3201:		else if ((editable && !hidden && controldown && (keycode == KeyEvent.VK_X)) ||
3202:				(!hidden && controldown && (keycode == KeyEvent.VK_C))) {
3203:			if (start != end) {
3204:				clipboard = text.substring(
3205:					Math.min(start, end), Math.max(start, end));
3206:				try {				
3207:					getToolkit().getSystemClipboard().setContents(
3208:						new StringSelection(clipboard), null);
3209:				} catch (Exception exc) {}
3210:				if (keycode == KeyEvent.VK_X) { insert = ""; } else { return true; }
3211:			}
3212:		}
3213:		else if (editable && controldown && (keycode == KeyEvent.VK_V)) {
3214:			try {
3215:				insert = (String) getToolkit().getSystemClipboard().
3216:					getContents(this ).getTransferData(DataFlavor.stringFlavor);
3217:			} catch (Exception exc) {
3218:				insert = clipboard;
3219:			}
3220:			if (insert != null) { // no text on system clipboard nor internal clipboard text
3221:				insert = filter(insert, multiline);
3222:			}
3223:		}
3224:		if (filter) {
3225:			for (int i = insert.length() - 1; i >= 0; i--) {
3226:				if (!Character.isDigit(insert.charAt(i))) { return false; }
3227:			}
3228:		}
3229:		return changeField(component, text, insert, istart, iend, start, end);
3230:	}
3231:	
3232:	/**
3233:	 * @param text
3234:	 * @param multiline
3235:	 * @return
3236:	 */
3237:	private static String filter(String text, boolean multiline) {
3238:		StringBuffer filtered = new StringBuffer(text.length());
3239:		for (int i = 0; i < text.length(); i++) {
3240:			char ckey = text.charAt(i);
3241:			if (((ckey > 0x1f) && (ckey < 0x7f)) ||
3242:					((ckey > 0x9f) && (ckey < 0xffff)) ||
3243:					(multiline && (ckey == '\n'))) {
3244:				filtered.append(ckey);
3245:			}
3246:		}
3247:		return (filtered.length() != text.length()) ? filtered.toString() : text;
3248:	}
3249:
3250:	/**
3251:	 * @param component a textfield, passwordfield, textarea, combobox, or spinbox
3252:	 * @param text current text
3253:	 * @param insert a string to replace thr current selection 
3254:	 * @param movestart new selection start position
3255:	 * @param moveend new caret (selection end) position
3256:	 * @param start current selection start position
3257:	 * @param end current caret position
3258:	 * @return true if selection, caret location, or text content changed
3259:	 */
3260:	private boolean changeField(Object component, String text, String insert,
3261:			int movestart, int moveend, int start, int end) {
3262:		movestart = Math.max(0, Math.min(movestart, text.length()));
3263:		moveend = Math.max(0, Math.min(moveend, text.length()));
3264:		if ((insert == null) && (start == movestart) && (end == moveend)) {
3265:			return false;
3266:		}
3267:		if (insert != null) {
3268:			int min = Math.min(movestart, moveend);
3269:			set(component, "text", text.substring(0, min) + insert +
3270:				text.substring(Math.max(movestart, moveend)));
3271:			movestart = moveend = min + insert.length();
3272:			invoke(component, null, "action"); // deprecated
3273:		}
3274:		if (start != movestart) { setInteger(component, "start", movestart, 0); }
3275:		if (end != moveend) { setInteger(component, "end", moveend, 0); }
3276:		validate(component);
3277:		invoke(component, null, (insert != null) ?
3278:			((insert.length() > 0) ? "insert" : "remove") : "caret");
3279:		return true;
3280:	}
3281:
3282:	/**
3283:	 *
3284:	 */
3285:	private boolean processList(Object component, boolean shiftdown, boolean controldown,
3286:			int keychar, int keycode, int modifiers, boolean recursive) {
3287:		if ((keycode == KeyEvent.VK_UP) || // select previous/next/first/... item
3288:				(keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
3289:				(keycode == KeyEvent.VK_PAGE_DOWN) ||
3290:				(keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
3291:			Object lead = get(component, ":lead");
3292:			Object row = getListItem(component, component, keycode, lead, recursive);
3293:			if (row != null) {
3294:				String selection = getString(component, "selection", "single");
3295:				if (shiftdown && (selection != "single") && (lead != null)) {
3296:					extend(component, lead, row, recursive);
3297:				}
3298:				else if (!controldown) {
3299:					selectItem(component, row, recursive);
3300:				}
3301:				setLead(component, lead, row);
3302:				return true;
3303:			}
3304:		}
3305:		else if (keycode == KeyEvent.VK_LEFT) {
3306:			return processScroll(component, "left");
3307:		}
3308:		else if (keycode == KeyEvent.VK_RIGHT) {
3309:			return processScroll(component, "right");
3310:		}
3311:		else if (keychar == KeyEvent.VK_SPACE) { // select the current item
3312:			select(component, get(component, ":lead"), recursive, shiftdown, controldown); //...
3313:			return true;
3314:		}
3315:		else if (controldown) {
3316:			if (((keycode == KeyEvent.VK_A) || (keycode == 0xBF)) && //KeyEvent.VK_SLASH
3317:					(getString(component, "selection", "single") != "single")) { // select all
3318:				selectAll(component, true, recursive);
3319:				return true;
3320:			}
3321:			else if (keycode == 0xDC) { //KeyEvent.VK_BACK_SLASH // deselect all
3322:				selectAll(component, false, recursive);
3323:				return true;
3324:			}
3325:		}
3326:		else {
3327:			Object item = findText((char) keychar, component, component, recursive);
3328:			if (item != null) {
3329:				select(component, item, recursive, false, false);
3330:				return true;
3331:			}
3332:		}
3333:		return false;
3334:	}
3335:	
3336:	/**
3337:	 * Search for the next/first appropriate item starting with the collected string
3338:	 * or the given single character
3339:	 * @param keychar the last typed character
3340:	 * @param component a list, tree, table, or combobox
3341:	 * @param leadowner the list, tree, table, or the combobox's drop down list
3342:	 * @param recursive if the component is a tree
3343:	 * @return the appropriate item or null
3344:	 */
3345:	private Object findText(char keychar, Object component,
3346:			Object leadowner, boolean recursive) {
3347:		if (keychar != 0) {
3348:			long current = System.currentTimeMillis();
3349:			int i = (current > findtime + 1000) ? 1 : 0; // clear the starting string after a second
3350:			findtime = current;
3351:			Object lead = get(leadowner, ":lead");
3352:			for (; i < 2; i++) { // 0: find the long text, 1: the stating character only
3353:				findprefix = (i == 0) ? (findprefix + keychar) : String.valueOf(keychar);
3354:				for (int j = 0; j < 2; j++) { // 0: lead to last, 1: first to lead
3355:					for (Object item = (j == 0) ? ((i == 0) ? lead : getNextItem(component, lead, recursive)) :
3356:							get(component, ":comp"); (j == 0) ? (item != null) : (item != lead);
3357:							item = getNextItem(component, item, recursive)) {
3358:						if (getString(item, "text", "").regionMatches(true,
3359:								0, findprefix, 0, findprefix.length())) { //table first column...
3360:							return item;
3361:						}
3362:					}
3363:				}
3364:			}
3365:		}
3366:		return null;
3367:	}
3368:
3369:	/**
3370:	 *
3371:	 */
3372:	private Object getListItem(Object component, Object scrollpane,
3373:			int keycode, Object lead, boolean recursive) {
3374:		Object row = null;
3375:		if (keycode == KeyEvent.VK_UP) {
3376:			for (Object prev = get(component, ":comp"); prev != lead;
3377:					prev = getNextItem(component, prev, recursive)) {
3378:				row = prev; // component -> getParent(lead)
3379:			}
3380:		}
3381:		else if (keycode == KeyEvent.VK_DOWN) {
3382:			row = (lead == null) ? get(component, ":comp") :
3383:				getNextItem(component, lead, recursive);
3384:		}
3385:		else if ((keycode == KeyEvent.VK_PAGE_UP) ||
3386:				(keycode == KeyEvent.VK_PAGE_DOWN)) {
3387:			Rectangle view = getRectangle(scrollpane, ":view");
3388:			Rectangle port = getRectangle(scrollpane, ":port");
3389:			Rectangle rl = (lead != null) ? getRectangle(lead, "bounds") : null;
3390:			int vy = (keycode == KeyEvent.VK_PAGE_UP) ?
3391:				view.y : (view.y + port.height);
3392:			if ((keycode == KeyEvent.VK_PAGE_UP) &&
3393:					(rl != null) && (rl.y <= view.y)) {
3394:				vy -= port.height;
3395:			}
3396:			if ((keycode == KeyEvent.VK_PAGE_DOWN) &&
3397:					(rl != null) && (rl.y + rl.height >= view.y + port.height)) {
3398:				vy += port.height;
3399:			}
3400:			for (Object item = get(component, ":comp"); item != null;
3401:					item = getNextItem(component, item, recursive)) {
3402:				Rectangle r = getRectangle(item, "bounds");
3403:				if (keycode == KeyEvent.VK_PAGE_UP) {
3404:					row = item;
3405:					if (r.y + r.height > vy) { break; }
3406:				} else {
3407:					if (r.y > vy) { break; }
3408:					row = item;
3409:				}
3410:			}
3411:		}
3412:		else if (keycode == KeyEvent.VK_HOME) {
3413:			row = get(component, ":comp");
3414:		}
3415:		else if (keycode == KeyEvent.VK_END) {
3416:			for (Object last = lead; last != null;
3417:					last = getNextItem(component, last, recursive)) {
3418:				row = last;
3419:			}
3420:		}
3421:		return row;
3422:	}
3423:
3424:	/**
3425:	 * Select all the items
3426:	 * @param component a list/tree/table
3427:	 * @param selected selects or deselects items
3428:	 * @param recursive true for tree
3429:	 */
3430:	private void selectAll(Object component,
3431:			boolean selected, boolean recursive) {
3432:		boolean changed = false;
3433:		for (Object item = get(component, ":comp");
3434:				item != null; item = getNextItem(component, item, recursive)) {
3435:			if (setBoolean(item, "selected", selected, false)) {
3436:				repaint(component, null, item); changed = true;
3437:			}
3438:		}
3439:		set(component, ":anchor", null);
3440:		if (changed) { invoke(component, null, "action"); }
3441:	}
3442:
3443:	/**
3444:	 * Select a single given item, deselect others
3445:	 * @param component a list/tree/table
3446:	 * @param row the item/node/row to select
3447:	 * @param recursive true for tree
3448:	 */
3449:	private void selectItem(Object component, Object row, boolean recursive) {
3450:		boolean changed = false;
3451:		for (Object item = get(component, ":comp");
3452:				item != null; item = getNextItem(component, item, recursive)) {
3453:			if (setBoolean(item, "selected", (item == row), false)) {
3454:				repaint(component, null, item); changed = true;
3455:			}
3456:		}
3457:		set(component, ":anchor", null);
3458:		if (changed) { invoke(component, row, "action"); }
3459:	}
3460:
3461:	/**
3462:	 *
3463:	 */
3464:	private void extend(Object component, Object lead,
3465:			Object row, boolean recursive) {
3466:		Object anchor = get(component, ":anchor");
3467:		if (anchor == null) { set(component, ":anchor", anchor = lead); }
3468:		char select = 'n'; boolean changed = false;
3469:		for (Object item = get(component, ":comp"); // anchor - row
3470:				item != null; item = getNextItem(component, item, recursive)) {
3471:			if (item == anchor) select = (select == 'n') ? 'y' : 'r';
3472:			if (item == row) select = (select == 'n') ? 'y' : 'r';
3473:			if (setBoolean(item, "selected", (select != 'n'), false)) {
3474:				repaint(component, null, item); changed = true;
3475:			}
3476:			if (select == 'r') select = 'n';
3477:		}
3478:		if (changed) { invoke(component, row, "action"); }
3479:	}
3480:
3481:	/**
3482:	 * Update the lead item of a list/tree/table, repaint, and scroll
3483:	 * @param component a list, tree, or table
3484:	 * @param oldlead the current lead item
3485:	 * @param lead the new lead item
3486:	 */
3487:	private void setLead(Object component, Object oldlead, Object lead) {
3488:		if (oldlead != lead) { //?
3489:			if (oldlead != null) { repaint(component, null, oldlead); }
3490:			set(component, ":lead", lead);
3491:			repaint(component, null, lead);
3492:			
3493:			Rectangle r = getRectangle(lead, "bounds");
3494:			scrollToVisible(component, r.x, r.y, 0, r.height);
3495:		}
3496:	}
3497:
3498:	/**
3499:	 * Update the lead item of a combolist, repaint, and scroll
3500:	 * @param component a combobox drop down list
3501:	 * @param part the current hotspot item
3502:	 * @param scroll scroll to the part if true
3503:	 */
3504:	private void setInside(Object component, Object part, boolean scroll) {
3505:			Object previous = get(component, ":lead");
3506:			if (previous != null) {
3507:				repaint(component, ":combolist", previous);
3508:			}
3509:			set(component, ":lead", part);
3510:			if (part != null) {
3511:				repaint(component, ":combolist", part);
3512:				if (scroll) {
3513:					Rectangle r = getRectangle(part, "bounds");
3514:					scrollToVisible(component, r.x, r.y, 0, r.height);
3515:				}
3516:			}
3517:	}
3518:			
3519:	/**
3520:	 * @param x mouse x position relative to thinlet component
3521:	 * @param y mouse y position relative to the main desktop
3522:	 */
3523:	private void handleMouseEvent(int x, int y, int clickcount,
3524:			boolean shiftdown, boolean controldown, boolean popuptrigger,
3525:			int id, Object component, Object part) {
3526:		if (id == MouseEvent.MOUSE_ENTERED) {
3527:			setTimer(750L);
3528:		}
3529:		else if (id == MouseEvent.MOUSE_EXITED) {
3530:			hideTip();
3531:		}
3532:		if (!getBoolean(component, "enabled", true)) { return; }
3533:		String classname = getClass(component);
3534:		if (("button" == classname) ||
3535:				("checkbox" == classname) || ("togglebutton" == classname)) {
3536:			if ((id == MouseEvent.MOUSE_ENTERED) ||
3537:					(id == MouseEvent.MOUSE_EXITED) ||
3538:					(id == MouseEvent.MOUSE_PRESSED) ||
3539:					(id == MouseEvent.MOUSE_RELEASED)) {
3540:				if (id == MouseEvent.MOUSE_PRESSED) {
3541:					setFocus(component);
3542:				}
3543:				if (("button" == classname) &&
3544:						((mousepressed == null) || (mousepressed == component)) &&
3545:						((id == MouseEvent.MOUSE_ENTERED) ||
3546:							(id == MouseEvent.MOUSE_EXITED)) &&
3547:						(get(component, "type") == "link")) {
3548:					setCursor(Cursor.getPredefinedCursor(
3549:						(id == MouseEvent.MOUSE_ENTERED) ?
3550:							Cursor.HAND_CURSOR : Cursor.DEFAULT_CURSOR));
3551:				}
3552:				else if ((id == MouseEvent.MOUSE_RELEASED) &&
3553:						(mouseinside == component)) {
3554:					if ("button" != classname) {
3555:						changeCheck(component, true);
3556:					}
3557:					else invoke(component, null, "action");
3558:				}
3559:				repaint(component);
3560:			}
3561:		}
3562:		else if ("combobox" == classname) {
3563:			boolean editable = getBoolean(component, "editable", true);
3564:			if (editable && (part == null)) { // textfield area
3565:				Image icon = null;
3566:				int left = ((id == MouseEvent.MOUSE_PRESSED) &&
3567:					((icon = getIcon(component, "icon", null)) != null)) ?
3568:						icon.getWidth(this ) : 0;
3569:				processField(x, y, clickcount, id, component, part, false, false, left);
3570:			}
3571:			else if (part != "icon") { // part = "down"
3572:				if (((id == MouseEvent.MOUSE_ENTERED) ||
3573:						(id == MouseEvent.MOUSE_EXITED)) && (mousepressed == null)) {
3574:					if (editable) { repaint(component, "combobox", part); } // hover the arrow button
3575:						else { repaint(component); } // hover the whole combobox
3576:				}
3577:				else if (id == MouseEvent.MOUSE_PRESSED) {
3578:					Object combolist = get(component, ":combolist");
3579:					if (combolist == null) { // combolist is closed
3580:						setFocus(component);
3581:						repaint(component);
3582:						popupCombo(component);
3583:					} else { // combolist is visible
3584:						closeCombo(component, combolist, null);
3585:					}
3586:				}
3587:				else if (id == MouseEvent.MOUSE_RELEASED) {
3588:					if (mouseinside != component) {
3589:						Object combolist = get(component, ":combolist");
3590:						closeCombo(component, combolist,
3591:							((mouseinside == combolist) && (insidepart instanceof  Object[])) ? insidepart : null);
3592:					} else {
3593:						repaint(component);
3594:					}
3595:				}
3596:			}
3597:		}
3598:		else if (":combolist" == classname) {
3599:			if (!processScroll(x, y, id, component, part)) {
3600:				if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) {
3601:					if (part != null) { //+ scroll if dragged
3602:						setInside(component, part, false);
3603:					}
3604:				}
3605:				else if (id == MouseEvent.MOUSE_RELEASED) {
3606:					closeCombo(get(component, "combobox"), component, part);
3607:				}
3608:			}
3609:		}
3610:		else if (("textfield" == classname) || ("passwordfield" == classname)) {
3611:			processField(x, y, clickcount, id, component, part,
3612:				false, ("passwordfield" == classname), 0);
3613:		}
3614:		else if ("textarea" == classname) {
3615:			if (!processScroll(x, y, id, component, part)) {
3616:				processField(x, y, clickcount, id, component, part, true, false, 0);
3617:			}
3618:		}
3619:		else if ("panel" == classname) {
3620:			processScroll(x, y, id, component, part);
3621:		}
3622:		else if ("desktop" == classname) {
3623:			if (part == "modal") {
3624:				if (id == MouseEvent.MOUSE_ENTERED) {
3625:					setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
3626:				}
3627:				else if (id == MouseEvent.MOUSE_EXITED) {
3628:					setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3629:				}
3630:			}
3631:		}
3632:		else if ("spinbox" == classname) {
3633:			if (part == null) {
3634:				processField(x, y, clickcount, id, component, part, false, false, 0);
3635:			}
3636:			else { // part = "up" || "down"
3637:				if ((id == MouseEvent.MOUSE_ENTERED) ||
3638:						(id == MouseEvent.MOUSE_EXITED) ||
3639:						(id == MouseEvent.MOUSE_PRESSED) ||
3640:						(id == MouseEvent.MOUSE_RELEASED)) {
3641:					if (id == MouseEvent.MOUSE_PRESSED) {
3642:						setFocus(component);
3643:						if (processSpin(component, part)) { setTimer(375L); }
3644:						//settext: start end selection, parse exception...
3645:					}
3646:					else {
3647:						if (id == MouseEvent.MOUSE_RELEASED) {
3648:							setTimer(0L);
3649:						}
3650:					}
3651:					repaint(component, classname, part);
3652:				}
3653:			}
3654:		}
3655:		else if ("tabbedpane" == classname) {
3656:			if ((id == MouseEvent.MOUSE_ENTERED) ||
3657:					(id == MouseEvent.MOUSE_EXITED)) {
3658:				if ((part != null) && getBoolean(part, "enabled", true) &&
3659:						(getInteger(component, "selected", 0) != getIndex(component, part))) {
3660:					repaint(component, "tabbedpane", part);
3661:				}
3662:			}
3663:			else if ((part != null) && (id == MouseEvent.MOUSE_PRESSED) &&
3664:					getBoolean(part, "enabled", true)) {
3665:				int selected = getInteger(component, "selected", 0);
3666:				int current = getIndex(component, part);
3667:				if (selected == current) {
3668:					setFocus(component);
3669:					repaint(component, "tabbedpane", part);
3670:				}
3671:				else {
3672:					setInteger(component, "selected", current, 0);
3673:					//Object tabcontent = getItem(component, current);
3674:					//setFocus((tabcontent != null) ? tabcontent : component);
3675:					setNextFocusable(component, false);
3676:					checkOffset(component);
3677:					repaint(component);
3678:					invoke(component, part, "action");
3679:				}
3680:			}
3681:		}
3682:		else if ("slider" == classname) {
3683:			if ((id == MouseEvent.MOUSE_PRESSED) ||
3684:					(id == MouseEvent.MOUSE_DRAGGED)) {
3685:				if (id == MouseEvent.MOUSE_PRESSED) {
3686:					setReference(component, block / 2, block / 2);
3687:					setFocus(component);
3688:				}
3689:				int minimum = getInteger(component, "minimum", 0);
3690:				int maximum = getInteger(component, "maximum", 100);
3691:				int value = getInteger(component, "value", 50);
3692:				Rectangle bounds = getRectangle(component, "bounds");
3693:				boolean horizontal = ("vertical" != get(component, "orientation"));
3694:				int newvalue = minimum +
3695:					(horizontal ? (x - referencex) : (y - referencey)) *
3696:					(maximum - minimum) /
3697:					((horizontal ? bounds.width : bounds.height) - block); //... +0.5
3698:				newvalue = Math.max(minimum, Math.min(newvalue, maximum));
3699:				if (value != newvalue) {
3700:					setInteger(component, "value", newvalue, 50);
3701:					invoke(component, null, "action");
3702:				}
3703:				if ((value != newvalue) || (id == MouseEvent.MOUSE_PRESSED)) {
3704:					repaint(component);
3705:				}
3706:			}
3707:		}
3708:		else if ("splitpane" == classname) {
3709:			if (id == MouseEvent.MOUSE_PRESSED) {
3710:				setReference(component, 2, 2);
3711:			}
3712:			else if (id == MouseEvent.MOUSE_DRAGGED) {
3713:				int divider = getInteger(component, "divider", -1);
3714:				boolean horizontal = ("vertical" != get(component, "orientation"));
3715:				int moveto = horizontal ? (x - referencex) : (y - referencey);
3716:				Rectangle bounds = getRectangle(component, "bounds");
3717:				moveto = Math.max(0, Math.min(moveto,
3718:					Math.abs(horizontal ? bounds.width : bounds.height) - 5));
3719:				if (divider != moveto) {
3720:					setInteger(component, "divider", moveto, -1);
3721:					validate(component);
3722:				}
3723:			}
3724:			else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
3725:				boolean horizontal = ("vertical" != get(component, "orientation"));
3726:				setCursor(Cursor.getPredefinedCursor(horizontal ?
3727:					Cursor.E_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR));
3728:			}
3729:			else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
3730:					((id == MouseEvent.MOUSE_RELEASED) && (mouseinside != component))) {
3731:				setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3732:			}
3733:		}
3734:		else if (("list" == classname) ||
3735:				("table" == classname) || ("tree" == classname)) {
3736:			if (!processScroll(x, y, id, component, part)) {
3737:				if (((id == MouseEvent.MOUSE_PRESSED)||
3738:						((id == MouseEvent.MOUSE_DRAGGED) &&
3739:							!shiftdown && !controldown))) { 
3740:					//Rectangle view = getRectangle(component, ":view");
3741:					Rectangle port = getRectangle(component, ":port");
3742:					int my = y + port.y - referencey;
3743:					for (Object item = get(component, ":comp"); item != null;) {
3744:						Rectangle r = getRectangle(item, "bounds");
3745:						if (my < r.y + r.height) {
3746:							if (id == MouseEvent.MOUSE_DRAGGED) { //!!!
3747:								scrollToVisible(component, r.x, r.y, 0, r.height);
3748:							}
3749:							else if ("tree" == classname) {
3750:								int mx = x + port.x - referencex;
3751:								if (mx < r.x) {
3752:									if ((mx >= r.x - block) && (get(item, ":comp") != null)) {
3753:										boolean expanded = getBoolean(item, "expanded", true);
3754:										setBoolean(item, "expanded", !expanded, 	true);
3755:										selectItem(component, item, true);
3756:										setLead(component, get(component, ":lead"), item);
3757:										setFocus(component);
3758:										validate(component);
3759:										invoke(component, item, expanded ? "collapse" : "expand"); //item
3760:									}
3761:									break;
3762:								}
3763:							}
3764:							if ((id != MouseEvent.MOUSE_DRAGGED) ||
3765:									!getBoolean(item, "selected", false)) {
3766:								if (id != MouseEvent.MOUSE_DRAGGED) {
3767:									if (setFocus(component)) { repaint(component, classname, item); } //?
3768:								}
3769:								select(component, item, ("tree" == classname), shiftdown, controldown);
3770:								if (clickcount == 2) { invoke(component, item, "perform"); }
3771:							}
3772:							break;
3773:						}
3774:						item = getNextItem(component, item, ("tree" == classname));
3775:					}
3776:				}
3777:			}
3778:		}
3779:		else if ("menubar" == classname) {
3780:			Object selected = get(component, "selected");
3781:			if (((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_EXITED)) &&
3782:					(part != null) && (selected == null) && getBoolean(part, "enabled", true)) {
3783:				repaint(component, classname, part);
3784:			}
3785:			else if ((part != null) && ((selected == null) ?
3786:					(id == MouseEvent.MOUSE_PRESSED) :
3787:						((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED))) &&
3788:					getBoolean(part, "enabled", true)) {
3789:					// || ((id == MouseEvent.MOUSE_PRESSED) && (insidepart != part))
3790:				set(component, "selected", part);
3791:				popupMenu(component);
3792:				repaint(component, classname, part);
3793:			}
3794:			else if ((id == MouseEvent.MOUSE_PRESSED) && (selected != null)) {
3795:				closeup();
3796:			}
3797:			else if (id == MouseEvent.MOUSE_RELEASED) {
3798:				if ((part != insidepart) && ((insidepart == null) ||
3799:						((insidepart instanceof  Object[]) && (getClass(insidepart) != "menu")))) {
3800:					if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
3801:						if (getClass(insidepart) == "checkboxmenuitem") {
3802:							changeCheck(insidepart, false);
3803:						}
3804:						else invoke(insidepart, null, "action");
3805:					}
3806:					closeup();
3807:				}
3808:			}
3809:		}
3810:		else if (":popup" == classname) {
3811:			if (part != null) {
3812:				if (((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) &&
3813:						getBoolean(part, "enabled", true)) {
3814:					set(component, "selected", part);
3815:					popupMenu(component);
3816:					repaint(component, classname, part);
3817:				}
3818:				else if (id == MouseEvent.MOUSE_RELEASED) {
3819:					if ((insidepart == null) || (getClass(insidepart) != "menu")) {
3820:						if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
3821:							if (getClass(insidepart) == "checkboxmenuitem") {
3822:								changeCheck(insidepart, false);
3823:							}
3824:							else invoke(insidepart, null, "action");
3825:						}
3826:						closeup();
3827:					}
3828:				}
3829:				else if (((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) &&
3830:						getBoolean(part, "enabled", true)) {
3831:					if (getClass(part) != "menu") {
3832:						set(component, "selected", null);
3833:					}
3834:					repaint(component, classname, part);
3835:				}
3836:			}
3837:		}
3838:		else if ("dialog" == classname) {
3839:			if (part == "header") {
3840:				if (id == MouseEvent.MOUSE_PRESSED) {
3841:					Rectangle bounds = getRectangle(component, "bounds");
3842:					referencex = x - bounds.x; referencey = y - bounds.y;
3843:					Object parent = getParent(component);
3844:					if (get(parent, ":comp") != component) { // to front
3845:						removeItemImpl(parent, component);
3846:						insertItem(parent, ":comp", component, 0);
3847:						set(component, ":parent", parent);
3848:						repaint(component); // to front always...
3849:						setNextFocusable(component, false);
3850:					}
3851:				}
3852:				else if (id == MouseEvent.MOUSE_DRAGGED) {
3853:					Rectangle bounds = getRectangle(component, "bounds");
3854:					Rectangle parents = getRectangle(getParent(component), "bounds");
3855:					int mx = Math.max(0, Math.min(x - referencex, parents.width - bounds.width));
3856:					int my = Math.max(0, Math.min(y - referencey, parents.height - bounds.height));
3857:					if ((bounds.x != mx) || (bounds.y != my)) {
3858:						// repaint the union of the previous and next bounds
3859:						repaint(component, Math.min(bounds.x, mx), Math.min(bounds.y, my),
3860:							bounds.width + Math.abs(mx - bounds.x), bounds.height + Math.abs(my - bounds.y));
3861:						bounds.x = mx; bounds.y = my;
3862:					}
3863:				}
3864:			}
3865:			else if (!processScroll(x, y, id, component, part) && (part != null)) {
3866:				if (id == MouseEvent.MOUSE_PRESSED) {
3867:					referencex = x; referencey = y;
3868:				}
3869:				else if (id == MouseEvent.MOUSE_DRAGGED) {
3870:					repaint(component);
3871:					Rectangle bounds = getRectangle(component, "bounds");
3872:					if ((part == ":nw") || (part == ":n") || (part == ":ne")) {
3873:						bounds.y += y - referencey; bounds.height -= y - referencey;
3874:					}
3875:					if ((part == ":ne") || (part == ":e") || (part == ":se")) {
3876:						bounds.width += x - referencex;
3877:					}
3878:					if ((part == ":sw") || (part == ":s") || (part == ":se")) {
3879:						bounds.height += y - referencey;
3880:					}
3881:					if ((part == ":nw") || (part == ":w") || (part == ":sw")) {
3882:						bounds.x += x - referencex; bounds.width -= x - referencex;
3883:					}
3884:					referencex = x; referencey = y;
3885:					doLayout(component); repaint(component);
3886:				}
3887:				else if (id == MouseEvent.MOUSE_ENTERED) {
3888:					setCursor(Cursor.getPredefinedCursor(
3889:						(part == ":n") ? Cursor.N_RESIZE_CURSOR :
3890:						(part == ":ne") ? Cursor.NE_RESIZE_CURSOR :
3891:						(part == ":e") ? Cursor.E_RESIZE_CURSOR :
3892:						(part == ":se") ? Cursor.SE_RESIZE_CURSOR :
3893:						(part == ":s") ? Cursor.S_RESIZE_CURSOR :
3894:						(part == ":sw") ? Cursor.SW_RESIZE_CURSOR :
3895:						(part == ":w") ? Cursor.W_RESIZE_CURSOR :
3896:							Cursor.NW_RESIZE_CURSOR));
3897:				}
3898:				else if (id == MouseEvent.MOUSE_EXITED) {
3899:					setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
3900:				}
3901:			}
3902:		}
3903:		
3904:		if (popuptrigger) {// && (id == MouseEvent.MOUSE_RELEASED)) {
3905:			Object popupmenu = get(component, "popupmenu");
3906:			if (popupmenu != null) {
3907:				popupPopup(popupmenu, x, y);
3908:			}
3909:		}
3910:	}
3911:
3912:	/**
3913:	 * Calculate the given point in a component relative to the thinlet desktop and
3914:	 * set as reference value
3915:	 * @param component a widget
3916:	 * @param x reference point relative to the component left edge 
3917:	 * @param y relative to the top edge
3918:	 */
3919:	private void setReference(Object component, int x, int y) {
3920:		referencex = x; referencey = y;
3921:		for (; component != null; component = getParent(component)) {
3922:			Rectangle bounds = getRectangle(component, "bounds");
3923:			referencex += bounds.x; referencey += bounds.y;
3924:			
3925:			Rectangle port = getRectangle(component, ":port");
3926:			if (port != null) { // content scrolled
3927:					Rectangle view = getRectangle(component, ":view");
3928:					referencex -= view.x - port.x; referencey -= view.y - port.y;
3929:				}
3930:		}
3931:	}
3932:
3933:	/**
3934:	 *
3935:	 */
3936:	private void select(Object component, Object row,
3937:			boolean recursive, boolean shiftdown, boolean controldown) {
3938:		String selection = getString(component, "selection", "single");
3939:		Object lead = null;
3940:		if (shiftdown && (selection != "single") &&
3941:				((lead = get(component, ":lead")) != null)) {
3942:			extend(component, lead, row, recursive);
3943:		}
3944:		else {
3945:			if (controldown && (selection == "multiple")) {
3946:				setBoolean(row, "selected",
3947:					!getBoolean(row, "selected", false), false);
3948:				repaint(component, null, row);
3949:				invoke(component, row, "action");
3950:				set(component, ":anchor", null);
3951:			}
3952:			else if (controldown && getBoolean(row, "selected", false)) {
3953:				for (Object item = row;
3954:						item != null; item = getNextItem(component, item, recursive)) {
3955:					if (setBoolean(item, "selected", false, false)) {
3956:						repaint(component, null, item);
3957:					}
3958:				}
3959:				invoke(component, row, "action");
3960:				set(component, ":anchor", null);
3961:			}
3962:			else {
3963:				selectItem(component, row, recursive);
3964:			}
3965:		}
3966:		setLead(component, (lead != null) ? lead : get(component, ":lead"), row);
3967:	}
3968:
3969:	/**
3970:	 * Find the next item after the given
3971:	 * @param component a list/tree/table widget
3972:	 * @param item the next item after this, or the first if null
3973:	 * @param recursive true if tree
3974:	 * @return next (or first) item
3975:	 */
3976:	private Object getNextItem(Object component,
3977:			Object item, boolean recursive) {
3978:		if (!recursive) { return get(item, ":next"); }
3979:		Object next = get(item, ":comp");
3980:		if ((next == null) || !getBoolean(item, "expanded", true)) {
3981:			while ((item != component) && ((next = get(item, ":next")) == null)) {
3982:				item = getParent(item);
3983:			}
3984:		}
3985:		return next;
3986:	}
3987:	
3988:	/**
3989:	 *
3990:	 */
3991:	private void processField(int x, int y, int clickcount,
3992:			int id, Object component,
3993:			Object part, boolean multiline, boolean hidden, int left) {
3994:		if (id == MouseEvent.MOUSE_PRESSED) {
3995:			//+ middle=alt paste clipboard content
3996:			setReference(component, 2 + left, 2);
3997:			int mx = x - referencex;
3998:			int my = 0;
3999:			if (!multiline) {
4000:				mx += getInteger(component, ":offset", 0);
4001:			} else {
4002:				Rectangle port = getRectangle(component, ":port");
4003:				mx += port.x - 1;
4004:				my = y - referencey + port.y - 1;
4005:			}
4006:			int caretstart = getCaretLocation(component, mx, my, multiline, hidden);
4007:			int caretend = caretstart;
4008:			if (clickcount > 1) {
4009:				String text = getString(component, "text", "");
4010:				while ((caretstart > 0) && ((clickcount == 2) ?
4011:					Character.isLetterOrDigit(text.charAt(caretstart - 1)) :
4012:						(text.charAt(caretstart - 1) != '\n'))) { caretstart--; }
4013:				while ((caretend < text.length()) && ((clickcount == 2) ?
4014:					Character.isLetterOrDigit(text.charAt(caretend)) :
4015:						(text.charAt(caretend) != '\n'))) { caretend++; }
4016:			}
4017:			setInteger(component, "start", caretstart, 0);
4018:			setInteger(component, "end", caretend, 0);
4019:			setFocus(component);
4020:			validate(component); // caret check only
4021:		}
4022:		else if (id == MouseEvent.MOUSE_DRAGGED) {
4023:			int mx = x - referencex;
4024:			int my = 0;
4025:			if (!multiline) {
4026:				mx += getInteger(component, ":offset", 0);
4027:			} else {
4028:				Rectangle port = getRectangle(component, ":port");
4029:				mx += port.x - 1;
4030:				my = y - referencey + port.y - 1;
4031:			}
4032:			int dragcaret = getCaretLocation(component, mx, my, multiline, hidden);
4033:			if (dragcaret != getInteger(component, "end", 0)) {
4034:				setInteger(component, "end", dragcaret, 0);
4035:				validate(component); // caret check only
4036:			}
4037:		}
4038:		else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
4039:			setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
4040:		}
4041:		else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
4042:			((id == MouseEvent.MOUSE_RELEASED) &&
4043:				((mouseinside != component) || (insidepart != null)))) {
4044:			setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
4045:		}
4046:	}
4047:
4048:	/**
4049:	 *
4050:	 */
4051:	private int getCaretLocation(Object component,
4052:			int x, int y, boolean multiline, boolean hidden) {
4053:		Font currentfont = (Font) get(component, "font");
4054:		FontMetrics fm = getFontMetrics((currentfont != null) ? currentfont : font);
4055:		char[] chars = multiline ? ((char[]) get(component, ":text")) :
4056:			getString(component, "text", "").toCharArray(); // update it
4057:		int linestart = 0;
4058:		if (multiline) {
4059:			int height = fm.getHeight(); // find the line start by y value
4060:			for (int i = 0; (y >= height) && (i < chars.length); i++) {
4061:				if ((chars[i] == '\n') || (chars[i] == '\t')) {
4062:					linestart = i + 1; y -= height;
4063:				}
4064:			}
4065:		}
4066:		for (int i = linestart; i < chars.length; i++) {
4067:			if ((chars[i] == '\n') || (chars[i] == '\t')) { return i; }
4068:			int charwidth = fm.charWidth(hidden ? '*' : chars[i]);
4069:			if (x <= (charwidth / 2)) { return i; }
4070:			x -= charwidth;
4071:		}
4072:		return chars.length;
4073:	}
4074:
4075:	/**
4076:	 *
4077:	 */
4078:	private boolean processScroll(int x, int y,
4079:			int id, Object component, Object part) {
4080:		if ((part == "up") || (part == "down") ||
4081:				(part == "left") || (part == "right")) {
4082:			if ((id == MouseEvent.MOUSE_ENTERED) ||
4083:					(id == MouseEvent.MOUSE_EXITED) ||
4084:					(id == MouseEvent.MOUSE_PRESSED) ||
4085:					(id == MouseEvent.MOUSE_RELEASED)) {
4086:				if (id == MouseEvent.MOUSE_PRESSED) {
4087:					if (processScroll(component, part)) {
4088:						setTimer(300L); return true;
4089:					}
4090:				}
4091:				else {
4092:					if (id == MouseEvent.MOUSE_RELEASED) { setTimer(0L); }
4093:					repaint(component, null, part);
4094:				}
4095:			}
4096:		}
4097:		else if ((part == "uptrack") || (part == "downtrack") ||
4098:				(part == "lefttrack") || (part == "righttrack")) {
4099:			if (id == MouseEvent.MOUSE_PRESSED) {
4100:				if (processScroll(component, part)) {
4101:					setTimer(300L);
4102:				}
4103:			}
4104:			else if (id == MouseEvent.MOUSE_RELEASED) {
4105:				setTimer(0L);
4106:			}
4107:		}
4108:		else if ((part == "vknob") || (part == "hknob")) {
4109:			if (id == MouseEvent.MOUSE_PRESSED) {
4110:				Rectangle port = getRectangle(component, ":port");
4111:				Rectangle view = getRectangle(component, ":view");
4112:				if (part == "hknob") {
4113:					referencex = x - view.x * (port.width - 2 * block) / view.width;
4114:				} else {
4115:					referencey = y - view.y * (port.height - 2 * block) / view.height;
4116:				}
4117:			}
4118:			else if (id == MouseEvent.MOUSE_DRAGGED) {
4119:				Rectangle port = getRectangle(component, ":port");
4120:				Rectangle view = getRectangle(component, ":view");
4121:				if (part == "hknob") {
4122:					int viewx = (x - referencex) * view.width / (port.width - 2 * block);
4123:					viewx = Math.max(0, Math.min(viewx, view.width - port.width));
4124:					if (view.x != viewx) {
4125:						view.x = viewx;
4126:						repaint(component, null, "horizontal");
4127:					}
4128:				}
4129:				else { // (part == "vknob")
4130:					int viewy = (y - referencey) * view.height / (port.height - 2 * block);
4131:					viewy = Math.max(0, Math.min(viewy, view.height - port.height));
4132:					if (view.y != viewy) {
4133:						view.y = viewy;
4134:						repaint(component, null, "vertical");
4135:					}
4136:				}
4137:			}
4138:		}
4139:		else if (part == "corner") {
4140:				part = "corner"; // compiler bug
4141:		}
4142:		else { //?
4143:			if (id == MouseEvent.MOUSE_PRESSED) {
4144:				Rectangle port = getRectangle(component, ":port");
4145:				if (port != null) { setReference(component, port.x, port.y); }
4146:			}
4147:			return false;
4148:		}
4149:		return true;
4150:	}
4151:
4152:	/**
4153:	 *
4154:	 */
4155:	private boolean processScroll(Object component, Object part) {
4156:		Rectangle view = getRectangle(component, ":view");
4157:		Rectangle port = ((part == "left") || (part == "up")) ? null :
4158:			getRectangle(component, ":port");
4159:		int dx = 0; int dy = 0;
4160:		if (part == "left") { dx = -10; }
4161:		else if (part == "lefttrack") { dx = -port.width; }
4162:		else if (part == "right") { dx = 10; }
4163:		else if (part == "righttrack") { dx = port.width; }
4164:		else if (part == "up") { dy = -10; }
4165:		else if (part == "uptrack") { dy = -port.height; }
4166:		else if (part == "down") { dy = 10; }
4167:		else if (part == "downtrack") { dy = port.height; }
4168:		if (dx != 0) {
4169:			dx = (dx < 0) ? Math.max(-view.x, dx) :
4170:				Math.min(dx, view.width - port.width - view.x);
4171:		}
4172:		else if (dy != 0) {
4173:			dy = (dy < 0) ? Math.max(-view.y, dy) :
4174:				Math.min(dy, view.height - port.height - view.y);
4175:		}
4176:		else return false;
4177:		if ((dx == 0) && (dy == 0)) { return false; }
4178:		view.x += dx; view.y += dy;
4179:		repaint(component, null, (dx != 0) ? "horizontal" : "vertical");
4180:		return (((part == "left") || (part == "lefttrack")) && (view.x > 0)) ||
4181:			(((part == "right") || (part == "righttrack")) &&
4182:				(view.x < view.width - port.width)) ||
4183:			(((part == "up") || (part == "uptrack")) && (view.y > 0)) ||
4184:			(((part == "down") || (part == "downtrack")) &&
4185:				(view.y < view.height - port.height));
4186:	}
4187:
4188:	/**
4189:	 *
4190:	 */
4191:	private boolean processSpin(Object component, Object part) {
4192:		String text = getString(component, "text", "");
4193:		try {
4194:			int itext = Integer.parseInt(text);
4195:			int step = getInteger(component, "step", 1);
4196:			if ((part == "up") ?
4197:					(itext + step <= getInteger(component, "maximum", Integer.MAX_VALUE)) :
4198:					(itext - step >= getInteger(component, "minimum", Integer.MIN_VALUE))) {
4199:				String value = String.valueOf((part == "up") ? (itext + step) : (itext - step));
4200:				setString(component, "text", value, null);
4201:				setInteger(component, "start", value.length(), 0);
4202:				setInteger(component, "end", 0, 0);
4203:				repaint(component, "spinbox", "text");
4204:				invoke(component, null, "action");
4205:				return true;
4206:			}
4207:		} catch (NumberFormatException nfe) {}
4208:		return false;
4209:	}
4210:
4211:	/**
4212:	 *
4213:	 */
4214:	private boolean invoke(Object component, Object part, String event) {
4215:		Object method = get(component, event);
4216:		if (method != null) {
4217:			invokeImpl(method, component, part);
4218:			return true;
4219:		}
4220:		return false;
4221:	}
4222:	
4223:	/**
4224:	 *
4225:	 */
4226:	private void invokeImpl(Object method, Object component, Object part) {
4227:		Object[] data = (Object[]) method;
4228:		Object[] args = (data.length > 2) ? new Object[(data.length - 2) / 3] : null;
4229:		if (args != null) for (int i = 0; i < args.length; i++) {
4230:			Object target = data[2 + 3 * i];
4231:			if ("thinlet" == target) {
4232:				args[i] = this ;
4233:			}
4234:			else {
4235:				if ("item" == target) { target = part; }
4236:				Object parametername = data[2 + 3 * i + 1];
4237:				if (parametername == null) {
4238:					args[i] = target;
4239:					//args[i] = new Widget(this, target);
4240:				}
4241:				else {
4242:					args[i] = (target != null) ? get(target, parametername) : null;
4243:					if (args[i] == null) { args[i] = data[2 + 3 * i + 2]; }
4244:				}
4245:			}
4246:		}
4247:		try {
4248:			((Method) data[1]).invoke(data[0], args);
4249:		} catch (InvocationTargetException ite) {
4250:			handleException(ite.getTargetException());
4251:		} catch (Throwable throwable) {
4252:			handleException(throwable);
4253:		}
4254:	}
4255:	
4256:	/**
4257:	 * Overwrite this method to handle exceptions thrown
4258:	 * by the invoked custom methods
4259:	 *
4260:	 * @param throwable the thrown exception by the bussiness logic
4261:	 */
4262:	protected void handleException(Throwable throwable) {
4263:		throwable.printStackTrace();
4264:	}
4265:
4266:	/**
4267:	 *
4268:	 */
4269:	private boolean findComponent(Object component, int x, int y) {
4270:		if (component == content) {
4271:			mouseinside = insidepart = null;
4272:			mousex = x; mousey = y;
4273:		}
4274:		if (!getBoolean(component, "visible", true)) { return false; }
4275:		Rectangle bounds = getRectangle(component, "bounds");
4276:		if ((bounds == null) || !(bounds.contains(x, y))) { return false; }
4277:		mouseinside = component;
4278:		x -= bounds.x; y -= bounds.y;
4279:		String classname = getClass(component);
4280:
4281:		if ("combobox" == classname) {
4282:			if (getBoolean(component, "editable", true) && (x <= bounds.width - block)) {
4283:				Image icon = getIcon(component, "icon", null);
4284:				insidepart = ((icon != null) && (x <= 2 + icon.getWidth(this ))) ?
4285:					"icon" : null;
4286:			} else {
4287:				insidepart = "down";
4288:			}
4289:		}
4290:		else if (":combolist" == classname) {
4291:			if (!findScroll(component, x, y)) {
4292:				y += getRectangle(component, ":view").y;
4293:				for (Object choice = get(get(component, "combobox"), ":comp");
4294:						choice != null; choice = get(choice, ":next")) {
4295:					Rectangle r = getRectangle(choice, "bounds");
4296:					if ((y >= r.y) && (y < r.y + r.height)) {
4297:						insidepart = choice; break;
4298:					}
4299:				}
4300:			}
4301:		}
4302:		else if ("textarea" == classname) {
4303:			findScroll(component, x, y);
4304:		}
4305:		else if ("tabbedpane" == classname) {
4306:			int selected = getInteger(component, "selected", 0);
4307:			int i = 0;
4308:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
4309:				Rectangle r = getRectangle(tab, "bounds");
4310:				if (i == selected) {
4311:					Object tabcontent = get(tab, ":comp");
4312:					if ((tabcontent != null) && findComponent(tabcontent, x - r.x, y - r.y)) {
4313:						break;
4314:					}
4315:				}
4316:				if (r.contains(x, y)) {
4317:					insidepart = tab; break;
4318:				}
4319:				i++;
4320:			}
4321:		}
4322:		else if (("panel" == classname) || ("desktop" == classname) ||
4323:				("dialog" == classname)) {
4324:			if ("dialog" == classname) {
4325:				if (getBoolean(component, "resizable", false)) {
4326:					if (x < 4) {
4327:						insidepart = (y < block) ? ":nw" :
4328:							(y >= bounds.height - block) ? ":sw" : ":w";
4329:					} else if (y < 4) {
4330:						insidepart = (x < block) ? ":nw" :
4331:							(x >= bounds.width - block) ? ":ne" : ":n";
4332:					} else if (x >= bounds.width - 4) {
4333:						insidepart = (y < block) ? ":ne" :
4334:							(y >= bounds.height - block) ? ":se" : ":e";
4335:					} else if (y >= bounds.height - 4) {
4336:						insidepart = (x < block) ? ":sw" :
4337:							(x >= bounds.width - block) ? ":se" : ":s";
4338:					}
4339:				}
4340:				if ((insidepart == null) &&
4341:						(y < 4 + getInteger(component, ":titleheight", 0))) {
4342:					insidepart = "header";
4343:				}
4344:			}
4345:			if ((insidepart == null) && !findScroll(component, x, y)) {
4346:				Rectangle port = getRectangle(component, ":port");
4347:				if (port != null) { // content scrolled
4348:					Rectangle view = getRectangle(component, ":view");
4349:					x += view.x - port.x; y += view.y - port.y;
4350:				}
4351:				for (Object comp = get(component, ":comp");
4352:						comp != null; comp = get(comp, ":next")) {
4353:					if (findComponent(comp, x, y)) { break; }
4354:					if (("desktop" == classname) &&
4355:							getBoolean(comp, "modal", false)) { insidepart = "modal"; break; } // && dialog
4356:				}
4357:			}
4358:		}
4359:		else if ("spinbox" == classname) {
4360:			insidepart = (x <= bounds.width - block) ? null :
4361:				((y <= bounds.height / 2) ? "up" : "down");
4362:		}
4363:		else if ("splitpane" == classname) { 
4364:			Object comp1 = get(component, ":comp");
4365:			if (comp1 != null) {
4366:				if (!findComponent(comp1, x, y)) {
4367:					Object comp2 = get(comp1, ":next");
4368:					if (comp2 != null) {
4369:						findComponent(comp2, x, y);
4370:					}
4371:				}
4372:			}
4373:		}
4374:		else if ("list" == classname) {
4375:			findScroll(component, x, y);
4376:		}
4377:		else if ("table" == classname) {
4378:			if (!findScroll(component, x, y)) {
4379:			}
4380:		}
4381:		else if ("tree" == classname) {
4382:			findScroll(component, x, y);
4383:		}
4384:		else if ("menubar" == classname) {
4385:			for (Object menu = get(component, ":comp");
4386:					menu != null; menu = get(menu, ":next")) {
4387:				Rectangle r = getRectangle(menu, "bounds");
4388:				if ((x >= r.x) && (x < r.x + r.width)) {
4389:					insidepart = menu; break;
4390:				}
4391:			}
4392:		}
4393:		else if (":popup" == classname) {
4394:			for (Object menu = get(get(component, "menu"), ":comp");
4395:					menu != null; menu = get(menu, ":next")) {
4396:				Rectangle r = getRectangle(menu, "bounds");
4397:				if ((y >= r.y) && (y < r.y + r.height)) {
4398:					insidepart = menu; break;
4399:				}
4400:			}
4401:		}
4402:		return true;
4403:	}
4404:
4405:	/**
4406:	 * @param component a scrollable widget
4407:	 * @param x point x location
4408:	 * @param y point y location
4409:	 * @return true if the point (x, y) is inside scroll-control area
4410:	 * (scrollbars, corners, borders), false otherwise (vievport, header, or no scrollpane)
4411:	 */
4412:	private boolean findScroll(Object component, int x, int y) {
4413:		Rectangle port = getRectangle(component, ":port");
4414:		if ((port == null) || port.contains(x, y)) { return false; }
4415:		Rectangle view = getRectangle(component, ":view");
4416:		Rectangle horizontal = getRectangle(component, ":horizontal");
4417:		Rectangle vertical = getRectangle(component, ":vertical");
4418:		if ((horizontal != null) && horizontal.contains(x, y)) {
4419:			findScroll(x - horizontal.x, horizontal.width, port.width, view.x, view.width, true);
4420:		}
4421:		else if ((vertical != null) && vertical.contains(x, y)) {
4422:			findScroll(y - vertical.y, vertical.height, port.height, view.y, view.height, false);
4423:		}
4424:		else { insidepart = "corner"; }
4425:		return true;
4426:	}
4427:	
4428:	/**
4429:	 * @param p x or y relative to the scrollbar begin
4430:	 * @param size scrollbar width or height
4431:	 * @param portsize viewport width or height
4432:	 * @param viewp view x or y
4433:	 * @param viewsize view width or height
4434:	 * @param horizontal if true horizontal, vertical otherwise
4435:	 */
4436:	private void findScroll(int p, int size, int portsize, int viewp, int viewsize, boolean horizontal) {
4437:		if (p < block) { insidepart = horizontal ? "left" : "up"; }
4438:		else if (p > size - block) { insidepart = horizontal ? "right" : "down"; }
4439:		else {
4440:			int track = size - 2 * block;
4441:			if (track < 10) { insidepart = "corner"; return; } // too small
4442:			int knob = Math.max(track * portsize / viewsize, 10);
4443:			int decrease = viewp * (track - knob) / (viewsize - portsize);
4444:			if (p < block + decrease) { insidepart = horizontal ? "lefttrack" : "uptrack"; }
4445:			else if (p < block + decrease + knob) { insidepart = horizontal ? "hknob" : "vknob"; }
4446:			else { insidepart = horizontal ? "righttrack" : "downtrack"; }
4447:		}
4448:	}
4449:
4450:	/**
4451:	 *
4452:	 */
4453:	private void repaint(Object component, Object classname, Object part) {
4454:		Rectangle b = getRectangle(component, "bounds");
4455:		if (classname == "combobox") { // combobox down arrow
4456:			repaint(component, b.x + b.width - block, b.y, block, b.height); // icon?+
4457:		}
4458:		else if (classname == "spinbox") {
4459:			if (part == "text") { // spinbox textfield content
4460:				repaint(component, b.x, b.y, b.width - block, b.height);
4461:			}
4462:			else { // spinbox increase or decrease button
4463:				repaint(component, b.x + b.width - block,
4464:					(part == "up") ? b.y : (b.y + b.height - b.height / 2), block, b.height / 2);
4465:			}
4466:		}
4467:		//else if (classname == "dialog") {}
4468:			//int titleheight = getInteger(component, ":titleheight", 0);
4469:		//else if (classname == "splitpane") {}
4470:		else if ((classname == "tabbedpane") || // tab
4471:				(classname == "menubar") || (classname == ":popup")) { // menuitem
4472:			Rectangle r = getRectangle(part, "bounds");
4473:			repaint(component, b.x + r.x, b.y + r.y,
4474:				(classname == ":popup") ? b.width : r.width, r.height);
4475:		}
4476:		// classname: ":combolist" "textarea" "list" "table" "tree"
4477:		else if ((part == "left") || (part == "right")) { // horizontal scrollbar button
4478:			Rectangle r = getRectangle(component, ":horizontal");
4479:			repaint(component, b.x + ((part == "left") ? r.x : (r.x + r.width - block)), b.y + r.y, block, r.height);
4480:		}
4481:		else if ((part == "up") || (part == "down")) { // vertical scrollbar button
4482:			Rectangle r = getRectangle(component, ":vertical");
4483:			repaint(component, b.x + r.x, b.y + ((part == "up") ? r.y : (r.y + r.height - block)), r.width, block);
4484:		}
4485:		else if ((part == "text") || (part == "horizontal") || (part == "vertical")) {
4486:			Rectangle port = getRectangle(component, ":port"); // textarea or content
4487:			repaint(component, b.x + port.x, b.y + port.y, port.width, port.height);
4488:			if (part == "horizontal") {
4489:				Rectangle r = getRectangle(component, ":horizontal");
4490:				repaint(component, b.x + r.x, b.y + r.y, r.width, r.height);
4491:				repaint(component, b.x + r.x, b.y, r.width, port.y); // paint header too
4492:			}
4493:			else if (part == "vertical") {
4494:				Rectangle r = getRectangle(component, ":vertical");
4495:				repaint(component, b.x + r.x, b.y + r.y, r.width, r.height);
4496:			}
4497:		}
4498:		else { // repaint the whole line of its subcomponent
4499:			Rectangle port = getRectangle(component, ":port");
4500:			Rectangle view = getRectangle(component, ":view");
4501:			Rectangle r = getRectangle(part, "bounds");
4502:			if ((r.y + r.height >= view.y) && (r.y <= view.y + port.height)) {
4503:				repaint(component, b.x + port.x, b.y + port.y - view.y + r.y,
4504:					port.width, r.height);
4505:				//? need cut item rectangle above/bellow viewport
4506:			}
4507:		}
4508:	}
4509:	
4510:	/**
4511:	 * Layout and paint the given component later
4512:	 * @param component
4513:	 */
4514:	private void validate(Object component) {
4515:		repaint(component);
4516:		Rectangle bounds = getRectangle(component, "bounds");
4517:		if (bounds != null) { bounds.width = -1 * Math.abs(bounds.width); }
4518:	}
4519:	
4520:	/**
4521:	 * Repaint the given component's area later
4522:	 * @param component a visible widget inside thinlet desktop
4523:	 */
4524:	public void repaint(Object component) {
4525:		Rectangle bounds = getRectangle(component, "bounds");
4526:		if (bounds != null) {
4527:			repaint(component, bounds.x, bounds.y, bounds.width, bounds.height);
4528:		}
4529:	}
4530:
4531:	/**
4532:	 * Repaint the given component's area later
4533:	 * @param component
4534:	 * @param x
4535:	 * @param y
4536:	 * @param width
4537:	 * @param height
4538:	 */
4539:	private void repaint(Object component, int x, int y, int width, int height) {
4540:		while ((component = getParent(component)) != null) {
4541:			Rectangle bounds = getRectangle(component, "bounds");
4542:			x += bounds.x; y += bounds.y;
4543:			Rectangle view = getRectangle(component, ":view");
4544:			if (view != null) {
4545:				Rectangle port = getRectangle(component, ":port");
4546:				x += -view.x + port.x; y += -view.y + port.y; //+ clip :port
4547:			}
4548:		}
4549:		repaint(x, y, width, height);
4550:	}
4551:
4552:	/**
4553:	 * Requests that both the <i>Thinlet</i> component,
4554:	 * and the given widget get the input focus
4555:	 *
4556:	 * @param component a focusable widget inside
4557:	 * visible and enabled parents, and tabbedpane's selected tab
4558:	 * @return true, if the given component was focusable
4559:	 */
4560:	public boolean requestFocus(Object component) { //#
4561:		if (isFocusable(component, true)) {
4562:			setFocus(component);
4563:			repaint(component);
4564:			return true;
4565:		}
4566:		return false;
4567:	}
4568:
4569:	/**
4570:	 * Request focus for the given component
4571:	 * @param component a focusable component
4572:	 * @return true if the focusowner was changed, otherwise false
4573:	 */
4574:	private boolean setFocus(Object component) {
4575:		if (!focusinside) { // request focus for the thinlet component
4576:			requestFocus();
4577:		}
4578:		if (focusowner != component) {
4579:			Object focused = focusowner;
4580:			if (focusowner != null) {
4581:				focusowner = null; // clear focusowner
4582:				repaint(focused);
4583:				// invoke the focus listener of the previously focused component
4584:				invoke(focused, null, "focuslost");
4585:			}
4586:			if(focusowner == null) { // it won't be null, if refocused
4587:				focusowner = component;
4588:				// invoke the focus listener of the new focused component
4589:				invoke(component, null, "focusgained");
4590:			}
4591:			return true;
4592:		}
4593:		return false;
4594:	}
4595:	
4596:	/**
4597:	 * @return next focusable component is found (not the first of the desktop/dialog)
4598:	 */
4599:	private boolean setNextFocusable(Object current, boolean outgo) {
4600:		boolean consumed = true;
4601:		for (Object next = null, component = current; true; component = next) {
4602:			next = get(component, ":comp"); // check first subcomponent
4603:			if (next == null) { next = get(component, ":next"); } // check next component
4604:			while (next == null) { // find the next of the parents, or the topmost
4605:				component = getParent(component); // current is not on the desktop
4606:				if (component == null) { return false; }
4607:				if ((component == content) || ((getClass(component) == "dialog") &&
4608:						(!outgo || getBoolean(component, "modal", false)))) {
4609:					consumed = false; // find next focusable but does not consume event
4610:					next = component; // the topmost (desktop or modal dialog)
4611:				}
4612:				else {
4613:					next = get(component, ":next");
4614:				}
4615:			}
4616:			if (next == current) { return false; } // one fucusable, no loop
4617:			if (isFocusable(next, false)) {
4618:				setFocus(next);
4619:				return consumed;
4620:			}
4621:		}
4622:	}
4623:
4624:	/**
4625:	 * @return previous focusable component is found (not the last of the desktop/dialog)
4626:	 */
4627:	private boolean setPreviousFocusable(Object component, boolean outgo) {
4628:		for (int i = 0; i < 2; i++) { // 0 is backward direction
4629:			Object previous = getPreviousFocusable(component, null, true, false, (i == 0), outgo);
4630:			if (previous != null) {
4631:				setFocus(previous);
4632:				return (i == 0);
4633:			}
4634:		}
4635:		return false;
4636:	}
4637:	
4638:	/**
4639:	 * For the starting component search its parent direction for a focusable component, and then
4640:	 * its next component (if not search backward from the component).<br />
4641:	 * For its parent components check its first component, the current one, and its parent direction
4642:	 * (backward search), or its parent, then next component (forward direction).<br />
4643:	 * For the rest components check the next, then the first subcomponent direction, and finally
4644:	 * check whether the component is focusable.
4645:	 */
4646:	private Object getPreviousFocusable(Object component,
4647:			Object block, boolean start, boolean upward, boolean backward, boolean outgo) {
4648:		Object previous = null;
4649:		if ((component != null) && (component != block)) {
4650:			boolean go = ((getClass(component) != "dialog") ||
4651:				(outgo && !getBoolean(component, "modal", false)));
4652:			if (!start && !upward && go) {
4653:				previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
4654:			}
4655:			if ((previous == null) && ((upward && backward) || (!start && !upward))) {
4656:				previous = getPreviousFocusable(get(component, ":comp"), block, false, false, backward, outgo);
4657:				if ((previous == null) && isFocusable(component, false)) {
4658:					previous = component;
4659:				}
4660:			}
4661:			if ((previous == null) && (start || upward) && go) {
4662:				previous = getPreviousFocusable(getParent(component), component, false, true, backward, outgo);
4663:			}
4664:			if ((previous == null) && (start || upward) && !backward && go) {
4665:				previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
4666:			}
4667:		}
4668:		return previous;
4669:	}
4670:	
4671:	/**
4672:	 * Check whether the given widget can become focusowner
4673:	 * @param component check this widget
4674:	 * @param forced splitpane is also checked
4675:	 * (e.g. false for tab navigating, and true for mouse selection or application request)
4676:	 * @return true if focusable, otherwise false
4677:	 */
4678:	private boolean isFocusable(Object component, boolean forced) {
4679:		String classname = getClass(component);
4680:		if ((classname == "button") || (classname == "checkbox") || ("togglebutton" == classname) ||
4681:				(classname == "combobox") || (classname == "textfield") ||
4682:				(classname == "passwordfield") || (classname == "textarea") ||
4683:				(classname == "spinbox") || (classname == "slider") ||
4684:				(classname == "list") || (classname == "table") || (classname == "tree") ||
4685:				(classname == "tabbedpane") || (forced && (classname == "splitpane"))) {
4686:			for (Object comp = component; comp != null;) {
4687:				// component and parents are enabled and visible
4688:				if (!getBoolean(comp, "enabled", true) || !getBoolean(comp, "visible", true)) {
4689:					return false;
4690:				}
4691:				Object parent = getParent(comp);
4692:				// inside the selected tabbedpane tab
4693:				if ((getClass(comp) == "tab") && (getItem(parent,
4694:					getInteger(parent, "selected", 0)) != comp)) { return false; }
4695:				comp = parent;
4696:			}
4697:			return true;
4698:		}
4699:		return false;
4700:	}
4701:
4702:	// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
4703:
4704:	/**
4705:	 * Creates a new component
4706:	 *
4707:	 * @param classname the widget type (e.g. <i>button</i>)
4708:	 * @return a new component, every component is simply an <i>Object</i>
4709:	 * @throws java.lang.IllegalArgumentException for unknown widget type
4710:	 */
4711:	public static Object create(String classname) { //#
4712:		for (int i = 0; i < dtd.length; i += 3) {
4713:			if (dtd[i].equals(classname)) {
4714:				return createImpl((String) dtd[i]);
4715:			}
4716:		}
4717:		throw new IllegalArgumentException("unknown " + classname);
4718:	}
4719:	
4720:	/**
4721:	 * Gets the type of the given component
4722:	 *
4723:	 * @param component a widget
4724:	 * @return the class name of the component (e.g. <i>button</i>)
4725:	 */
4726:	public static String getClass(Object component) { //#
4727:		return (String) get(component, ":class");
4728:	}
4729:
4730:	/**
4731:	 * Get the topmost component
4732:	 *
4733:	 * @return the root object (it is a <i>desktop</i>), never <i>null</i>
4734:	 */
4735:	public Object getDesktop() {//#
4736:		return content;
4737:	}
4738:
4739:	/**
4740:	 *
4741:	 */
4742:	private static Object createImpl(String classname) {
4743:		return new Object[] { ":class", classname, null };
4744:	}
4745:	
4746:	/**
4747:	 *
4748:	 */
4749:	private static boolean set(Object component, Object key, Object value) {
4750:		Object[] previous = (Object[]) component;
4751:		for (Object[] entry = previous; entry != null;
4752:				entry = (Object[]) entry[2]) {
4753:			if (entry[0] == key) {
4754:				if (value != null) { // set the row's value
4755:					Object oldvalue = entry[1];
4756:					entry[1] = value;
4757:					return !value.equals(oldvalue);
4758:				}
4759:				else { // remove the row
4760:					previous[2] = entry[2];
4761:					entry[2] = null;
4762:					return true;
4763:				}
4764:			}
4765:			previous = entry;
4766:		}
4767:		if (value != null) { // append a new row
4768:			previous[2] = new Object[] { key, value, null };
4769:			return true;
4770:		}
4771:		return false;
4772:	}
4773:
4774:	/**
4775:	 *
4776:	 */
4777:	private static Object get(Object component, Object key) {
4778:		for (Object[] entry = (Object[]) component; entry != null;
4779:				entry = (Object[]) entry[2]) {
4780:			if (entry[0] == key) {
4781:				return entry[1];
4782:			}
4783:		}
4784:		return null;
4785:	}
4786:
4787:	/**
4788:	 * Gets the count of subcomponents in the list of the given component
4789:	 *
4790:	 * @param component a widget
4791:	 * @return the number of components in this component
4792:	 */
4793:	public int getCount(Object component) {
4794:		return getItemCountImpl(component, ":comp");
4795:	}
4796:	
4797:	/**
4798:	 * Gets the parent of this component
4799:	 *
4800:	 * @param component a widget
4801:	 * @return the parent container of this component or item
4802:	 */
4803:	public Object getParent(Object component) {
4804:		return get(component, ":parent");
4805:	}
4806:
4807:	/**
4808:	 * Gets the index of the first selected item in the given component
4809:	 *
4810:	 * @param component a widget (combobox, tabbedpane, list, table, or tree)
4811:	 * @return the first selected index or -1
4812:	 */
4813:	public int getSelectedIndex(Object component) {
4814:		String classname = getClass(component);
4815:		if ((classname == "combobox") || (classname == "tabbedpane")) {
4816:			return getInteger(component, "selected", (classname == "combobox") ? -1 : 0);
4817:		}
4818:		if ((classname == "list") || (classname == "table") || (classname == "tree")) {
4819:			Object item = get(component, ":comp");
4820:			for (int i = 0; item != null; i++) {
4821:				if (getBoolean(item, "selected", false)) { return i; }
4822:				item = get(item, ":next");
4823:			}
4824:			return -1;
4825:		}
4826:		throw new IllegalArgumentException(classname);
4827:	}
4828:	
4829:	/**
4830:	 * Gets the first selected item of the given component
4831:	 *
4832:	 * @param component a widget (combobox, tabbedpane, list, table, or tree)
4833:	 * @return the first selected item or null
4834:	 */
4835:	public Object getSelectedItem(Object component) {
4836:		String classname = getClass(component);
4837:		if ((classname == "combobox") || (classname == "tabbedpane")) {
4838:			int index = getInteger(component, "selected",
4839:				(classname == "combobox") ? -1 : 0);
4840:			return (index != -1) ? getItemImpl(component, ":comp", index) : null;
4841:		}
4842:		if ((classname == "list") || (classname == "table") || (classname == "tree")) {
4843:			for (Object item = findNextItem(component, classname, null); item != null;
4844:					item = findNextItem(component, classname, item)) {
4845:				if (getBoolean(item, "selected", false)) { return item; }
4846:			}
4847:			return null;
4848:		}
4849:		throw new IllegalArgumentException(classname);
4850:	}
4851:
4852:	/**
4853:	 * Gets the selected item of the given component (list, table, or tree)
4854:	 * when multiple selection is allowed
4855:	 *
4856:	 * @param component a widget
4857:	 * @return the array of selected items, or a 0 length array
4858:	 */
4859:	public Object[] getSelectedItems(Object component) {
4860:		String classname = getClass(component);
4861:		Object[] selecteds = new Object[0];
4862:		for (Object item = findNextItem(component, classname, null); item != null;
4863:				item = findNextItem(component, classname, item)) {
4864:			if (getBoolean(item, "selected", false)) {
4865:				Object[] temp = new Object[selecteds.length + 1];
4866:				System.arraycopy(selecteds, 0, temp, 0, selecteds.length);
4867:				temp[selecteds.length] = item;
4868:				selecteds = temp;
4869:			}
4870:		}
4871:		return selecteds;
4872:	}
4873:
4874:	/**
4875:	 * @return the first or the next item of the (list, table, or tree) component
4876:	 */
4877:	private Object findNextItem(Object component, String classname, Object item) {
4878:		if (item == null) { // first item
4879:			return get(component, ":comp");
4880:		}
4881:		else if ("tree" == classname) { // next tree node
4882:			Object next = get(item, ":comp");
4883:			if ((next == null) || !getBoolean(item, "expanded", true)) { // no subnode or collapsed
4884:				while ((item != component) && ((next = get(item, ":next")) == null)) {
4885:					item = getParent(item); //next node of in backward path
4886:				}
4887:			}
4888:			return next;
4889:		}
4890:		else { //next list or tree item
4891:			return get(item, ":next");
4892:		}
4893:	}
4894:
4895:	/**
4896:	 * Removes all the components from this container's specified list
4897:	 *
4898:	 * @param component the specified container
4899:	 */
4900:	public void removeAll(Object component) {
4901:		if (get(component, ":comp") != null) {
4902:			set(component, ":comp", null);
4903:			update(component, "validate");
4904:		}
4905:	}
4906:
4907:	/**
4908:	 *
4909:	 */
4910:	private static int getItemCountImpl(Object component, String key) {
4911:		int i = 0;
4912:		for (Object comp = get(component, key); comp != null; comp = get(comp, ":next")) {
4913:			i++;
4914:		}
4915:		return i;
4916:	}
4917:
4918:	/**
4919:	 * Returns the subcomponent of the given component's specified list at the given index
4920:	 *
4921:	 * @param component a specified container
4922:	 * @param index the index of the component to get
4923:	 * @return the index<sup>th</sup> component in this container
4924:	 */
4925:	public Object getItem(Object component, int index) {
4926:		return getItemImpl(component, ":comp", index);
4927:	}
4928:	
4929:	/**
4930:	 * Gets all the components in this container
4931:	 *
4932:	 * @param component a specified container
4933:	 * @return an array of all the components in this container
4934:	 */
4935:	public Object[] getItems(Object component) {
4936:		Object[] items = new Object[getItemCountImpl(component, ":comp")];
4937:		Object comp = get(component, ":comp");
4938:		for (int i = 0; i < items.length; i++) {
4939:			items[i] = comp;
4940:			comp = get(comp, ":next");
4941:		}
4942:		return items;
4943:	}
4944:
4945:	/**
4946:	 * Referenced by DOM, replace by getItem for others
4947:	 */
4948:	private static Object getItemImpl(Object component, Object key, int index) {
4949:		int i = 0;
4950:		for (Object item = get(component, key); item != null; item = get(item, ":next")) {
4951:			if (i == index) { return item; }
4952:			i++;
4953:		}
4954:		return null;
4955:	}
4956:
4957:	/**
4958:	 *
4959:	 */
4960:	private int getIndex(Object component, Object value) {
4961:		int index = 0;
4962:		for (Object item = get(component, ":comp"); item != null; item = get(item, ":next")) {
4963:			if (value == item) { return index; }
4964:			index++;
4965:		}
4966:		return -1;
4967:	}
4968:
4969:	/**
4970:	 * Adds the specified component to the root desktop
4971:	 *
4972:	 * @param component a widget to be added
4973:	 */
4974:	public void add(Object component) {
4975:		add(content, component, 0);
4976:	}
4977:
4978:	/**
4979:	 * Adds the specified component to the end of the specified container
4980:	 *
4981:	 * @param parent a container
4982:	 * @param component a component to be added
4983:	 */
4984:	public void add(Object parent, Object component) {
4985:		add(parent, component, -1);
4986:	}
4987:
4988:	/**
4989:	 * Adds the specified component to the container at the given position
4990:	 *
4991:	 * @param parent a container
4992:	 * @param component a component to be inserted
4993:	 * @param index the position at which to insert the component, 
4994:	 * or -1 to insert the component at the end
4995:	 */
4996:	public void add(Object parent, Object component, int index) {
4997:		addImpl(parent, component, index);
4998:		update(component, "validate");
4999:		if (parent == content) {
5000:			setNextFocusable(component, false);
5001:		}
5002:	}
5003:
5004:	/**
5005:	 * Referenced by DOM
5006:	 */
5007:	private void insertItem(Object parent,
5008:			Object key, Object component, int index) {
5009:		Object item = parent, next = get(parent, key);
5010:		for (int i = 0;; i++) {
5011:			if ((i == index) || (next == null)) {
5012:				set(item, key, component);
5013:				set(component, ":next", next);
5014:				break;
5015:			}
5016:			next = get(item = next, key = ":next");
5017:		}
5018:	}
5019:
5020:	/**
5021:	 * Remove the specified component from its parent list, or
5022:	 * delete component's popupmenu or table's header
5023:	 *
5024:	 * @param component the component to be removed
5025:	 */
5026:	public void remove(Object component) {
5027:		update(component, "validate");
5028:		Object parent = getParent(component);
5029:		Object classname = getClass(component);
5030:		if (("popupmenu" == classname) || ("header" == classname)) {
5031:			set(parent, classname, null);
5032:		}
5033:		else {
5034:			removeItemImpl(parent, component);
5035:			// reuest focus for its parent if the component (or subcomponent) is currently focused
5036:			for (Object comp = focusowner; comp != null; comp = getParent(comp)) {
5037:				if (comp == component) {
5038:					setNextFocusable(parent, false); break;
5039:				}
5040:			}
5041:		}
5042:	}
5043:
5044:	/**
5045:	 * Delete the give component from its parent list
5046:	 * @param parent
5047:	 * @param component
5048:	 */
5049:	private void removeItemImpl(Object parent, Object component) {
5050:		Object previous = null; // the widget before the given component
5051:		for (Object comp = get(parent, ":comp"); 	comp != null;) {
5052:			Object next = get(comp, ":next");
5053:			if (next == component) { previous = comp; break; }
5054:			comp = next;
5055:		}
5056:		set((previous != null) ? previous : parent,
5057:			(previous != null) ? ":next" : ":comp", get(component, ":next"));
5058:		set(component, ":next", null); 	set(component, ":parent", null); // not required
5059:	}
5060:
5061:	/**
5062:	 * Finds the first component from the root desktop by a specified name value
5063:	 *
5064:	 * @param name parameter value identifies the widget
5065:	 * @return the first suitable component, or null
5066:	 */
5067:	public Object find(String name) {
5068:		return find(content, name);
5069:	}
5070:
5071:	/**
5072:	 * Finds the first component from the specified component by a name
5073:	 *
5074:	 * @param component the widget is searched inside this component
5075:	 * @param name parameter value identifies the widget
5076:	 * @return the first suitable component, or null
5077:	 */
5078:	public Object find(Object component, String name) {
5079:		if (name.equals(get(component, "name"))) {
5080:			return component;
5081:		}
5082:		// otherwise search in its subcomponents
5083:		Object found = null;
5084:		for (Object comp = get(component, ":comp"); 	comp != null; comp = get(comp, ":next")) {
5085:			if ((found = find(comp, name)) != null) { return found; }
5086:		}
5087:		// search in table header
5088:		Object header = get(component, "header"); // if ("table" == classname)
5089:		if ((header != null) && ((found = find(header, name)) != null)) { return found; }
5090:		// search in component's popupmenu
5091:		Object popupmenu = get(component, "popupmenu"); // if instance(classname, "component")
5092:		if ((popupmenu != null) && ((found = find(popupmenu, name)) != null)) { return found; }
5093:		return null;
5094:	}
5095:	
5096:	/**
5097:	 * mnemonic (e.g. Alt-X):
5098:	 * - check: label, button, checkbox, togglebutton, menubar menus, tabbedpane tabs
5099:	 * - path: panel, desktop, dialog, splitpane components, tabbedpane selected component 
5100:	 * accelerator (e.g. Ctrl-Shift-X, F4):
5101:	 * - check: menuitem, checkboxmenuitem
5102:	 * - path: see above, and menubar, and menu items
5103:	 * menubar F10: check menubar only
5104:	 * button enter, escape: check button only
5105:	 * @param component
5106:	 * @param parent check upwards if true
5107:	 * @param checked this leaf is already checked
5108:	 * @param mnemonic
5109:	 * @return true if the char was consumed
5110:	 */
5111:	private boolean checkMnemonic(Object component,
5112:			boolean parent, Object checked, int keycode, int modifiers) {
5113:		if ((component == null) || !getBoolean(component, "visible", true) ||
5114:				!getBoolean(component, "enabled", true)) { //+ enabled comp in disabled parent
5115:			return false;
5116:		}
5117:		String classname = getClass(component);
5118:		if ("label" == classname) {
5119:			if (hasMnemonic(component, keycode, modifiers)) {
5120:				Object labelfor = get(component, "for");
5121:				if (labelfor != null) {
5122:					requestFocus(labelfor);
5123:					return true;
5124:				}
5125:			}
5126:		}
5127:		else if ("button" == classname) {
5128:			if (((modifiers == 0) &&
5129:				(((keycode == KeyEvent.VK_ENTER) && (get(component, "type") == "default")) ||
5130:				((keycode == KeyEvent.VK_ESCAPE) && (get(component, "type") == "cancel")))) ||
5131:					hasMnemonic(component, keycode, modifiers)) {
5132:				invoke(component, null, "action");
5133:				repaint(component);
5134:				return true;
5135:			}
5136:		}
5137:		else if (("checkbox" == classname) || ("togglebutton" == classname)) {
5138:			if (hasMnemonic(component, keycode, modifiers)) {
5139:				changeCheck(component, true);
5140:				repaint(component);
5141:				return true;
5142:			}
5143:		}
5144:		else if ("menubar" == classname) {
5145:			for (Object menu = get(component, ":comp"); menu != null; menu = get(menu, ":next")) {
5146:				if (hasMnemonic(menu, keycode, modifiers) ||
5147:						((modifiers == 0) && (keycode == KeyEvent.VK_F10))) {
5148:					closeup();
5149:					set(component, "selected", menu);
5150:					popupMenu(component);
5151:					repaint(component, "menubar", menu);
5152:					return true;
5153:				}
5154:			}
5155:		}
5156:		else if (("menuitem" == classname) || ("checkboxmenuitem" == classname)) {
5157:			if (hasAccelerator(component, keycode, modifiers)) {
5158:				invoke(component, null, "action");
5159:			}
5160:		}
5161:		else if ("tabbedpane" == classname) {
5162:			int selected = getInteger(component, "selected", 0); int i = 0;
5163:			for (Object tab = get(component, ":comp"); tab != null; tab = get(tab, ":next")) {
5164:				if (hasMnemonic(tab, keycode, modifiers)) {
5165:					if (selected != i) {
5166:						setInteger(component, "selected", i, 0);
5167:						repaint(component);
5168:						invoke(component, getItem(component, i), "action");
5169:					}
5170:					return true;
5171:				}
5172:				i++;
5173:			}
5174:			Object comp = get(getItem(component, selected), ":comp");
5175:			if ((comp != null) && (comp != checked) &&
5176:					checkMnemonic(comp, false, null, keycode, modifiers)) {
5177:				return true;
5178:			}
5179:		}
5180:		// check subcomponents
5181:		if (("panel" == classname) || ("desktop" == classname) ||
5182:				("dialog" == classname) || ("splitpane" == classname) ||
5183:				("menubar" == classname) || ("menu" == classname)) {
5184:			for (Object comp = get(component, ":comp"); comp != null; comp = get(comp, ":next")) {
5185:				if ((comp != checked) && checkMnemonic(comp, false, null, keycode, modifiers)) { return true; }
5186:			}
5187:		}
5188:		// check parent
5189:		if (parent && (("dialog" != classname) || !getBoolean(component, "modal", false))) {
5190:			if (checkMnemonic(getParent(component), true,
5191:					("tab" == classname) ? checked : component, keycode, modifiers)) { return true; }
5192:		}
5193:		return false;
5194:	}
5195:	
5196:	/**
5197:	 * @param component
5198:	 * @param keycode
5199:	 * @param modifiers
5200:	 * @return true if the component has the given mnemonic
5201:	 */
5202:	private boolean hasMnemonic(Object component, int keycode, int modifiers) {
5203:		if (modifiers == InputEvent.ALT_MASK) {
5204:			int index = getInteger(component, "mnemonic", -1);
5205:			if (index != -1) {
5206:				String text = getString(component, "text", null);
5207:				return (text != null) && (text.length() > index) &&
5208:					(Character.toUpperCase(text.charAt(index)) == keycode);
5209:			}
5210:		}
5211:		return false;
5212:	}
5213:	
5214:	/**
5215:	 * @param component
5216:	 * @param keycode
5217:	 * @param modifiers
5218:	 * @return true if the component has the given accelerator
5219:	 */
5220:	private boolean hasAccelerator(Object component, int keycode, int modifiers) {
5221:		Object accelerator = get(component, "accelerator");
5222:		if (accelerator != null) {
5223:			long keystroke = ((Long) accelerator).longValue();
5224:			return ((keystroke >> 32) == modifiers) && ((keystroke & 0xffff) == keycode);
5225:		}
5226:		return false;
5227:	}
5228:	
5229:	/**
5230:	 * Binds the specified key to the specified value, and stores in this component.
5231:	 * <i>Null</i> value removes the property. Use the parameter tag in the xml
5232:	 * resource to bind a string value, the format is: <i>parameter='key=value'</i>
5233:	 *
5234:	 * @param component the hashtable is binded to this component
5235:	 * @param key the client property key
5236:	 * @param value the new client property value
5237:	 */
5238:	public void putProperty(Object component, Object key, Object value) {
5239:		Object table = get(component, ":bind");
5240:		if (value != null) {
5241:			if (table == null) {
5242:				set(component, ":bind", table = new Hashtable());
5243:			}
5244:			((Hashtable) table).put(key, value);
5245:		}
5246:		else if (table != null) {
5247:			((Hashtable) table).remove(key);
5248:		}
5249:	}
5250:	
5251:	/**
5252:	 * Returns the value of the property with the specified key.
5253:	 *
5254:	 * @param component searches the hashtable of this component
5255:	 * @param key the client property key
5256:	 * @return the value to which the key is mapped or null if the key is not mapped to any value
5257:	 */
5258:	public Object getProperty(Object component, Object key) {
5259:		Object table = get(component, ":bind");
5260:		return (table != null) ? ((Hashtable) table).get(key) : null;
5261:	}
5262:
5263:	// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
5264:
5265:	/**
5266:	 * Creates a component (and its subcomponents, and properties)
5267:	 * from the given xml resource
5268:	 *
5269:	 * @param path is relative to your thinlet instance or the classpath
5270:	 * (if the path starts with an <i>/</i> character), or a full URL
5271:	 * @return the root component of the parsed resource
5272:	 * @throws java.io.IOException
5273:	 */
5274:	public Object parse(String path) throws IOException {
5275:		return parse(path, this );
5276:	}
5277:
5278:	/**
5279:	 * Creates a component from the given xml resource using the
5280:	 * specified event handler
5281:	 *
5282:	 * @param path is relative to your application package or the classpath, or an URL
5283:	 * @param handler bussiness methods are implemented in this object 
5284:	 * @return the parsed components' root
5285:	 * @throws java.io.IOException
5286:	 */
5287:	public Object parse(String path, Object handler) throws IOException {
5288:		InputStream inputstream = null;
5289:		try {
5290:			inputstream = getClass().getResourceAsStream(path);
5291:			if (inputstream == null) {
5292:				try {
5293:					inputstream = new URL(path).openStream();
5294:				} catch (MalformedURLException mfe) { /* thows nullpointerexception*/ }
5295:			}
5296:		} catch (Throwable e) {}
5297:		return parse(inputstream, handler);
5298:	}
5299:
5300:	/**
5301:	 * Creates a component from the given stream
5302:	 *
5303:	 * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5304:	 * @return the root component of the parsed stream
5305:	 * @throws java.io.IOException
5306:	 */
5307:	public Object parse(InputStream inputstream) throws IOException {
5308:		return parse(inputstream, this );
5309:	}
5310:
5311:	/**
5312:	 * Creates a component from the given stream and event handler
5313:	 *
5314:	 * @param inputstream read xml from this stream
5315:	 * @param handler event handlers are implemented in this object
5316:	 * @return the parsed components' root
5317:	 * @throws java.io.IOException
5318:	 */
5319:	public Object parse(InputStream inputstream, Object handler) throws IOException {
5320:		return parse(inputstream, true, false, handler);
5321:	}
5322:
5323:	/**
5324:	 * You can use the internal xml parser as a simple SAX-like parser,
5325:	 * during the process it calls the <i>startElement</i>, <i>characters</i>,
5326:	 * and <i>endElement</i> methods
5327:	 *
5328:	 * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5329:	 * @throws java.io.IOException
5330:	 */
5331:	protected void parseXML(InputStream inputstream) throws IOException {
5332:		parse(inputstream, false, false, null);
5333:	}
5334:
5335:	/**
5336:	 * The SAX-like parser calls this method, you have to overwrite it
5337:	 *
5338:	 * @param name of the tag
5339:	 * @param attributelist a list of attributes including keys and value pairs
5340:	 */
5341:	protected void startElement(String name, Hashtable attributelist) {}
5342:
5343:	/**
5344:	 * The SAX-like parser calls this method, you have to overwrite it
5345:	 *
5346:	 * @param text the content of a tag
5347:	 */
5348:	protected void characters(String text) {}
5349:
5350:	/**
5351:	 * The SAX-like parser calls this method, you have to overwrite it
5352:	 */
5353:	protected void endElement() {}
5354:	
5355:	/**
5356:	 * You can use the internal xml parser as a simple DOM-like parser,
5357:	 * use the <i>getDOMAttribute</i>, <i>getDOMText</i>,
5358:	 * <i>getDOMCount</i>, <i>getDOMNode</i>, <i>getClass</i>,
5359:	 * and <i>getParent</i> methods to analise the document
5360:	 *
5361:	 * @param inputstream e.g. <i>new URL("http://myserver/myservlet").openStream()</i>
5362:	 * @return the root tag
5363:	 * @throws java.io.IOException
5364:	 */
5365:	protected Object parseDOM(InputStream inputstream) throws IOException {
5366:		return parse(inputstream, false, true, null);
5367:	}
5368:	
5369:	/**
5370:	 * Gets the attribute value by the specified key for a DOM tag
5371:	 *
5372:	 * @param node a specified tag
5373:	 * @param key a string to identify the value pair
5374:	 * @return the value, or null
5375:	 */
5376:	protected static String getDOMAttribute(Object node, String key) {
5377:		return (String) get(node, key.intern());
5378:	}
5379:	
5380:	/**
5381:	 * Gets the content string of a tag
5382:	 *
5383:	 * @param node a specified tag
5384:	 * @return the value, or null
5385:	 */
5386:	protected static String getDOMText(Object node) {
5387:		return (String) get(node, ":text");
5388:	}
5389:	
5390:	/**
5391:	 * Gets the number of tags in a tag by a specified tagname 
5392:	 *
5393:	 * @param node a specified tag
5394:	 * @param key the searched tagname
5395:	 * @return the number of tags
5396:	 */
5397:	protected static int getDOMCount(Object node, String key) {
5398:		return getItemCountImpl(node, key.intern());
5399:	}
5400:	
5401:	/**
5402:	 * Gets the subtag of the specified tag by tagname and index 
5403:	 *
5404:	 * @param node a specified tag
5405:	 * @param key the searched tagname
5406:	 * @param index the index of the requested subtag
5407:	 * @return the found tag, or null
5408:	 */
5409:	protected static Object getDOMNode(Object node, String key, int index) {
5410:		return getItemImpl(node, key.intern(), index);
5411:	}
5412:	
5413:	/**
5414:	 * Set a bundle used in parse method, it replaces the parameter values starting
5415:	 * with the 'i18n.' string with a value found in the given bundle
5416:	 * @param resourcebundle a bundle for the next parsing or null to remove
5417:	 * the current one
5418:	 * @throws MissingResourceException if no object for the given key can be found
5419:	 */
5420:	public void setResourceBundle(ResourceBundle resourcebundle) {
5421:		this .resourcebundle = resourcebundle;
5422:	}
5423:
5424:	/**
5425:	 *
5426:	 * @param inputstream
5427:	 * @param validate parse GUI from xml if true
5428:	 * @param dom parse an xml resoource
5429:	 * @param handler
5430:	 * @return
5431:	 * @throws java.io.IOException
5432:	 * @throws java.lang.IllegalArgumentException
5433:	 */
5434:	private Object parse(InputStream inputstream,
5435:			boolean validate, boolean dom, Object handler) throws IOException {
5436:		Reader reader = new BufferedReader(new InputStreamReader(inputstream));
5437:		try {
5438:			Object[] parentlist = null;
5439:			Object current = null;
5440:			Hashtable attributelist = null;
5441:			Vector methods = (validate && !dom) ? new Vector() : null;
5442:			StringBuffer text = new StringBuffer();
5443:			for (int c = reader.read(); c != -1;) {
5444:				if (c == '<') {
5445:					if ((c = reader.read()) == '/') { //endtag
5446:						if (text.length() > 0) {
5447:							if (text.charAt(text.length() - 1) == ' ') {
5448:								text.setLength(text.length() - 1);
5449:							}
5450:							if (!validate) {
5451:								if (dom) {
5452:									set(current, ":text", text.toString());
5453:								} else {
5454:									characters(text.toString());
5455:								}
5456:							}
5457:							// else {
5458:								//addContent(current, text.toString());
5459:							//}
5460:							text.setLength(0);
5461:						}
5462:						String tagname = (String) parentlist[2]; //getClass(current);
5463:						for (int i = 0; i < tagname.length(); i++) { // current-tag
5464:							if ((c = reader.read()) != tagname.charAt(i)) {
5465:								throw new IllegalArgumentException(tagname);
5466:							}
5467:						}
5468:						while (" \t\n\r".indexOf(c = reader.read()) != -1); // whitespace
5469:						if (c != '>') throw new IllegalArgumentException(); // '>'
5470:						c = reader.read();
5471:						if (!validate && !dom) { endElement(); }
5472:						if (parentlist[0] == null) {
5473:							reader.close();
5474:							finishParse(methods, current, handler);
5475:							return current;
5476:						}
5477:						current = parentlist[0];
5478:						parentlist = (Object[]) parentlist[1];
5479:					}
5480:					else if (c == '!') { // DOCTYPE
5481:						while ((c = reader.read()) != '>'); //+(-1)
5482:					}
5483:					else if (c == '?') { // Processing Instructions
5484:						boolean question = false; // read until '?>'
5485:						while (((c = reader.read()) != '>') || !question) { question = (c == '?'); }
5486:					}
5487:					else { //start or standalone tag
5488:						text.setLength(0);
5489:						boolean iscomment = false;
5490:						while (">/ \t\n\r".indexOf(c) == -1) {
5491:							text.append((char) c);
5492:							if ((text.length() == 3) && (text.charAt(0) == '!') &&
5493:									(text.charAt(1) == '-') && (text.charAt(2) == '-')) {
5494:								int m = 0;
5495:								while (true) {
5496:									c = reader.read();
5497:									if (c == '-') { m++; }
5498:									else if ((c == '>') && (m >= 2)) { break; }
5499:									else { m = 0; }
5500:								}
5501:								iscomment = true;
5502:							}
5503:							c = reader.read();
5504:						}
5505:						if (iscomment) { continue; }
5506:						String tagname = text.toString();
5507:						parentlist = new Object[] { current, parentlist, tagname };
5508:						if (validate) {
5509:							current = (current != null) ?
5510:								addElement(current, tagname) : create(tagname);
5511:						} else {
5512:							if (dom) {
5513:								Object parent = current;
5514:								current = createImpl(tagname = tagname.intern());
5515:								if (parent != null) {
5516:									insertItem(parent, tagname, current, -1);
5517:									//set(current, ":parent", parent);
5518:								}
5519:							} else {
5520:								current = tagname;
5521:							}
5522:						}
5523:						text.setLength(0);
5524:						while (true) {
5525:							boolean whitespace = false;
5526:							while (" \t\n\r".indexOf(c) != -1) {
5527:								c = reader.read();
5528:								whitespace = true;
5529:							}
5530:							if (c == '>') {
5531:								if (!validate && !dom) {
5532:									startElement((String) current, attributelist); attributelist = null;
5533:								}
5534:								c = reader.read();
5535:								break;
5536:							}
5537:							else if (c == '/') {
5538:								if ((c = reader.read()) != '>') {
5539:									throw new IllegalArgumentException(); // '>'
5540:								}
5541:								if (!validate && !dom) {
5542:									startElement((String) current, attributelist); attributelist = null;
5543:									endElement();
5544:								}
5545:								if (parentlist[0] == null) {
5546:									reader.close();
5547:									finishParse(methods, current, handler);
5548:									return current;
5549:								}
5550:								current = parentlist[0];
5551:								parentlist = (Object[]) parentlist[1];
5552:								c = reader.read();
5553:								break;
5554:							}
5555:							else if (whitespace) {
5556:								while ("= \t\n\r".indexOf(c) == -1) {
5557:									text.append((char) c);
5558:									c = reader.read();
5559:								}
5560:								String key = text.toString();
5561:								text.setLength(0);
5562:								while (" \t\n\r".indexOf(c) != -1) c = reader.read();
5563:								if (c != '=') throw new IllegalArgumentException();
5564:								while (" \t\n\r".indexOf(c = reader.read()) != -1);
5565:								char quote = (char) c;
5566:								if ((c != '\"') && (c != '\'')) throw new IllegalArgumentException();
5567:								while (quote != (c = reader.read())) {
5568:									if (c == '&') {
5569:										StringBuffer eb = new StringBuffer();
5570:										while (';' != (c = reader.read())) { eb.append((char) c); }
5571:										String entity = eb.toString();
5572:										if ("lt".equals(entity)) { text.append('<'); }
5573:										else if ("gt".equals(entity)) { text.append('>'); }
5574:										else if ("amp".equals(entity)) { text.append('&'); }
5575:										else if ("quot".equals(entity)) { text.append('"'); }
5576:										else if ("apos".equals(entity)) { text.append('\''); }
5577:										else if (entity.startsWith("#")) {
5578:											boolean hexa = (entity.charAt(1) == 'x');
5579:											text.append((char) Integer.parseInt(entity.substring(hexa ? 2 : 1), hexa ? 16 : 10));
5580:										}
5581:										else throw new IllegalArgumentException("unknown " + "entity " + entity);
5582:									}
5583:									else text.append((char) c);
5584:								}
5585:								if (validate) {
5586:									addAttribute(current, key, text.toString(), methods);
5587:								} else {
5588:									if (dom) {
5589:										set(current, key.intern(), text.toString());
5590:									} else {
5591:										if (attributelist == null) { attributelist = new Hashtable(); }
5592:										attributelist.put(key, text.toString());
5593:									}
5594:								}
5595:								//'<![CDATA[' ']]>'
5596:								text.setLength(0);
5597:								c = reader.read();
5598:							}
5599:							else throw new IllegalArgumentException();
5600:						}
5601:					}
5602:				}
5603:				else {
5604:					if (" \t\n\r".indexOf(c) != -1) {
5605:						if ((text.length() > 0) && (text.charAt(text.length() - 1) != ' ')) {
5606:							text.append(' ');
5607:						}
5608:					}
5609:					else {
5610:						text.append((char) c);
5611:					}
5612:					c = reader.read();
5613:				} 
5614:			}
5615:			throw new IllegalArgumentException();
5616:		}
5617:		finally {
5618:			if (reader != null) { reader.close(); }
5619:		}
5620:	}
5621:	
5622:	/**
5623:	 *
5624:	 */
5625:	private void finishParse(Vector methods, Object root, Object handler) {
5626:		if (methods != null) {
5627:			for (int i = 0; i < methods.size(); i += 3) {
5628:				Object component = methods.elementAt(i);
5629:				Object[] definition = (Object[]) methods.elementAt(i + 1);
5630:				String value = (String) methods.elementAt(i + 2);
5631:				
5632:				if ("method" == definition[0]) {
5633:					Object[] method = getMethod(component, value, root, handler);
5634:					if ("init" == definition[1]) {
5635:						invokeImpl(method, component, null);
5636:					}
5637:					else {
5638:						set(component, definition[1], method);
5639:					}
5640:				}
5641:				else { // ("component" == definition[0])
5642:					Object reference = find(root, value); //+start find from the component
5643:					if (reference == null) throw new IllegalArgumentException(value + " not found"); 
5644:					set(component, definition[1], reference);
5645:				}
5646:			}
5647:		}
5648:	}
5649:
5650:	/**
5651:	 * Add the component to the parent's ':comp' list, and set its ':parent'
5652:	 * or set single components
5653:	 *
5654:	 * @param index add at the specified index
5655:	 * @throws java.lang.IllegalArgumentException
5656:	 */
5657:	private void addImpl(Object parent, Object component, int index) {
5658:		String parentclass = getClass(parent);
5659:		String classname = getClass(component);
5660:		if ((("combobox" == parentclass) && ("choice" == classname)) ||
5661:				(("tabbedpane" == parentclass) && ("tab" == classname)) ||
5662:				(("list" == parentclass) && ("item" == classname)) ||
5663:				(("table" == parentclass) && ("row" == classname)) ||
5664:				(("header" == parentclass) && ("column" == classname)) ||
5665:				(("row" == parentclass) && ("cell" == classname)) ||
5666:				((("tree" == parentclass) || ("node" == parentclass)) && ("node" == classname)) ||
5667:				(("menubar" == parentclass) && ("menu" == classname)) ||
5668:				((("menu" == parentclass) || ("popupmenu" == parentclass)) &&
5669:					(("menu" == classname) || ("menuitem" == classname) ||
5670:					("checkboxmenuitem" == classname) || ("separator" == classname))) ||
5671:				((("panel" == parentclass) || ("desktop" == parentclass) ||
5672:					("splitpane" == parentclass) || ("dialog" == parentclass) ||
5673:					("tab" == parentclass)) && instance(classname, "component") &&
5674:						(classname != "popupmenu"))) {
5675:			insertItem(parent, ":comp", component, index);
5676:			set(component, ":parent", parent);
5677:		}
5678:		else if ((("table" == parentclass) && ("header" == classname)) ||
5679:				(("popupmenu" == classname) && instance(parentclass, "component"))) {
5680:			set(parent, classname, component);
5681:			set(component, ":parent", parent);
5682:		}
5683:		else throw new IllegalArgumentException(classname + " add " + parentclass);
5684:	}
5685:	
5686:	/**
5687:	 *
5688:	 */
5689:	private boolean instance(Object classname, Object extendclass) {
5690:		if (classname == extendclass) { return true; }
5691:		for (int i = 0; i < dtd.length; i += 3) {
5692:				if (classname == dtd[i]) {
5693:					return instance(dtd[i + 1], extendclass);
5694:				}
5695:		}
5696:		return false;
5697:	}
5698:
5699:	/**
5700:	 *
5701:	 */
5702:	private Object addElement(Object parent, String name) {
5703:		Object component = create(name);
5704:		addImpl(parent, component, -1);
5705:		return component;
5706:	}
5707:
5708:	/**
5709:	 *
5710:	 * @throws java.lang.IllegalArgumentException
5711:	 */
5712:	private void addAttribute(Object component, String key, String value, Vector lasts) {
5713:		// replace value found in the bundle
5714:		if ((resourcebundle != null) && value.startsWith("i18n.")) {
5715:			value = resourcebundle.getString(value.substring(5));
5716:		}
5717:		
5718:		Object[] definition = getDefinition(getClass(component), key, null);
5719:		key = (String) definition[1];
5720:		if ("string" == definition[0]) {
5721:			setString(component, key, value, (String) definition[3]);
5722:		}
5723:		else if ("choice" == definition[0]) {
5724:			String[] values = (String[]) definition[3];
5725:			setChoice(component, key, value, values, values[0]);
5726:		}
5727:		else if ("boolean" == definition[0]) {
5728:			if ("true".equals(value)) {
5729:				if (definition[3] == Boolean.FALSE) {
5730:					set(component, key, Boolean.TRUE);
5731:				}
5732:			}
5733:			else if ("false".equals(value)) {
5734:				if (definition[3] == Boolean.TRUE) {
5735:					set(component, key, Boolean.FALSE);
5736:				}
5737:			}
5738:			else throw new IllegalArgumentException(value);
5739:		}
5740:		else if ("integer" == definition[0]) {
5741:			set(component, key, Integer.valueOf(value));
5742:		}
5743:		else if ("icon" == definition[0]) {
5744:			set(component, key, getIcon(value));
5745:		}
5746:		else if (("method" == definition[0]) || ("component" == definition[0])) {
5747:			lasts.addElement(component);
5748:			lasts.addElement(definition);
5749:			lasts.addElement(value);
5750:		}
5751:		else if ("property" == definition[0]) {
5752:			StringTokenizer st = new StringTokenizer(value, ";");
5753:			while (st.hasMoreTokens()) {
5754:				String token = st.nextToken();
5755:				int equals = token.indexOf('=');
5756:				if (equals == -1) { throw new IllegalArgumentException(token); }
5757:				putProperty(component, token.substring(0, equals), token.substring(equals + 1));
5758:			}
5759:		}
5760:		else if ("font" == definition[0]) {
5761:			String name = null;
5762:			boolean bold = false; boolean italic = false;
5763:			int size = 0;
5764:			StringTokenizer st = new StringTokenizer(value);
5765:			while (st.hasMoreTokens()) {
5766:				String token = st.nextToken();
5767:				if ("bold".equalsIgnoreCase(token)) { bold = true; }
5768:				else if ("italic".equalsIgnoreCase(token)) { italic = true; }
5769:				else {
5770:					try {
5771:						size = Integer.parseInt(token);
5772:					} catch (NumberFormatException nfe) {
5773:						name = (name == null) ? token : (name + " " + token);
5774:					}
5775:				}
5776:			}
5777:			if (name == null) { name = font.getName(); }
5778:			if (size == 0) { size = font.getSize(); }
5779:			set(component, key, new Font(name,
5780:				(bold ? Font.BOLD : 0) | (italic ? Font.ITALIC : 0), size));
5781:		}
5782:		else if ("color" == definition[0]) {
5783:			int color = 0;
5784:			if (value.startsWith("#")) { color = Integer.parseInt(value.substring(1), 16); }
5785:			else if (value.startsWith("0x")) { color = Integer.parseInt(value.substring(2), 16); }
5786:			else { // three separated integer including red, green, and blue
5787:				StringTokenizer st = new StringTokenizer(value, " \r\n\t,");
5788:				color = 0xff000000 | ((Integer.parseInt(st.nextToken()) & 0xff) << 16) |
5789:					((Integer.parseInt(st.nextToken()) & 0xff) << 8) |
5790:					(Integer.parseInt(st.nextToken()) & 0xff);
5791:			}				
5792:			set(component, key, new Color(color));
5793:		}
5794:		else if ("keystroke" == definition[0]) {
5795:			setKeystrokeImpl(component, key, value);
5796:		}
5797:		else if ("bean" == definition[0]) {
5798:			try {
5799:				set(component, key, (Component) Class.forName(value).newInstance());
5800:			} catch (Exception exc) { throw new IllegalArgumentException(value); }
5801:		}
5802:		else throw new IllegalArgumentException((String) definition[0]);
5803:	}
5804:
5805:	/**
5806:	 *
5807:	 * @throws java.lang.IllegalArgumentException
5808:	 */
5809:	private static Object[] getDefinition(Object classname, String key, String type) {
5810:		Object currentname = classname;
5811:		while (classname != null) {
5812:			for (int i = 0; i < dtd.length; i += 3) {
5813:				if (dtd[i] == classname) {
5814:					Object[][] attributes = (Object[][]) dtd[i + 2];
5815:					if (attributes != null) {
5816:						for (int j = 0; j < attributes.length; j++) {
5817:							if (attributes[j][1].equals(key)) {
5818:								if ((type != null) && (type != attributes[j][0])) {
5819:									throw new IllegalArgumentException(attributes[j][0].toString());
5820:								}
5821:								return attributes[j];
5822:							}
5823:						}
5824:					}
5825:					classname = dtd[i + 1];
5826:					break;
5827:				}
5828:			}
5829:		}
5830:		throw new IllegalArgumentException("unknown " + key + " " + type +
5831:			" for " + currentname);
5832:	}
5833:
5834:	// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
5835:
5836:	/**
5837:	 * Sets the given property pair (key and value) for the component
5838:	 */
5839:	public void setString(Object component, String key, String value) {
5840:		Object[] definition = getDefinition(getClass(component), key, "string");
5841:		if (setString(component, (String) definition[1],
5842:				value, (String) definition[3])) {
5843:			update(component, definition[2]);
5844:		}
5845:	}
5846:
5847:	/**
5848:	 * Gets the property value of the given component by the property key
5849:	 */
5850:	public String getString(Object component, String key) {
5851:		return (String) get(component, key, "string");
5852:		
5853:	}
5854:
5855:	/**
5856:	 * Sets the given property pair (key and value) for the component
5857:	 */
5858:	public void setChoice(Object component, String key, String value) {
5859:		Object[] definition = getDefinition(getClass(component), key, "choice");
5860:		String[] values = (String[]) definition[3];
5861:		if (setChoice(component, (String) definition[1],
5862:				value, values, values[0])) {
5863:			update(component, definition[2]);
5864:		}
5865:	}
5866:
5867:	/**
5868:	 * Gets the property value of the given component by the property key
5869:	 */
5870:	public String getChoice(Object component, String key) {
5871:		Object[] definition = getDefinition(getClass(component), key, "choice");
5872:		return getString(component, (String) definition[1],
5873:			((String[]) definition[3])[0]);
5874:	}
5875:
5876:	/**
5877:	 * Sets the given property pair (key and value) for the component
5878:	 */
5879:	public void setBoolean(Object component, String key, boolean value) {
5880:		Object[] definition = getDefinition(getClass(component), key, "boolean");
5881:		if (setBoolean(component, (String) definition[1],
5882:				value, (definition[3] == Boolean.TRUE))) {
5883:			update(component, definition[2]);
5884:		}
5885:	}
5886:
5887:	/**
5888:	 * Gets the property value of the given component by the property key
5889:	 */
5890:	public boolean getBoolean(Object component, String key) {
5891:		return get(component, key, "boolean") == Boolean.TRUE;
5892:	}
5893:
5894:	/**
5895:	 * Sets the given property pair (key and value) for the component
5896:	 */
5897:	public void setInteger(Object component, String key, int value) {
5898:		Object[] definition = getDefinition(getClass(component), key, "integer");
5899:		if (setInteger(component, (String) definition[1],
5900:				value, ((Integer) definition[3]).intValue())) {
5901:			update(component, definition[2]);
5902:		}
5903:	}
5904:
5905:	/**
5906:	 * Gets the property value of the given component by the property key
5907:	 */
5908:	public int getInteger(Object component, String key) {
5909:		return ((Integer) get(component, key, "integer")).intValue();
5910:	}
5911:
5912:	/**
5913:	 * Sets the given property pair (key and value) for the component
5914:	 */
5915:	public void setIcon(Object component, String key, Image icon) {
5916:		Object[] definition = getDefinition(getClass(component), key, "icon");
5917:		if (set(component, (String) definition[1], icon)) {
5918:			update(component, definition[2]);
5919:		}
5920:	}
5921:
5922:	/**
5923:	 * Gets the property value of the given component by the property key
5924:	 */
5925:	public Image getIcon(Object component, String key) {
5926:		return (Image) get(component, key, "icon");
5927:	}
5928:	
5929:	/**
5930:	 *
5931:	 */
5932:	public void setKeystroke(Object component, String key, String value) {
5933:		Object[] definition = getDefinition(getClass(component), key, "keystroke");
5934:		// TODO check if changed
5935:		setKeystrokeImpl(component, (String) definition[1], value);
5936:		update(component, definition[2]);
5937:	}
5938:
5939:	/**
5940:	 * Set custom font on a component, 
5941:	 * use the other <code>setFont</code> method instead
5942:	 */
5943:	public void setFont(Object component, Font font) { // deprecated
5944:		setFont(component, "font", font);
5945:	}
5946:	
5947:	/**
5948:	 * Set custom font on a component
5949:	 *
5950:	 * @param component component to use the custom font
5951:	 * @param font custom font to use, or null to reset component to use default font
5952:	 */
5953:	public void setFont(Object component, String key, Font font) {
5954:		Object[] definition = getDefinition(getClass(component), key, "font");
5955:		if (set(component, (String) definition[1], font)) {
5956:			update(component, definition[2]);
5957:		}
5958:	}
5959:
5960:	/**
5961:	 * Set custom color on a component.
5962:	 * Notes: For "foreground" key, this sets the text color.
5963:	 * For "background" key, on gradient-filled
5964:	 * components (such as tabs, buttons etc) this will result in a 
5965:	 * component filled with solid background color, and not a new gradient.
5966:	 * Also, Color.brighter() will be used for highlight, and Color.darker()
5967:	 * will be used for pressed or not selected.
5968:	 *
5969:	 * @param component component to use for custom color
5970:	 * @param key currently "background" and "foreground" are supported
5971:	 * @param color custom color to use, or null to reset component to use default color
5972:	 */
5973:	public void setColor(Object component, String key, Color color) {
5974:		Object[] definition = getDefinition(getClass(component), key, "color");
5975:		if (set(component, (String) definition[1], color)) {
5976:			update(component, definition[2]);
5977:		}
5978:	}
5979:	
5980:	/**
5981:	 *
5982:	 */
5983:	private void setKeystrokeImpl(Object component, String key, String value) {
5984:		Long keystroke = null;
5985:		if (value != null) {
5986:			String token = value;
5987:			try {
5988:				int keycode = 0, modifiers = 0;
5989:				StringTokenizer st = new StringTokenizer(value, " \r\n\t+");
5990:				while (st.hasMoreTokens()) {
5991:					token = st.nextToken().toUpperCase();
5992:					try {
5993:							modifiers = modifiers | InputEvent.class.getField(token + "_MASK").getInt(null);
5994:					} catch (Exception exc) { // not mask value
5995:						keycode = KeyEvent.class.getField("VK_" + token).getInt(null);
5996:					}
5997:				}
5998:				keystroke = new Long(((long) modifiers) << 32 | keycode);
5999:			} catch (Exception exc) { throw new IllegalArgumentException(token); }
6000:		}
6001:		set(component, key, keystroke);
6002:	}
6003:	
6004:	//TODO add set/getComponent for popupmenu and header
6005:	
6006:	/**
6007:	 *
6008:	 */
6009:	public Object getWidget(Object component, String key) {
6010:		if ("popupmenu".equals(key)) { return get(component, "popupmenu");}
6011:		else if ("header".equals(key)) { return get(component, "header");}
6012:		else throw new IllegalArgumentException(key);
6013:	}
6014:	
6015:	/**
6016:	 *
6017:	 */
6018:	private static Object get(Object component, String key, String type) {
6019:		Object[] definition = getDefinition(getClass(component), key, type);
6020:		Object value = get(component, (String) definition[1]);
6021:		return (value != null) ? value : definition[3];
6022:	}
6023:	
6024:	/**
6025:	 * Sets a new event handler method for a component
6026:	 *
6027:	 * @param component the target component
6028:	 * @param key the key name of the parameter (e.g. <i>action</i>)
6029:	 * @param value the method name and parameters
6030:	 * (e.g. <i>foo(this, this.text, mybutton, mybutton.enabled)</i>
6031:	 * for <i>public void foo(Object component, String text, Object mybutton, boolean enabled)</i>)
6032:	 * @param root the search starting component for name components in the arguments
6033:	 * @param handler the target event handler object including the method
6034:	 * @throws java.lang.IllegalArgumentException
6035:	 */
6036:	public void setMethod(Object component, String key, String value, Object root, Object handler) {
6037:		key = (String) getDefinition(getClass(component), key, "method")[1];
6038:		Object[] method = getMethod(component, value, root, handler);
6039:		set(component, key, method);
6040:	}
6041:	
6042:	/**
6043:	 * @return an object list including as follows:
6044:	 * - handler object,
6045:	 * - method,
6046:	 * - list of parameters including 3 values:
6047:	 * - ("thinlet", null, null) for the single thinlet component,
6048:	 * - (target component, null, null) for named widget as parameter, e.g. mybutton,
6049:	 * - (target, parameter name, default value) for a widget's given property, e.g. mylabel.enabled,
6050:	 * - ("item", null, null) for an item of the target component as parameter, e.g. tree node,
6051:	 * - ("item", parameter name, default value) for the item's given property e.g. list item's text.
6052:	 */
6053:	private Object[] getMethod(Object component, String value, Object root, Object handler) {
6054:		StringTokenizer st = new StringTokenizer(value, "(, \r\n\t)");
6055:		String methodname = st.nextToken();
6056:		int n = st.countTokens();
6057:		Object[] data = new Object[2 + 3 * n];
6058:		Class[] parametertypes = (n > 0) ? new Class[n] : null;
6059:		for (int i = 0; i < n; i++) {
6060:			String arg = st.nextToken();
6061:			if ("thinlet".equals(arg)) {
6062:				data[2 + 3 * i] = "thinlet"; // the target component
6063:				parametertypes[i] = Thinlet.class;
6064:			}
6065:			else {
6066:				int dot = arg.indexOf('.');
6067:				String compname = (dot == -1) ? arg : arg.substring(0, dot);
6068:				Object comp = null;
6069:				String classname = null;
6070:				if ("item".equals(compname)) {
6071:					comp = "item";
6072:					String parentclass = getClass(component);
6073:					if ("list" == parentclass) { classname = "item"; }
6074:					else if ("tree" == parentclass) { classname = "node"; }
6075:					else if ("table" == parentclass) { classname = "row"; }
6076:					else if ("combobox" == parentclass) { classname = "choice"; }
6077:					else if ("tabbedpane" == parentclass) { classname = "tab"; }
6078:					else throw new IllegalArgumentException(parentclass + " has no item");
6079:				}
6080:				else {
6081:					comp = ("this".equals(compname)) ? component : find(root, compname);
6082:					classname = getClass(comp);
6083:				}
6084:				data[2 + 3 * i] = comp; // the target component
6085:				if (dot == -1) {
6086:					parametertypes[i] = Object.class; // Widget.class
6087:				}
6088:				else {
6089:					Object[] definition = getDefinition(classname, arg.substring(dot + 1), null);
6090:					data[2 + 3 * i + 1] = definition[1]; // parameter name, e.g. enabled
6091:					data[2 + 3 * i + 2] = definition[3]; // default value, e.g. Boolean.TRUE
6092:					Object fieldclass = definition[0];
6093:					if ((fieldclass == "string") || (fieldclass == "choice")) {
6094:						parametertypes[i] = String.class;
6095:					}
6096:					else if (fieldclass == "boolean") {
6097:						parametertypes[i] = Boolean.TYPE;
6098:					}
6099:					else if (fieldclass == "integer") {
6100:						parametertypes[i] = Integer.TYPE;
6101:					}
6102:					else if (fieldclass == "icon") {
6103:						parametertypes[i] = Image.class;
6104:					}
6105:					else throw new IllegalArgumentException((String) fieldclass);
6106:				}
6107:			}
6108:		}
6109:		data[0] = handler;
6110:		try {
6111:			data[1] = handler.getClass().getMethod(methodname, parametertypes);
6112:			return data;
6113:		} catch (Exception exc) {
6114:			throw new IllegalArgumentException(value + " " + exc.getMessage());
6115:		}
6116:	}
6117:
6118:	/**
6119:	 *
6120:	 */
6121:	private void update(Object component, Object mode) {
6122:		if ("parent" == mode) {
6123:			component = getParent(component);
6124:			mode = "validate";
6125:		}
6126:		boolean firstpaint = true;
6127:		int x = 0; int y = 0; int width = 0; int height = 0;
6128:		while (component != null) {
6129:			if (!getBoolean(component, "visible", true)) { break; }
6130:			if ("paint" == mode) {//|| (firstpaint && (component == content))
6131:				Rectangle bounds = getRectangle(component, "bounds");
6132:				if (bounds == null) { return; }
6133:				if (firstpaint) {
6134:					x = bounds.x; y = bounds.y;
6135:					width = Math.abs(bounds.width); height = bounds.height;
6136:					firstpaint = false;
6137:				} else {
6138:					x += bounds.x; y += bounds.y;
6139:				}
6140:				if (component == content) {
6141:					repaint(x, y, width, height);
6142:				}
6143:			}
6144:			Object parent = getParent(component);
6145:			String classname = getClass(parent);
6146:			if ("combobox" == classname) {
6147:				parent = get(parent, ":combolist");
6148:			}
6149:			else if ("menu" == classname) {
6150:				parent = get(parent, ":popup");
6151:			}
6152:			else if (("paint" == mode) && ("tabbedpane" == classname)) {
6153:				if (getItem(parent, getInteger(parent, "selected", 0)) != component) { break; }
6154:			}
6155:			if (("layout" == mode) || (("validate" == mode) &&
6156:					(("list" == classname) || ("table" == classname) ||
6157:					("tree" == classname) || ("dialog" == classname) || (parent == content)))) {
6158:				Rectangle bounds = getRectangle(parent, "bounds");
6159:				if (bounds == null) { return; }
6160:				bounds.width = -1 * Math.abs(bounds.width);
6161:				mode = "paint";
6162:			}
6163:			component = parent;
6164:		}
6165:	}
6166:
6167:	// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
6168:
6169:	/**
6170:	 *
6171:	 */
6172:	private boolean setString(Object component,
6173:			String key, String value, String defaultvalue) {
6174:		if (allI18n && (langResource != null) &&
6175:				((key == "text") || (key == "tooltip"))) {
6176:			putProperty(component, "i18n." + key, null); // for I18N
6177:		}
6178:		return set(component, key, value);
6179:	}
6180:
6181:	/**
6182:	 *
6183:	 */
6184:	private String getString(Object component,
6185:			String key, String defaultvalue) {
6186:		Object value = get(component, key);
6187:		return (value == null) ? defaultvalue :
6188:			getI18NString(component, key, (String) value);
6189:	}
6190:	
6191:	/**
6192:	 * Set current language resource bundle. This flushes all cached translated values, performs
6193:	 * lazy loading of new values, and repaints the desktop. This implementation allows applications to switch
6194:	 * language resources on the fly, without rebuilding/reloading components that use them.
6195:	 * <br />The pseudo-code is as follows:
6196:	 * 
6197:	 * <ul><li>if langResource and langResourceDefault are null, don't
6198:	 * translate anything, no matter what other settings are. This
6199:	 * behaviour provides compatibility with previous versions.</li>
6200:	 * <li>if only langResourceDefault is set, use this when translation is required</li>
6201:	 * <li>if allI18n is set to true:
6202:	 * 	<ul><li>if property "i18n" on a component is missing,
6203:	 * 	or set to "true", translate</li>
6204:	 * 	<li>if property "i18n" is present, and set to "false",
6205:	 * 	do not translate</li></ul></li>
6206:	 * <li>if allI18n is set to false:
6207:	 * 	<ul><li>if property "i18n" on a component is missing,
6208:	 * 	or set to "false", do not translate</li>
6209:	 * 	<li>if property "i18n" is present, and set to "true", translate</li>
6210:	 * 	</ul></li></ul>
6211:	 * 
6212:	 * The "translate" step is applied only to values from "text"
6213:	 * and "tooltip" properties (for now), and is applied as follows:
6214:	 * 
6215:	 * <ul><li>use the value of "text" or "tooltip" as a lookup key</li>
6216:	 * <li>use langResource to lookup the result value
6217:	 * 	<ul><li>if no value is found, use langResourceDefault for lookup
6218:	 * 		<ul><li>if no value is found, just return the original value of
6219:	 * 		the property. Set a flag on component that prevents
6220:	 * 		lookups in the future. This flag is cleared when langResource is changed.</li>
6221:	 * 		</ul></li></ul></li>
6222:	 * <li>cache the result value, if any</li></ul>
6223:	 * 
6224:	 * If translated value is found successfully, it is cached in the
6225:	 * component. This cache is gradually flushed when setLangResource
6226:	 * is called. Cached value is also flushed when setString() is
6227:	 * called on a component.
6228:	 * 
6229:	 * @param res resource bundle containing localized texts for "text" and "tooltip"
6230:	 */
6231:	public void setLangResource(ResourceBundle res) { // for I18N
6232:		langResource = res;
6233:		doLayout(content);
6234:		repaint(content);
6235:	}
6236:
6237:	/**
6238:	 * Returns language resource bundle currently in use, or default bundle, or null.
6239:	 */
6240:	public static ResourceBundle getLangResource() { // for I18N
6241:		return langResource;
6242:	}
6243:
6244:	/**
6245:	 * Set default language resource bundle. Resources from this bundle will be used if
6246:	 * they are missing in the current bundle.
6247:	 *
6248:	 * @param res resource bundle containing default localized texts for "text" and "tooltip"
6249:	 */
6250:	public void setLangResourceDefault(ResourceBundle res) { // for I18N
6251:		langResourceDefault = res;
6252:		if (langResource == null) setLangResource(res);
6253:	}
6254:
6255:	/**
6256:	 * Returns default language resource bundle, or null.
6257:	 */
6258:	public static ResourceBundle getLangResourceDefault() { // for I18N
6259:		return langResourceDefault;
6260:	}
6261:
6262:	/**
6263:	 * Sets the default behaviour of internationalization code. If set to "true", try to translate
6264:	 * all components' "text" and "tooltip" values, unless explicitly prohibited by setting
6265:	 * <code>i18n="false"</code> on a specific component. If set to "false", do not translate
6266:	 * unless explicitly requested by setting <code>i18n="true"</code> on a specific component.
6267:	 * <br />Default value is "false", to provide backwards compatibility.
6268:	 *
6269:	 *@param val if "true", translate by default; if "false", do not translate by default.
6270:	 */
6271:	public void setAllI18n(boolean val) { // for I18N
6272:		allI18n = val;
6273:	}
6274:	
6275:	/**
6276:	 *
6277:	 */
6278:	private String getI18NString(Object component, String key, String text) { // for I18N
6279:		if (allI18n && (langResource != null) &&
6280:				((key == "text") || (key == "tooltip")) &&
6281:				getBoolean(component, "i18n", true)) {
6282:			String ikey = (String) getProperty(component, "i18n." + key);
6283:			if (!"__NONE__".equals(ikey)) {
6284:				if (ikey == null) { // initialize
6285:					putProperty(component, "i18n." + key, ikey = text);
6286:				}
6287:				try {
6288:					return langResource.getString(ikey);
6289:				} catch (Exception exc) { // not found. Try default
6290:					if (langResourceDefault != null) {
6291:						try {
6292:							return langResourceDefault.getString(ikey);
6293:						} catch (Exception dexc) {
6294:							putProperty(component, "i18n." + key, "__NONE__");
6295:						}
6296:					}
6297:				}
6298:			}
6299:		}
6300:		return text;
6301:	}
6302:	
6303:	/**
6304:	 *
6305:	 * @throws java.lang.IllegalArgumentException
6306:	 */
6307:	private boolean setChoice(Object component,
6308:			String key, String value, String[] values, String defaultvalue) {
6309:		if (value == null) {
6310:			return set(component, key, defaultvalue);
6311:		}
6312:		for (int i = 0; i < values.length; i++) {
6313:			if (value.equals(values[i])) {
6314:				return set(component, key, values[i]);
6315:			}
6316:		}
6317:		throw new IllegalArgumentException("unknown " + value + " for " + key);
6318:	}
6319:
6320:	/**
6321:	 *
6322:	 */
6323:	private boolean setIcon(Object component,
6324:			String key, String path, Image defaultvalue) {
6325:		return set(component, key, (path != null) ? getIcon(path) : defaultvalue);
6326:	}
6327:
6328:	/**
6329:	 *
6330:	 */
6331:	private Image getIcon(Object component, String key, Image defaultvalue) {
6332:		Object value = get(component, key);
6333:		return (value == null) ? defaultvalue : (Image) value;
6334:	}
6335:
6336:	/**
6337:	 *
6338:	 */
6339:	private boolean setBoolean(Object component,
6340:			String key, boolean value, boolean defaultvalue) {
6341:		return set(component, key, (value == defaultvalue) ? null :
6342:			(value ? Boolean.TRUE : Boolean.FALSE));
6343:	}
6344:
6345:	/**
6346:	 *
6347:	 */
6348:	private boolean getBoolean(Object component, 
6349:			String key, boolean defaultvalue) {
6350:		Object value = get(component, key);
6351:		return (value == null) ? defaultvalue : ((Boolean) value).booleanValue();
6352:	}
6353:
6354:	/**
6355:	 *
6356:	 */
6357:	private boolean setInteger(Object component,
6358:			String key, int value, int defaultvalue) {
6359:		return set(component, key, (value == defaultvalue) ? null : new Integer(value));
6360:	}
6361:
6362:	/**
6363:	 *
6364:	 */
6365:	private int getInteger(Object component, String key, int defaultvalue) {
6366:		Object value = get(component, key);
6367:		return (value == null) ? defaultvalue : ((Integer) value).intValue();
6368:	}
6369:
6370:	/**
6371:	 *
6372:	 */
6373:	private void setRectangle(Object component,
6374:			String key, int x, int y, int width, int height) {
6375:		Rectangle rectangle = getRectangle(component, key);
6376:		if (rectangle != null) {
6377:			rectangle.x = x; rectangle.y = y;
6378:			rectangle.width = width; rectangle.height = height;
6379:		}
6380:		else {
6381:			set(component, key, new Rectangle(x, y, width, height));
6382:		}
6383:	}
6384:
6385:	/**
6386:	 *
6387:	 */
6388:	private Rectangle getRectangle(Object component, String key) {
6389:		return (Rectangle) get(component, key);
6390:	}
6391:
6392:	// ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----
6393:
6394:	/**
6395:	 * Creates an image, and loads it immediately by default
6396:	 *
6397:	 * @param path is relative to your thinlet instance or the classpath
6398:	 * (if the path starts with <i>'/'</i> character), or a full URL
6399:	 * @return the loaded image or null
6400:	 */
6401:	public Image getIcon(String path) {
6402:		return getIcon(path, true);
6403:	}
6404:
6405:	/**
6406:	 * Creates an image from the specified resource.
6407:	 * To speed up loading the same images use a cache (a simple hashtable).
6408:	 * And flush the resources being used by an image when you won't use it henceforward
6409:	 *
6410:	 * @param path is relative to your thinlet instance or the classpath, or an URL
6411:	 * @param preload waits for the whole image if true, starts loading
6412:	 * (and repaints, and updates the layout) only when required (painted, or size requested) if false
6413:	 * @return the loaded image or null
6414:	 */
6415:	public Image getIcon(String path, boolean preload) {
6416:		if ((path == null) || (path.length() == 0)) {
6417:			return null;
6418:		}
6419:		Image image = null; //(Image) imagepool.get(path);
6420:		try {
6421:			URL url = getClass().getResource(path); //ClassLoader.getSystemResource(path)
6422:			if (url != null) { // contributed by Stefan Matthias Aust
6423:				image = Toolkit.getDefaultToolkit().getImage(url);
6424:			}
6425:		} catch (Throwable e) {}
6426:		if (image == null) {
6427:			try {
6428:				InputStream is = getClass().getResourceAsStream(path);
6429:				//InputStream is = ClassLoader.getSystemResourceAsStream(path);
6430:				if (is != null) {
6431:					byte[] data = new byte[is.available()];
6432:					is.read(data, 0, data.length);
6433:					image = getToolkit().createImage(data);
6434:					is.close();
6435:				}
6436:				else { // contributed by Wolf Paulus
6437:					image = Toolkit.getDefaultToolkit().getImage(new URL(path));
6438:				}
6439:			} catch (Throwable e) {}
6440:		}
6441:		if (preload && (image != null)) {
6442:			MediaTracker mediatracker = new MediaTracker(this );
6443:			mediatracker.addImage(image, 1);
6444:			try {
6445:				mediatracker.waitForID(1, 50);
6446:			} catch (InterruptedException ie) { }
6447:			//imagepool.put(path, image);
6448:		} 
6449:		return image;
6450:	}
6451:
6452:	/**
6453:	 * This method is called by the FrameLauncher if the window was closing,
6454:	 * or AppletLauncher's destroy method. Overwrite it to e.g. save the application changes.
6455:	 *
6456:	 * @return true to exit, and false to keep the frame and continue the application
6457:	 */
6458:	public boolean destroy() {
6459:		return true;
6460:	}
6461:
6462:	private static Object[] dtd;
6463:	static {
6464:		Integer integer_1 = new Integer(-1);
6465:		Integer integer0 = new Integer(0);
6466:		Integer integer1 = new Integer(1);
6467:		String[] orientation = { "horizontal", "vertical" };
6468:		String[] leftcenterright = { "left", "center", "right" };
6469:		String[] selections = { "single", "interval", "multiple" }; //+none
6470:		dtd = new Object[] {
6471:			"component", null, new Object[][] {
6472:				{ "string", "name", null, null },
6473:				{ "boolean", "enabled", "paint", Boolean.TRUE },
6474:				{ "boolean", "visible", "parent", Boolean.TRUE },
6475:				{ "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
6476:				{ "string", "tooltip", null, null },
6477:				{ "font", "font", "validate", null },
6478:				{ "color", "foreground", "paint", null },
6479:				{ "color", "background", "paint", null },
6480:				{ "integer", "width", "validate", integer0 },
6481:				{ "integer", "height", "validate", integer0 },
6482:				{ "integer", "colspan", "validate", integer1 },
6483:				{ "integer", "rowspan", "validate", integer1 },
6484:				{ "integer", "weightx", "validate", integer0 },
6485:				{ "integer", "weighty", "validate", integer0 },
6486:				{ "choice", "halign", "validate",
6487:					new String[] { "fill", "center", "left", "right" } },
6488:				{ "choice", "valign", "validate",
6489:				new String[] { "fill", "center", "top", "bottom" } },
6490:				// component class String null*
6491:				// parent Object null
6492:				// (bounds) Rectangle 0 0 0 0
6493:				{ "property", "property", null, null },
6494:				{ "method", "init" },
6495:				{ "method", "focuslost" },
6496:				{ "method", "focusgained" } },
6497:			"label", "component", new Object[][] {
6498:				{ "string", "text", "validate", null },
6499:				{ "icon", "icon", "validate", null },
6500:				{ "choice", "alignment", "validate", leftcenterright },
6501:				{ "integer", "mnemonic", "paint", integer_1 },
6502:				{ "component", "for", null, null } },
6503:			"button", "label", new Object[][] {
6504:				{ "choice", "alignment", "validate", new String[] { "center", "left", "right" } },
6505:				{ "method", "action" },
6506:				{ "choice", "type", "paint", new String[] { "normal", "default", "cancel", "link" } } },
6507:			"checkbox", "label", new Object[][] {
6508:				{ "boolean", "selected", "paint", Boolean.FALSE }, //...group
6509:				{ "string", "group", "paint", null }, //...group
6510:				{ "method", "action" } },
6511:			"togglebutton", "checkbox", null,
6512:			"combobox", "textfield", new Object[][] {
6513:				{ "icon", "icon", "validate", null },
6514:				{ "integer", "selected", "layout", integer_1 } },
6515:			"choice", null, new Object[][] {
6516:				{ "string", "name", null, null },
6517:				{ "boolean", "enabled", "paint", Boolean.TRUE },
6518:				{ "boolean", "i18n", "validate", Boolean.FALSE }, // for I18N
6519:				{ "string", "text", "parent", null },
6520:				{ "icon", "icon", "parent", null },
6521:				{ "choice", "alignment", "parent", leftcenterright },
6522:				{ "string", "tooltip", null, null },
6523:				{ "font", "font", "validate", null },
6524:				{ "color", "foreground", "paint", null },
6525:				{ "color", "background", "paint", null },
6526:				{ "property", "property", null, null } },
6527:			"textfield", "component", new Object[][] {
6528:				{ "string", "text", "layout", "" },
6529:				{ "integer", "columns", "validate", integer0 },
6530:				{ "boolean", "editable", "paint", Boolean.TRUE },
6531:				{ "integer", "start", "layout", integer0 },
6532:				{ "integer", "end", "layout", integer0 },
6533:				{ "method", "action" },
6534:				{ "method", "insert" },
6535:				{ "method", "remove" },
6536:				{ "method", "caret" },
6537:				{ "method", "perform" } },
6538:			"passwordfield", "textfield", null,
6539:			"textarea", "textfield", new Object[][] {
6540:				{ "integer", "rows", "validate", integer0 },
6541:				{ "boolean", "border", "validate", Boolean.TRUE },
6542:				{ "boolean", "wrap", "layout", Boolean.FALSE } },
6543:			"tabbedpane", "component", new Object[][] {
6544:				{ "choice", "placement", "validate",
6545:					new String[] { "top", "left", "bottom", "right", "stacked" } },
6546:				{ "integer", "selected", "paint", integer0 },
6547:				{ "method", "action" } }, //...focus
6548:			"tab", "choice", new Object[][] {
6549:				{ "integer", "mnemonic", "paint", integer_1 } },
6550:			"panel", "component", new Object[][] {
6551:				{ "integer", "columns", "validate", integer0 },
6552:				{ "integer", "top", "validate", integer0 },
6553:				{ "integer", "left", "validate", integer0 },
6554:				{ "integer", "bottom", "validate", integer0 },
6555:				{ "integer", "right", "validate", integer0 },
6556:				{ "integer", "gap", "validate", integer0 },
6557:				{ "string", "text", "validate", null },
6558:				{ "icon", "icon", "validate", null },
6559:				{ "boolean", "border", "validate", Boolean.FALSE },
6560:				{ "boolean", "scrollable", "validate", Boolean.FALSE } },
6561:			"desktop", "component", null,
6562:			"dialog", "panel", new Object[][] {
6563:				{ "boolean", "modal", null, Boolean.FALSE },
6564:				{ "boolean", "resizable", null, Boolean.FALSE },
6565:				{ "boolean", "closable", "paint", Boolean.FALSE },
6566:				{ "boolean", "maximizable", "paint", Boolean.FALSE },
6567:				{ "boolean", "iconifiable", "paint", Boolean.FALSE } },
6568:			"spinbox", "textfield", new Object[][] {
6569:				{ "integer", "minimum", null, new Integer(Integer.MIN_VALUE) },
6570:				{ "integer", "maximum", null, new Integer(Integer.MAX_VALUE) },
6571:				{ "integer", "step", null, integer1 },
6572:				{ "integer", "value", null, integer0 } }, // == text? deprecated
6573:			"progressbar", "component", new Object[][] {
6574:				{ "choice", "orientation", "validate", orientation },
6575:				{ "integer", "minimum", "paint", integer0 }, //...checkvalue
6576:				{ "integer", "maximum", "paint", new Integer(100) },
6577:				{ "integer", "value", "paint", integer0 } },
6578:				// change stringpainted
6579:			"slider", "progressbar", new Object[][] {
6580:				{ "integer", "unit", null, new Integer(5) },
6581:				{ "integer", "block", null, new Integer(25) },
6582:				{ "method", "action" } },
6583:				// minor/majortickspacing
6584:				// inverted
6585:				// labelincrement labelstart
6586:			"splitpane", "component", new Object[][] {
6587:				{ "choice", "orientation", "validate", orientation },
6588:				{ "integer", "divider", "layout", integer_1 } },
6589:			"list", "component", new Object[][] {
6590:				{ "choice", "selection", "paint", selections },
6591:				{ "method", "action" },
6592:				{ "method", "perform" },
6593:				{ "boolean", "line", "validate", Boolean.TRUE } },
6594:			"item", "choice", new Object[][] {
6595:				{ "boolean", "selected", null, Boolean.FALSE } },
6596:			"table", "list", new Object[][] {
6597:				/*{ "choice", "selection",
6598:					new String[] { "singlerow", "rowinterval", "multiplerow",
6599:						"cell", "cellinterval",
6600:						"singlecolumn", "columninterval", "multiplecolumn" } }*/ },
6601:			"header", null, null,
6602:				// reordering allowed
6603:				// autoresize mode: off next (column boundries) subsequents last all columns
6604:				// column row selection
6605:				// selection row column cell
6606:				// editing row/column
6607:			"column", "choice", new Object[][] {
6608:				{ "integer", "width", null, new Integer(80) },
6609:				{ "choice", "sort", null, new String[] { "none", "ascent", "descent" } } },
6610:			"row", null, new Object[][] {
6611:				{ "boolean", "selected", null, Boolean.FALSE } },
6612:			"cell", "choice", null,
6613:			"tree", "list", new Object[][] {
6614:				{ "boolean", "angle", null, Boolean.FALSE },
6615:				{ "method", "expand" },
6616:				{ "method", "collapse" } },
6617:			"node", "choice", new Object[][] {
6618:				{ "boolean", "selected", null, Boolean.FALSE },
6619:				{ "boolean", "expanded", null, Boolean.TRUE } },
6620:			"separator", "component", null,
6621:			"menubar", "component", new Object[][] {
6622:				{ "choice", "placement", "validate", new String[] { "top", "bottom" } } },
6623:			"menu", "choice", new Object[][] {
6624:				{ "integer", "mnemonic", "paint", integer_1 } },
6625:			"menuitem", "choice", new Object[][] {
6626:				{ "keystroke", "accelerator", null, null },
6627:				{ "method", "action" },
6628:				{ "integer", "mnemonic", "paint", integer_1 } },
6629:			"checkboxmenuitem", "menuitem", new Object[][] {
6630:				{ "boolean", "selected", "paint", Boolean.FALSE }, //...group
6631:				{ "string", "group", "paint", null } }, //...group
6632:			"popupmenu", "component", new Object[][] {
6633:				{ "method", "menushown" } }, // Post menu: Shift+F10
6634:			"bean", "component", new Object[][] {
6635:				{ "bean", "bean", null, null } }
6636:		};
6637:	}
6638:}
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.