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:}
|