/*
    Thinlet GUI toolkit - www.thinlet.com
    Copyright (C) 2002 Robert Bajzat (robert.bajzat@thinlet.com)

    This library is free software; you can redistribute it and/or
    modify it under the terms of the GNU Lesser General Public
    License as published by the Free Software Foundation; either
    version 2.1 of the License, or (at your option) any later version.

    This library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public
    License along with this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

//package thinlet; //java
//midp package thinlet.midp;

import java.applet.*; //java
import java.awt.*; //java
import java.awt.datatransfer.*; //java
import java.awt.image.*; //java
import java.awt.event.*; //java
import java.lang.reflect.*; //java
import java.io.*;
import java.net.*; //java
import java.util.*;
//midp import javax.microedition.lcdui.*;
//midp import javax.microedition.midlet.*;

/**
 *
 */
class Thinlet extends Container //java
  implements Runnable, Serializable { //java
//midp public class Thinlet extends Canvas implements CommandListener {

  //midp private static final Boolean TRUE = new Boolean(true);
  //midp private static final Boolean FALSE = new Boolean(false);

  //midp private transient Font font;
  private transient Color c_bg;
  private transient Color c_text;
  private transient Color c_textbg;
  private transient Color c_border;
  private transient Color c_disable;
  private transient Color c_hover;
  private transient Color c_press;
  private transient Color c_focus;
  private transient Color c_select;
  private transient Color c_ctrl = null; //java
  //midp private transient Color c_ctrl;
  private transient int block;
  private transient Image gradient; //java

  {
    setFont(new Font("SansSerif", Font.PLAIN, 12)); //java
    //midp setFont(Font.getDefaultFont());
    setColors(0xe6e6e6, 0x000000, 0xffffff,
              0x909090, 0xb0b0b0, 0xededed, 0xb9b9b9, 0x89899a, 0xc5c5dd); // f99237 eac16a // e68b2c ffc73c
  }

  private transient Thread timer;
  private transient long watchdelay;
  private transient long watch;
  private transient String clipboard;

  private Object content = createImpl("desktop");
  private transient Object mouseinside;
  private transient Object insidepart;
  private transient Object mousepressed;
  private transient Object pressedpart;
  private transient int referencex, referencey;
  private transient int mousex, mousey;
  private transient Object focusowner;
  private transient boolean focusinside; //midp { focusinside = true; }
  private transient Object popupowner;
  private transient Object tooltipowner;
  //private transient int pressedkey;

  //java>
  private static final int DRAG_ENTERED = AWTEvent.RESERVED_ID_MAX + 1;
  private static final int DRAG_EXITED = AWTEvent.RESERVED_ID_MAX + 2;

  private static long WHEEL_MASK = 0;
  private static int MOUSE_WHEEL = 0;
  private static Method wheelrotation = null;

  static {
    try {
      WHEEL_MASK = AWTEvent.class.getField("MOUSE_WHEEL_EVENT_MASK").getLong(null);
      MOUSE_WHEEL = MouseEvent.class.getField("MOUSE_WHEEL").getInt(null);
    }
    catch (Exception exc) { /* not 1.4 */ }
  }

  {
    if (MOUSE_WHEEL != 0) { // disable global focus-manager for this component in 1.4
      try {
        getClass().getMethod("setFocusTraversalKeysEnabled", new Class[]{Boolean.TYPE}).
          invoke(this, new Object[]{Boolean.FALSE});
      }
      catch (Exception exc) { /* never */ }
    }
    enableEvents(AWTEvent.COMPONENT_EVENT_MASK |
                 AWTEvent.FOCUS_EVENT_MASK | AWTEvent.KEY_EVENT_MASK |
                 AWTEvent.MOUSE_EVENT_MASK | AWTEvent.MOUSE_MOTION_EVENT_MASK | WHEEL_MASK);
  }
  //<java

  /**
   *
   */
  public void setColors(int background, int text, int textbackground,
                        int border, int disable, int hover, int press,
                        int focus, int select) {
    c_bg = new Color(background);
    c_text = new Color(text);
    c_textbg = new Color(textbackground);
    c_border = new Color(border);
    c_disable = new Color(disable);
    c_hover = new Color(hover);
    c_press = new Color(press);
    c_focus = new Color(focus);
    c_select = new Color(select);
    //midp c_ctrl = c_hover;
    //java>
    int[] pix = new int[block * block];
    int r1 = c_bg.getRed();
    int r2 = c_press.getRed();
    int g1 = c_bg.getGreen();
    int g2 = c_press.getGreen();
    int b1 = c_bg.getBlue();
    int b2 = c_press.getBlue();
    for (int i = 0; i < block; i++) {
      int r = r1 - (r1 - r2) * i / block;
      int g = g1 - (g1 - g2) * i / block;
      int b = b1 - (b1 - b2) * i / block;
      int color = (255 << 24) | (r << 16) | (g << 8) | b;
      for (int j = 0; j < block; j++) {
        pix[i * block + j] = color;
        //pix[j * block + i] = color;
      }
    }
    gradient = createImage(new MemoryImageSource(block, block, pix, 0, block));
    //<java
  }

  /**
   *
   */
  public void setFont(Font font) {
    block = getFontMetrics(font).getHeight(); //java
    super.setFont(font); //java
    //midp block = font.getHeight();
    //midp this.font = font;
  }

  /**
   *
   */
  private void doLayout(Object component) {
    String classname = getClass(component);
    if ("combobox" == classname) {
      if (getBoolean(component, "editable", true)) {
        Image icon = getIcon(component, "icon", null);
        layoutField(component, block, false,
                    (icon != null) ? icon.getWidth(this) : 0);
      } // set editable -> validate (overwrite textfield repaint)
      else {
        int selected = getInteger(component, "selected", -1);
        if (selected != -1) {
          Object choice = getItem(component, "choice", selected);
          set(component, "text", get(choice, "text"));
          set(component, "icon", get(choice, "icon"));
        }
      }
    }
    else if (("textfield" == classname) || ("passwordfield" == classname)) {
      layoutField(component, 0, ("passwordfield" == classname), 0);
    }
    else if ("textarea" == classname) {
      String text = getString(component, "text", "");
      int start = getInteger(component, "start", 0);
      if (start > text.length()) {
        setInteger(component, "start", start = text.length(), 0);
      }
      int end = getInteger(component, "end", 0);
      if (end > text.length()) {
        setInteger(component, "end", end = text.length(), 0);
      }
      int caretx = 0;
      int carety = 0;
      FontMetrics fm = getFontMetrics(getFont()); //java
      int width = 0, height = 0;
      for (int i = 0, j = 0; j != -1; i = j + 1) {
        j = text.indexOf('\n', i);
        if (i != j) { // && i != text.length()
          String line = (j != -1) ? text.substring(i, j) : text.substring(i); //java
          width = Math.max(width, fm.stringWidth(line)); //java
          //midp width = font.substringWidth(text, i, ((j != -1) ? j : text.length()) - i);
        }
        if ((end >= i) && ((j == -1) || (end <= j))) {
          caretx = fm.stringWidth(text.substring(i, end)); //java
          //midp caretx = font.substringWidth(text, i, end - i);
          carety = height;
        }
        height += fm.getHeight();
      }
      layoutScrollPane(component, width + 2,
                       height - fm.getLeading() + 2, 0, 0);
      scrollToVisible(component, caretx, carety,
                      2, fm.getAscent() + fm.getDescent() + 2);
    }
    else if ("tabbedpane" == classname) {
      Rectangle bounds = getRectangle(component, "bounds");
      String placement = getString(component, "placement", "top");
      boolean horizontal = ((placement == "top") || (placement == "bottom"));
      int tabd = 0;
      int tabsize = 0;
      for (Object comp = get(component, "tab");
           comp != null; comp = get(comp, ":next")) {
        Dimension d = getSize(comp, 8, 4, "left");
        setRectangle(comp, "bounds",
                     horizontal ? tabd : 0, horizontal ? 0 : tabd, d.width, d.height);
        tabd += horizontal ? d.width : d.height;
        tabsize = Math.max(tabsize, horizontal ? d.height : d.width);
      }
      for (Object comp = get(component, "tab");
           comp != null; comp = get(comp, ":next")) {
        Rectangle r = getRectangle(comp, "bounds");
        if (horizontal) {
          if (placement == "bottom") {
            r.y = bounds.height - tabsize;
          }
          r.height = tabsize;
        }
        else {
          if (placement == "right") {
            r.x = bounds.width - tabsize;
          }
          r.width = tabsize;
        }
      }
      int cx = (placement == "left") ? (tabsize + 1) : 2;
      int cy = (placement == "top") ? (tabsize + 1) : 2;
      int cwidth = bounds.width - (horizontal ? 4 : (tabsize + 3));
      int cheight = bounds.height - (horizontal ? (tabsize + 3) : 4);
      for (Object comp = get(component, "component");
           comp != null; comp = get(comp, ":next")) {
        if (!getBoolean(comp, "visible", true)) {
          continue;
        }
        setRectangle(comp, "bounds", cx, cy, cwidth, cheight);
        doLayout(comp);
      }
    }
    else if (("panel" == classname) || (classname == "dialog")) {
      int gap = getInteger(component, "gap", 0);
      int[][] grid = getGrid(component, gap);
      if (grid != null) {
        int top = getInteger(component, "top", 0);
        int left = getInteger(component, "left", 0);
        int bottom = getInteger(component, "bottom", 0);
        int right = getInteger(component, "right", 0);
        if (classname == "dialog") {
          int titleheight = getInteger(component, "titleheight", 0);
          top += 4 + titleheight;
          left += 4;
          bottom += 4;
          right += 4;
        }

        Rectangle bounds = getRectangle(component, "bounds");
        for (int i = 0; i < 2; i++) {
          int d = ((i == 0) ? (bounds.width - left - right) :
                   (bounds.height - top - bottom)) -
                  getSum(grid[i], 0, grid[i].length, gap, false);
          if (d != 0) {
            int w = getSum(grid[2 + i], 0, grid[2 + i].length, 0, false);
            if (w > 0) {
              for (int j = 0; j < grid[i].length; j++) {
                if (grid[2 + i][j] != 0) {
                  grid[i][j] += d * grid[2 + i][j] / w;
                }
              }
            }
          }
        }

        int i = 0;
        for (Object comp = get(component, "component");
             comp != null; comp = get(comp, ":next")) {
          if (!getBoolean(comp, "visible", true)) {
            continue;
          }
          int ix = left + getSum(grid[0], 0, grid[4][i], gap, true);
          int iy = top + getSum(grid[1], 0, grid[5][i], gap, true);
          int iwidth = getSum(grid[0], grid[4][i], grid[6][i], gap, false);
          int iheight = getSum(grid[1], grid[5][i], grid[7][i], gap, false);
          String halign = getString(comp, "halign", "fill");
          String valign = getString(comp, "valign", "fill");
          if ((halign != "fill") || (valign != "fill")) {
            Dimension d = getPreferredSize(comp);
            if (halign != "fill") {
              int dw = Math.max(0, iwidth - d.width);
              if (halign == "center") {
                ix += dw / 2;
              }
              else if (halign == "right") {
                ix += dw;
              }
              iwidth -= dw;
            }
            if (valign != "fill") {
              int dh = Math.max(0, iheight - d.height);
              if (valign == "center") {
                iy += dh / 2;
              }
              else if (valign == "bottom") {
                iy += dh;
              }
              iheight -= dh;
            }
          }
          setRectangle(comp, "bounds", ix, iy, iwidth, iheight);
          doLayout(comp);
          i++;
        }
      }
    }
    else if ("desktop" == classname) {
      Rectangle bounds = getRectangle(component, "bounds");
      for (Object comp = get(component, "component");
           comp != null; comp = get(comp, ":next")) {
        String iclass = getClass(comp);
        if (iclass == "dialog") {
          Dimension d = getPreferredSize(comp);
          if (get(comp, "bounds") == null) {
            setRectangle(comp, "bounds",
                         Math.max(0, (bounds.width - d.width) / 2),
                         Math.max(0, (bounds.height - d.height) / 2),
                         Math.min(d.width, bounds.width), Math.min(d.height, bounds.height));
          }
        }
        else if ((iclass == "combolist") || (iclass == "popupmenu")) {
          iclass = iclass; //compiler bug
        }
        else {
          setRectangle(comp, "bounds", 0, 0, bounds.width, bounds.height);
        }
        doLayout(comp);
      }
    }
    else if ("spinbox" == classname) {
      layoutField(component, block, false, 0);
    }
    else if ("splitpane" == classname) {
      Rectangle bounds = getRectangle(component, "bounds");
      boolean horizontal = ("vertical" != get(component, "orientation"));
      int divider = getInteger(component, "divider", -1);
      int maxdiv = (horizontal ? bounds.width : bounds.height) - 5;

      Object comp1 = get(component, "component");
      boolean visible1 = (comp1 != null) && getBoolean(comp1, "visible", true);
      if (divider == -1) {
        int d1 = 0;
        if (visible1) {
          Dimension d = getPreferredSize(comp1);
          d1 = horizontal ? d.width : d.height;
        }
        divider = Math.min(d1, maxdiv);
        setInteger(component, "divider", divider, -1);
      }
      else if (divider > maxdiv) {
        setInteger(component, "divider", divider = maxdiv, -1);
      }

      if (visible1) {
        setRectangle(comp1, "bounds", 0, 0, horizontal ? divider : bounds.width,
                     horizontal ? bounds.height : divider);
        doLayout(comp1);
      }
      Object comp2 = (comp1 != null) ? get(comp1, ":next") : null;
      if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
        setRectangle(comp2, "bounds", horizontal ? (divider + 5) : 0,
                     horizontal ? 0 : (divider + 5),
                     horizontal ? (bounds.width - 5 - divider) : bounds.width,
                     horizontal ? bounds.height : (bounds.height - 5 - divider));
        doLayout(comp2);
      }
    }
    else if (("list" == classname) ||
             ("table" == classname) || ("tree" == classname)) {
      int width = 0;
      int columnheight = 0;
      if ("table" == classname) {
        for (Object column = get(component, "column");
             column != null; column = get(column, ":next")) {
          width += getInteger(column, "width", 80);
          Dimension d = getSize(column, 2, 2, "left");
          columnheight = Math.max(columnheight, d.height);
        }
      }
      String itemname = ("list" == classname) ? "item" :
                        (("table" == classname) ? "row" : "node");
      int y = 0;
      int level = 0;
      for (Object item = get(component, itemname); item != null; ) {
        int x = 0;
        int iwidth = 0;
        int iheight = 0;
        if ("table" == classname) {
          iwidth = width;
          for (Object cell = get(item, "cell");
               cell != null; cell = get(cell, ":next")) {
            Dimension d = getSize(cell, 2, 3, "left");
            iheight = Math.max(iheight, d.height);
          }
        }
        else {
          if ("tree" == classname) {
            x = (level + 1) * block;
          }
          Dimension d = getSize(item, 2, 3, "left");
          iwidth = d.width;
          iheight = d.height;
          width = Math.max(width, x + d.width);
        }
        setRectangle(item, "bounds", x, y, iwidth, iheight);
        y += iheight;
        if ("tree" == classname) {
          Object next = get(item, "node");
          if ((next != null) && getBoolean(item, "expanded", true)) {
            level++;
          }
          else {
            while (((next = get(item, ":next")) == null) && (level > 0)) {
              item = getParent(item);
              level--;
            }
          }
          item = next;
        }
        else {
          item = get(item, ":next");
        }
      }
      layoutScrollPane(component, width, y - 1, 0, columnheight);
    }
    else if ("menubar" == classname) {
      Rectangle bounds = getRectangle(component, "bounds");
      int x = 0;
      for (Object menu = get(component, "menu");
           menu != null; menu = get(menu, ":next")) {
        Dimension d = getSize(menu, 8, 4, "left");
        setRectangle(menu, "bounds", x, 0, d.width, bounds.height);
        x += d.width;
      }
    }
    else if (("combolist" == classname) || ("popupmenu" == classname)) {
      boolean combo = ("combolist" == classname);
      int pw = 0;
      int ph = 0;
      int pxy = combo ? 0 : 1;
      for (Object item = get(get(component, combo ? "combobox" : "menu"),
                             combo ? "choice" : "menu"); item != null; item = get(item, ":next")) {
        String itemclass = combo ? null : getClass(item);
        Dimension d = (itemclass == "separator") ? new Dimension(1, 1) :
                      getSize(item, 8, 4, "left");
        if (itemclass == "checkboxmenuitem") {
          d.width = d.width + block + 3;
          d.height = Math.max(block, d.height);
        }
        else if (itemclass == "menu") {
          d.width += block;
        }
        setRectangle(item, "bounds", pxy, pxy + ph, d.width, d.height);
        pw = Math.max(pw, d.width);
        ph += d.height;
      }
      Rectangle r = getRectangle(component, "bounds");
      r.width = pw + 2;
      r.height = ph + 2;
      if (combo) {
        Rectangle db = getRectangle(content, "bounds");
        if (r.y + ph + 2 > db.height) {
          r.width = pw + 2 + block;
          r.height = db.height - r.y;
        }
        else {
          r.height = Math.min(r.height, db.height - r.y);
        }
        r.width = Math.min(r.width, db.width - r.x);
        layoutScrollPane(component, pw, ph, 0, 0);//~
      }
    }
    //java>
    else if ("bean" == classname) {
      Rectangle r = getRectangle(component, "bounds");
      ((Component)get(component, "bean")).setBounds(r);
    }
    //<java
  }

  /**
   *
   */
  private Object popup(Object component, Object classname) {
    Object popup = null;
    int px = 0;
    int py = 0;
    if (("menubar" == classname) || ("popupmenu" == classname)) {
      Object popupmenu = get(component, "popupmenu");
      Object selected = get(component, "selected");
      if (popupmenu != null) {
        if (get(popupmenu, "menu") == selected) {
          return null;
        }
        set(popupmenu, "selected", null);
        set(popupmenu, "menu", null);
        removeItemImpl(content, "component", popupmenu);
        repaint(popupmenu);
        set(popupmenu, ":parent", null);
        set(component, "popupmenu", null);
        if (mouseinside == popupmenu) {
          findComponent(content, mousex, mousey);
          handleMouseEvent(mousex, mousex, 1, false, false, false, //java
                           MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); //java
        }
        popup(popupmenu, "popupmenu");
      }
      if ((selected == null) || (getClass(selected) != "menu")) {
        return null;
      }
      popup = createImpl("popupmenu");
      set(popup, "menu", selected);
      set(component, "popupmenu", popup);

      Rectangle bounds = getRectangle(selected, "bounds");
      if ("menubar" == classname) {
        px = bounds.x;
        py = bounds.y + bounds.height - 1;
      }
      else {
        px = bounds.x + getRectangle(component, "bounds").width - 4;
        py = bounds.y;
      }
    }
    else { //if ("combobox" == classname) {
      popup = createImpl("combolist");
      set(popup, "combobox", component);
      set(component, "combolist", popup);

      py = getRectangle(component, "bounds").height + 1;
    }
    if (("menubar" == classname) || ("combobox" == classname)) {
      popupowner = component;
    }
    insertItem(content, "component", popup, 0);
    set(popup, ":parent", content);
    while (component != content) {
      Rectangle r = getRectangle(component, "bounds");
      px += r.x;
      py += r.y;
      component = getParent(component);
    }
    setRectangle(popup, "bounds", px, py, 0, 0);
    doLayout(popup);
    repaint(popup);
    return popup;
  }

  /**
   *
   */
  private void closeup(Object combobox, Object combolist, Object item) {
    if ((item != null) && getBoolean(item, "enabled", true)) {
      String text = getString(item, "text", "");
      set(combobox, "text", text); // if editable
      setInteger(combobox, "start", text.length(), 0);
      setInteger(combobox, "end", 0, 0);
      set(combobox, "icon", get(item, "icon"));
      validate(combobox);
      setInteger(combobox, "selected", getIndex(combobox, "choice", item), -1);
      invoke(combobox, "action");
    }
    set(combolist, "combobox", null);
    set(combobox, "combolist", null);
    removeItemImpl(content, "component", combolist);
    repaint(combolist);
    set(combolist, ":parent", null);
    popupowner = null;
    if (mouseinside == combolist) {
      findComponent(content, mousex, mousey);
      handleMouseEvent(mousex, mousex, 1, false, false, false, //java
                       MouseEvent.MOUSE_ENTERED, mouseinside, insidepart); //java
    }
  }

  /**
   *
   */
  private void closeup(Object menubar) {
    set(menubar, "selected", null);
    popup(menubar, "menubar");
    repaint(menubar); // , selected
    popupowner = null;
  }

  /**
   *
   */
  private void closeup() {
    if (popupowner != null) {
      String classname = getClass(popupowner);
      if ("menubar" == classname) {
        closeup(popupowner);
      }
      else if ("combobox" == classname) {
        closeup(popupowner, get(popupowner, "combolist"), null);
      }
    }
  }

  /**
   *
   */
  private void showTip() {
    String text = null;
    tooltipowner = null;
    String classname = getClass(mouseinside);
    if ((classname == "tabbedpane") || (classname == "menubar") || (classname == "popupmenu")) {
      if (insidepart != null) {
        text = getString(insidepart, "tooltip", null);
      }
    }
    else if (classname == "combolist") {
      if (insidepart instanceof Object[]) {
        text = getString(insidepart, "tooltip", null);
      }
    }
    //list table tree
    if (text == null) {
      text = getString(mouseinside, "tooltip", null);
    }
    else {
      tooltipowner = insidepart;
    }
    if (text != null) {
      FontMetrics fm = getFontMetrics(getFont());
      int width = fm.stringWidth(text) + 4;
      int height = fm.getAscent() + fm.getDescent() + 4;
      if (tooltipowner == null) {
        tooltipowner = mouseinside;
      }
      setRectangle(tooltipowner, "tooltipbounds", mousex + 10, mousey + 10, width, height);
      repaint(mousex + 10, mousey + 10, width, height);
    }
  }

  /**
   *
   */
  private void hideTip() {
    if (tooltipowner != null) {
      Rectangle bounds = getRectangle(tooltipowner, "tooltipbounds");
      set(tooltipowner, "tooltipbounds", null);
      tooltipowner = null;
      repaint(bounds.x, bounds.y, bounds.width, bounds.height);
    }
  }

  /**
   *
   */
  private void layoutField(Object component, int dw, boolean hidden, int left) {
    int width = getRectangle(component, "bounds").width - left - dw;
    String text = getString(component, "text", "");
    int start = getInteger(component, "start", 0);
    if (start > text.length()) {
      setInteger(component, "start", start = text.length(), 0);
    }
    int end = getInteger(component, "end", 0);
    if (end > text.length()) {
      setInteger(component, "end", end = text.length(), 0);
    }
    int offset = getInteger(component, "offset", 0);
    int off = offset;
    FontMetrics fm = getFontMetrics(getFont());
    int caret = hidden ? (fm.charWidth('*') * end) :
                fm.stringWidth(text.substring(0, end)); //java
    //midp font.substringWidth(text, 0, end);
    if (off > caret) {
      off = caret;
    }
    else if (off < caret - width + 4) {
      off = caret - width + 4;
    }
    off = Math.max(0, Math.min(off, (hidden ? (fm.charWidth('*') *
                                               text.length()) : fm.stringWidth(text)) - width + 4));
    if (off != offset) {
      setInteger(component, "offset", off, 0);
    }
  }

  /**
   *
   */
  private void layoutScrollPane(Object component,
                                int contentwidth, int contentheight, int rowwidth, int columnheight) {
    Rectangle bounds = getRectangle(component, "bounds");
    boolean hneed = false;
    boolean vneed = false;
    if (contentwidth > bounds.width - rowwidth - 2) {
      hneed = true;
      vneed = (contentheight > bounds.height - columnheight - 2 - block);
    }
    if (vneed || (contentheight > bounds.height - columnheight - 2)) {
      vneed = true;
      hneed = hneed || (contentwidth > bounds.width - rowwidth - 2 - block);
    }
    int viewportwidth = bounds.width - rowwidth - (vneed ? block : 0);
    int viewportheight = bounds.height - columnheight - (hneed ? block : 0);
    setRectangle(component, ":port",
                 rowwidth, columnheight, viewportwidth, viewportheight); //?rowwidth

    Rectangle view = getRectangle(component, ":view");
    setRectangle(component, ":view",
                 (view != null) ? Math.max(0,
                                           Math.min(view.x, contentwidth - viewportwidth + 2)) : 0,
                 (view != null) ? Math.max(0,
                                           Math.min(view.y, contentheight - viewportheight + 2)) : 0,
                 Math.max(viewportwidth - 2, contentwidth),
                 Math.max(viewportheight - 2, contentheight));
  }

  /**
   *
   */
  private void scrollToVisible(Object component,
                               int x, int y, int width, int height) {
    Rectangle view = getRectangle(component, ":view");
    Rectangle port = getRectangle(component, ":port");
    int vx = Math.max(x + width - port.width + 2, Math.min(view.x, x));
    int vy = Math.max(y + height - port.height + 2, Math.min(view.y, y));
    if ((view.x != vx) || (view.y != vy)) {
      repaint(component); // horizontal | vertical
      view.x = vx;
      view.y = vy;
    }
  }

  /**
   *
   */
  public Dimension getPreferredSize() {
    return getPreferredSize(content);
  }

  /**
   *
   */
  private Dimension getPreferredSize(Object component) {
    int width = getInteger(component, "width", 0);
    int height = getInteger(component, "height", 0);
    if ((width > 0) && (height > 0)) {
      return new Dimension(width, height);
    }
    String classname = getClass(component);
    //System.out.println("classname: " + classname);
    if ("label" == classname) {
      return getSize(component, 0, 0, "left");
    }
    if ("button" == classname) {
      return getSize(component, 12, 6, "center");
    }
    if ("checkbox" == classname) {
      Dimension d = getSize(component, 0, 0, "left");
      d.width = d.width + block + 3;
      d.height = Math.max(block, d.height);
      return d;
    }
    if ("combobox" == classname) {
      if (getBoolean(component, "editable", true)) {
        Dimension size = getFieldSize(component);
        Image icon = getIcon(component, "icon", null);
        if (icon != null) {
          size.width += icon.getWidth(this);
          size.height = Math.max(size.height, icon.getHeight(this) + 2);
        }
        size.width += block;
        return size;
      }
      else {
        int selected = getInteger(component, "selected", -1);
        return getSize((selected != -1) ?
                       getItemImpl(component, "choice", selected) :
                       get(component, "choice"), 4 + block, 4, "left");
      }
    }
    if (("textfield" == classname) || ("passwordfield" == classname)) {
      return getFieldSize(component);
    }
    if ("textarea" == classname) {
      int columns = getInteger(component, "columns", 0);
      int rows = getInteger(component, "rows", 0); // 'e' -> 'm' ?
      FontMetrics fm = getFontMetrics(getFont()); //java
      return new Dimension(
        ((columns > 0) ? (columns * fm.charWidth('e') + 2) : 76) + 2 + block,
        ((rows > 0) ? (rows * fm.getHeight() - fm.getLeading() + 2) : 76) + 2 + block);
    }
    if ("tabbedpane" == classname) {
      String placement = getString(component, "placement", "top");
      boolean horizontal = ((placement == "top") || (placement == "bottom"));
      int tabsize = 0;
      int contentwidth = 0;
      int contentheight = 0;
      for (Object comp = get(component, "tab");
           comp != null; comp = get(comp, ":next")) {
        Dimension d = getSize(comp, 8, 4, "left");
        tabsize = Math.max(tabsize, horizontal ? d.height : d.width);
      }
      for (Object comp = get(component, "component");
           comp != null; comp = get(comp, ":next")) {
        if (!getBoolean(comp, "visible", true)) {
          continue;
        }
        Dimension d = getPreferredSize(comp);
        contentwidth = Math.max(contentwidth, d.width);
        contentheight = Math.max(contentheight, d.height);
      }
      return new Dimension(contentwidth + (horizontal ? 4 : (tabsize + 3)),
                           contentheight + (horizontal ? (tabsize + 3) : 4));
    }
    if (("panel" == classname) || (classname == "dialog")) {
      Dimension size = new Dimension(
        getInteger(component, "left", 0) + getInteger(component, "right", 0),
        getInteger(component, "top", 0) + getInteger(component, "bottom", 0));
      if (classname == "dialog") {
        int titleheight = getSize(component, 0, 0, "left").height;
        setInteger(component, "titleheight", titleheight, 0);
        size.width += 8;
        size.height += 8 + titleheight;
      }
      int gap = getInteger(component, "gap", 0);
      int[][] grid = getGrid(component, gap);
      if (grid != null) {
        size.width += getSum(grid[0], 0, grid[0].length, gap, false);
        size.height += getSum(grid[1], 0, grid[1].length, gap, false);
      }
      return size;
    }
    else if ("desktop" == classname) {
      Dimension size = new Dimension();
      for (Object comp = get(component, "component");
           comp != null; comp = get(comp, ":next")) {
        String iclass = getClass(comp);
        if ((iclass != "dialog") && (iclass != "popupmenu") &&
            (iclass != "combolist")) {
          Dimension d = getPreferredSize(comp);
          size.width = Math.max(d.width, size.width);
          size.height = Math.max(d.height, size.height);
        }
      }
      return size;
    }
    if ("spinbox" == classname) {
      Dimension size = getFieldSize(component);
      size.width += block;
      return size;
    }
    if ("progressbar" == classname) {
      boolean horizontal = ("vertical" != get(component, "orientation"));
      return new Dimension(horizontal ? 76 : 6, horizontal ? 6 : 76);
    }
    if ("slider" == classname) {
      boolean horizontal = ("vertical" != get(component, "orientation"));
      return new Dimension(horizontal ? 76 : 10, horizontal ? 10 : 76);
    }
    if ("splitpane" == classname) {
      boolean horizontal = ("vertical" != get(component, "orientation"));
      Object comp1 = get(component, "component");
      Dimension size = ((comp1 == null) || !getBoolean(comp1, "visible", true)) ?
                       new Dimension() : getPreferredSize(comp1);
      Object comp2 = get(comp1, ":next");
      if ((comp2 != null) && getBoolean(comp2, "visible", true)) {
        Dimension d = getPreferredSize(comp2);
        size.width = horizontal ? (size.width + d.width) :
                     Math.max(size.width, d.width);
        size.height = horizontal ? Math.max(size.height, d.height) :
                      (size.height + d.height);
      }
      if (horizontal) {
        size.width += 5;
      }
      else {
        size.height += 5;
      }
      return size;
    }
    if (("list" == classname) ||
        ("table" == classname) || ("tree" == classname)) {
      return new Dimension(76 + 2 + block, 76 + 2 + block);
    }
    if ("separator" == classname) {
      return new Dimension(1, 1);
    }
    if ("menubar" == classname) {
      Dimension size = new Dimension(0, 0);
      for (Object menu = get(component, "menu");
           menu != null; menu = get(menu, ":next")) {
        Dimension d = getSize(menu, 8, 4, "left");
        size.width += d.width;
        size.height = Math.max(size.height, d.height);
      }
      return size;
    }
    //java>
    if ("bean" == classname) {
      return ((Component)get(component, "bean")).getPreferredSize();
    }
    //<java
    throw new IllegalArgumentException((String)classname);
  }

  /**
   *
   */
  private int[][] getGrid(Object component, int gap) {
    java.lang.ref.Reference.reachabilityFence(gap);
    int count = 0;
    for (Object comp = get(component, "component"); comp != null;
         comp = get(comp, ":next")) {
      if (getBoolean(comp, "visible", true)) {
        count++;
      }
    }
    if (count == 0) {
      return null;
    }
    int columns = getInteger(component, "columns", 0);
    int icols = (columns != 0) ? columns : count;
    int irows = (columns != 0) ? ((count + columns - 1) / columns) : 1;
    int[][] grid = {
      new int[icols], new int[irows], // columnwidths, rowheights
      new int[icols], new int[irows], // columnweights, rowweights
      new int[count], new int[count], // gridx, gridy
      new int[count], new int[count]}; // gridwidth, gridheight
    int[] columnheight = new int[icols];
    int[][] cache = null; // preferredwidth, height, columnweight, rowweight

    int i = 0;
    int x = 0;
    int y = 0;
    int nextsize = 0;
    for (Object comp = get(component, "component");
         comp != null; comp = get(comp, ":next")) {
      if (!getBoolean(comp, "visible", true)) {
        continue;
      }
      int colspan = ((columns != 0) && (columns < count)) ?
                    Math.min(getInteger(comp, "colspan", 1), columns) : 1;
      int rowspan = (columns != 1) ? getInteger(comp, "rowspan", 1) : 1;

      for (int j = 0; j < colspan; j++) {
        if ((columns != 0) && (x + colspan > columns)) {
          x = 0;
          y++;
          j = -1;
        }
        else if (columnheight[x + j] > y) {
          x += (j + 1);
          j = -1;
        }
      }
      if (y + rowspan > grid[1].length) {
        int[] rowheights = new int[y + rowspan];
        System.arraycopy(grid[1], 0, rowheights, 0, grid[1].length);
        grid[1] = rowheights;
        int[] rowweights = new int[y + rowspan];
        System.arraycopy(grid[3], 0, rowweights, 0, grid[3].length);
        grid[3] = rowweights;
      }
      for (int j = 0; j < colspan; j++) {
        columnheight[x + j] = y + rowspan;
      }

      int weightx = getInteger(comp, "weightx", 0);
      int weighty = getInteger(comp, "weighty", 0);
      Dimension d = getPreferredSize(comp);

      if (colspan == 1) {
        grid[0][x] = Math.max(grid[0][x], d.width); // columnwidths
        grid[2][x] = Math.max(grid[2][x], weightx); // columnweights
      }
      else {
        if (cache == null) {
          cache = new int[4][count];
        }
        cache[0][i] = d.width;
        cache[2][i] = weightx;
        if ((nextsize == 0) || (colspan < nextsize)) {
          nextsize = colspan;
        }
      }
      if (rowspan == 1) {
        grid[1][y] = Math.max(grid[1][y], d.height); // rowheights
        grid[3][y] = Math.max(grid[3][y], weighty); // rowweights
      }
      else {
        if (cache == null) {
          cache = new int[4][count];
        }
        cache[1][i] = d.height;
        cache[3][i] = weighty;
        if ((nextsize == 0) || (rowspan < nextsize)) {
          nextsize = rowspan;
        }
      }
      grid[4][i] = x; //gridx
      grid[5][i] = y; //gridy
      grid[6][i] = colspan; //gridwidth
      grid[7][i] = rowspan; //gridheight

      x += colspan;
      i++;
    }

    while (nextsize != 0) {
      int size = nextsize;
      nextsize = 0;
      for (int j = 0; j < 2; j++) { // horizontal, vertical
        for (int k = 0; k < count; k++) {
          if (grid[6 + j][k] == size) { // gridwidth, gridheight
            int gridpoint = grid[4 + j][k]; // gridx, gridy

            int weightdiff = cache[2 + j][k];
            for (int m = 0; (weightdiff > 0) && (m < size); m++) {
              weightdiff -= grid[2 + j][gridpoint + m];
            }
            if (weightdiff > 0) {
              int weightsum = cache[2 + j][k] - weightdiff;
              for (int m = 0; (weightsum > 0) && (m < size); m++) {
                int weight = grid[2 + j][gridpoint + m];
                if (weight > 0) {
                  int weightinc = weight * weightdiff / weightsum;
                  grid[2 + j][gridpoint + m] += weightinc;
                  weightdiff -= weightinc;
                  weightsum -= weightinc;
                }
              }
              grid[2 + j][gridpoint + size - 1] += weightdiff;
            }

            int sizediff = cache[j][k];
            int weightsum = 0;
            for (int m = 0; (sizediff > 0) && (m < size); m++) {
              sizediff -= grid[j][gridpoint + m];
              weightsum += grid[2 + j][gridpoint + m];
            }
            if (sizediff > 0) {
              for (int m = 0; (weightsum > 0) && (m < size); m++) {
                int weight = grid[2 + j][gridpoint + m];
                if (weight > 0) {
                  int sizeinc = weight * sizediff / weightsum;
                  grid[j][gridpoint + m] += sizeinc;
                  sizediff -= sizeinc;
                  weightsum -= weight;
                }
              }
              grid[j][gridpoint + size - 1] += sizediff;
            }
          }
          else if ((grid[6 + j][k] > size) &&
                   ((nextsize == 0) || (grid[6 + j][k] < nextsize))) {
            nextsize = grid[6 + j][k];
          }
        }
      }
    }
    return grid;
  }

  /**
   *
   */
  private int getSum(int[] values,
                     int from, int length, int gap, boolean last) {
    if (length <= 0) {
      return 0;
    }
    int value = 0;
    for (int i = 0; i < length; i++) {
      value += values[from + i];
    }
    return value + (length - (last ? 0 : 1)) * gap;
  }

  /**
   *
   */
  private Dimension getFieldSize(Object component) {
    String text = getString(component, "text", "");
    java.lang.ref.Reference.reachabilityFence(text);
    int columns = getInteger(component, "columns", 0);
    FontMetrics fm = getFontMetrics(getFont());
    return new Dimension(((columns > 0) ?
                          (columns * fm.charWidth('e')) : 76) + 4,
                         fm.getAscent() + fm.getDescent() + 4); // fm.stringWidth(text)
  }

  /**
   *
   */
  private Dimension getSize(Object component,
                            int dx, int dy, String defaultalignment) {
    java.lang.ref.Reference.reachabilityFence(defaultalignment);
    String text = getString(component, "text", null);
    int tw = 0;
    int th = 0;
    if (text != null) {
      FontMetrics fm = getFontMetrics(getFont());
      tw = fm.stringWidth(text);
      th = fm.getAscent() + fm.getDescent();
    }
    Image icon = getIcon(component, "icon", null);
    int iw = 0;
    int ih = 0;
    if (icon != null) {
      iw = icon.getWidth(this);
      ih = icon.getHeight(this);
    }
    return new Dimension(tw + iw + dx, Math.max(th, ih) + dy);
  }
  //java>

  /**
   *
   */
  public void update(Graphics g) {
    paint(g);
  }

  /**
   * ~
   */
  public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
    if (infoflags == ImageObserver.ALLBITS) {
      validate(content);
      return super.imageUpdate(img, infoflags, x, y, width, height);
    }
    return true;
  }

  /**
   *
   */
  public void paint(Graphics g) {
    //g.setColor(Color.orange);
    //g.fillRect(0, 0, getSize().width, getSize().height);
    //long time = System.currentTimeMillis();
    Rectangle clip = g.getClipBounds();
    ///dg.setClip(r.x, r.y, r.width, r.height);
    paint(g, clip.x, clip.y, clip.width, clip.height, content, isEnabled());
    //System.out.println(System.currentTimeMillis() - time);
    ///g.setClip(0, 0, getSize().width, getSize().height);
    //g.setColor(Color.red); g.drawRect(clip.x, clip.y, clip.width - 1, clip.height - 1);
  }

  //<java
        /*midp
    protected void paint(Graphics g) {
        paint(g, g.getClipX(), g.getClipY(),
            g.getClipWidth(), g.getClipHeight(), content, true);
    }

    protected void showNotify() {
        setRectangle(content, "bounds", 0, 0, getWidth(), getHeight());
        doLayout(content);
    }
    midp*/

  /**
   *
   */
  private void paint(Graphics g,
                     int clipx, int clipy, int clipwidth, int clipheight,
                     Object component, boolean enabled) {
    if (!getBoolean(component, "visible", true)) {
      return;
    }
    Rectangle bounds = getRectangle(component, "bounds");
    if (bounds == null) {
      return;
    }
    if (bounds.width < 0) {
      bounds.width = Math.abs(bounds.width);
      doLayout(component);
    }
    if ((clipx + clipwidth < bounds.x) ||
        (clipx > bounds.x + bounds.width) ||
        (clipy + clipheight < bounds.y) ||
        (clipy > bounds.y + bounds.height)) {
      return;
    }
    clipx -= bounds.x;
    clipy -= bounds.y;
    String classname = getClass(component);
    boolean pressed = (mousepressed == component);
    boolean inside = (mouseinside == component) &&
                     ((mousepressed == null) || pressed);
    boolean focus = focusinside && (focusowner == component);
    enabled = getBoolean(component, "enabled", true); //enabled &&
    g.translate(bounds.x, bounds.y);
    //g.setClip(0, 0, bounds.width, bounds.height);

    if ("label" == classname) {
      paintContent(component, g, clipx, clipy, clipwidth, clipheight,
                   0, 0, bounds.width, bounds.height,
                   enabled ? c_text : c_disable, "left", true);
    }
    else if ("button" == classname) {
      paintRect(g, 0, 0, bounds.width, bounds.height,
                enabled ? c_border : c_disable,
                enabled ? ((inside != pressed) ? c_hover :
                           (pressed ? c_press : c_ctrl)) : c_bg, true, true, true, true);
      if (focus) {
        g.setColor(c_focus);
        g.drawRect(2, 2, bounds.width - 5, bounds.height - 5);
      }
      paintContent(component, g, clipx, clipy, clipwidth, clipheight,
                   6, 3, bounds.width - 12, bounds.height - 6,
                   enabled ? c_text : c_disable, "center", true);
    }
    else if ("checkbox" == classname) {
      boolean selected = getBoolean(component, "selected", false);
      String group = getString(component, "group", null);
      Color border = enabled ? c_border : c_disable;
      Color foreground = enabled ? ((inside != pressed) ? c_hover :
                                    (pressed ? c_press : c_ctrl)) : c_bg;
      int dy = (bounds.height - block + 2) / 2;
      if (group == null) {
        paintRect(g, 1, dy + 1, block - 2, block - 2,
                  border, foreground, true, true, true, true);
      }
      else {
        g.setColor((foreground != c_ctrl) ? foreground : c_bg);
        g.fillOval(1, dy + 1, block - 3, block - 3); //java
        g.setColor(border);
        g.drawOval(1, dy + 1, block - 3, block - 3); //java
      }
      if (focus) {
        g.setColor(c_focus);
        if (group == null) {
          g.drawRect(3, dy + 3, block - 7, block - 7);
        }
        else {
          g.drawOval(3, dy + 3, block - 7, block - 7); //java
        }
      }
      if ((!selected && inside && pressed) ||
          (selected && (!inside || !pressed))) {
        g.setColor(enabled ? c_text : c_disable);
        if (group == null) {
          g.fillRect(3, dy + block - 9, 2, 6);
          g.drawLine(3, dy + block - 4, block - 4, dy + 3);
          g.drawLine(4, dy + block - 4, block - 4, dy + 4);
        }
        else {
          g.fillOval(5, dy + 5, block - 10, block - 10); //java
          g.drawOval(4, dy + 4, block - 9, block - 9); //java
        }
      }
      paintContent(component, g, clipx, clipy, clipwidth, clipheight,
                   block + 3, 0, bounds.width - block - 3, bounds.height,
                   enabled ? c_text : c_disable, "left", true);
    }
    else if ("combobox" == classname) {
      if (getBoolean(component, "editable", true)) {
        Image icon = getIcon(component, "icon", null);
        int left = (icon != null) ? icon.getWidth(this) : 0;
        paintField(g, clipx, clipy, clipwidth, clipheight, component,
                   bounds.width - block, bounds.height,
                   inside, pressed, focus, enabled, false, left);
        if (icon != null) {
          g.drawImage(icon, 2, (bounds.height - icon.getHeight(this)) / 2, this); //java
          //midp g.drawImage(icon, 2, bounds.height / 2, Graphics.LEFT | Graphics.VCENTER);
        }
        paintArrow(g, bounds.width - block, 0, block, bounds.height,
                   'S', enabled, inside, pressed, "down", true, false, true, true);
      }
      else {
        paintRect(g, 0, 0, bounds.width, bounds.height,
                  enabled ? c_border : c_disable,
                  enabled ? ((inside != pressed) ? c_hover :
                             (pressed ? c_press : c_ctrl)) : c_bg, true, true, true, true);
        paintContent(component, g, clipx, clipy, clipwidth, clipheight,
                     2, 2, bounds.width - block - 4, bounds.height - 4,
                     enabled ? c_text : c_disable, "left", false);
        paintArrow(g, bounds.width - block, 0, block, bounds.height, 'S');
        if (focus) {
          g.setColor(c_focus);
          g.drawRect(2, 2, bounds.width - block - 5, bounds.height - 5);
        }
      }
    }
    else if ("combolist" == classname) {
      Rectangle view = getRectangle(component, ":view");
      Rectangle viewport = getRectangle(component, ":port");
      g.setColor(c_border);
      g.drawRect(viewport.x, viewport.y, viewport.width - 1, viewport.height - 1);
      if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight,
                          bounds, view, viewport, enabled, inside, pressed)) {
        Object selected = get(component, "inside");
        int ly = clipy - viewport.y - 1;
        int yfrom = view.y + Math.max(0, ly);
        int yto = view.y + Math.min(viewport.height - 2, ly + clipheight);
        for (Object choice = get(get(component, "combobox"), "choice");
             choice != null; choice = get(choice, ":next")) {
          Rectangle r = getRectangle(choice, "bounds");
          if (yto <= r.y) {
            break;
          }
          if (yfrom >= r.y + r.height) {
            continue;
          }
          boolean armed = (selected == choice);
          paintRect(g, r.x, r.y, bounds.width - 2, r.height, c_border,
                    armed ? c_select : c_bg, false, false, false, false);
          paintContent(choice, g, clipx, yfrom, clipwidth, yto - yfrom,
                       r.x + 4, r.y + 2, bounds.width - 10, r.height - 4,
                       getBoolean(choice, "enabled", true) ? c_text : c_disable, "left", false);
        }
        resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport);
      }
      //paintRect(g, 0, 0, bounds.width, bounds.height,
      //    secondary1, c_ctrl, true, true, true, true);
    }
    else if (("textfield" == classname) || ("passwordfield" == classname)) {
      paintField(g, clipx, clipy, clipwidth, clipheight, component,
                 bounds.width, bounds.height,
                 inside, pressed, focus, enabled, ("passwordfield" == classname), 0);
    }
    else if ("textarea" == classname) {
      Rectangle view = getRectangle(component, ":view");
      Rectangle viewport = getRectangle(component, ":port");
      boolean editable = getBoolean(component, "editable", true);
      paintRect(g, viewport.x, viewport.y, viewport.width, viewport.height,
                enabled ? c_border : c_disable, editable ? c_textbg : c_bg,
                true, true, true, true);
      if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight,
                          bounds, view, viewport, enabled, inside, pressed)) {
        String text = getString(component, "text", "");
        int start = focus ? getInteger(component, "start", 0) : 0;
        int end = focus ? getInteger(component, "end", 0) : 0;
        int is = Math.min(start, end);
        int ie = Math.max(start, end);
        boolean wrap = getBoolean(component, "wrap", false);
        FontMetrics fm = g.getFontMetrics(); //java
        int fontascent = fm.getAscent();
        int fontheight = fm.getHeight(); //java
        //midp int fontheight = fm.getHeight();
        int ascent = 1;
        int ly = clipy - viewport.y - 1;
        int yfrom = view.y + Math.max(0, ly);
        int yto = view.y + Math.min(viewport.height - 2, ly + clipheight);
        //g.setColor(Color.pink); g.fillRect(0, yfrom - 1, 75, 2); g.fillRect(0, yto - 1, 75, 2);

        boolean prevletter = false;
        int n = text.length();
        char c = 0;
        for (int i = 0, j = -1, k = 0; k <= n; k++) { // j is the last space index (before k)
          if (yto <= ascent) {
            break;
          }
          if (wrap) {
            if (((k == n) || ((c = text.charAt(k)) == '\n') || (c == ' ')) &&
                (j > i) && (fm.stringWidth(text.substring(i, k)) > viewport.width - 4)) {
              k--; // draw line to the begin of the current word (+ spaces) if it is out of width
            }
            else if ((k == n) || (c == '\n')) { // draw line to the text/line end
              j = k;
              prevletter = false;
            }
            else {
              if ((c == ' ') && (prevletter || (j > i))) {
                j = k;
              } // keep spaces starting the line
              prevletter = (c != ' ');
              continue;
            }
          }
          else {
            if ((k == n) || ((c = text.charAt(k)) == '\n')) {
              j = k;
            }
            else {
              continue;
            }
          }
          if (yfrom < ascent + fontheight) {
            String line = (j != -1) ? text.substring(i, j) : text.substring(i); //java
            if (focus && (is != ie) && (ie >= i) && ((j == -1) || (is <= j))) {
              int xs = (is < i) ? -1 : (((j != -1) && (is > j)) ? (view.width - 1) :
                                        fm.stringWidth(text.substring(i, is))); //java
              //midp font.substringWidth(text, i, is - i));
              int xe = ((j != -1) && (ie > j)) ? (view.width - 1) :
                       fm.stringWidth(text.substring(i, ie)); //java
              //midp font.substringWidth(text, i, ie - i);
              g.setColor(c_select);
              g.fillRect(1 + xs, ascent, xe - xs, fontheight);
            }
            g.setColor(enabled ? c_text : c_disable);
            g.drawString(line, 1, ascent + fontascent); //java
            //midp g.drawSubstring(text, i, ((j != -1) ? j : text.length()) - i, 1, ascent, Graphics.LEFT | Graphics.TOP);
            if (focus && (end >= i) && ((j == -1) || (end <= j))) {
              int caret = fm.stringWidth(text.substring(i, end)); //java
              //midp int caret = font.substringWidth(text, i, end - i);
              g.setColor(c_focus);
              g.fillRect(caret, ascent, 1, fontheight);
            }
          }
          ascent += fontheight;
          i = j + 1;
        }
        resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport);
      }
    }
    else if ("tabbedpane" == classname) {
      int i = 0;
      Rectangle last = null;
      int selected = getInteger(component, "selected", 0);
      String placement = getString(component, "placement", "top");
      for (Object comp = get(component, "tab");
           comp != null; comp = get(comp, ":next")) {
        Rectangle r = getRectangle(comp, "bounds");
        boolean hover = !(selected == i) && inside &&
                        (mousepressed == null) && (insidepart == comp);
        boolean sel = (selected == i);
        boolean tabenabled = enabled && getBoolean(comp, "enabled", true);
        paintRect(g, r.x, r.y, r.width, r.height,
                  enabled ? c_border : c_disable,
                  tabenabled ? (sel ? c_bg : (hover ? c_hover : c_ctrl)) : c_ctrl,
                  (placement != "bottom") || !sel, (placement != "right") || !sel,
                  (placement == "bottom") || ((placement == "top") && !sel),
                  (placement == "right") || ((placement == "left") && !sel));
        if (focus && sel) {
          g.setColor(c_focus);
          g.drawRect(r.x + 2, r.y + 2, r.width - 4, r.height - 4);
        }
        paintContent(comp, g, clipx, clipy, clipwidth, clipheight,
                     r.x + 4, r.y + 2, r.width - 8, r.height - 4,
                     tabenabled ? c_text : c_disable, "left", true);
        i++;
        last = r;
      }
      if (last != null) {
        boolean horizontal = ((placement == "top") || (placement == "bottom"));
        paintRect(g, horizontal ? (last.x + last.width) : last.x,
                  horizontal ? last.y : (last.y + last.height),
                  horizontal ? (bounds.width - last.x - last.width) : last.width,
                  horizontal ? last.height : (bounds.height - last.y - last.height),
                  enabled ? c_border : c_disable, c_bg,
                  (placement != "top"), (placement != "left"),
                  (placement == "top"), (placement == "left"));
        paintRect(g, (placement == "left") ? last.width : 0,
                  (placement == "top") ? last.height : 0,
                  horizontal ? bounds.width : (bounds.width - last.width),
                  horizontal ? (bounds.height - last.height) : bounds.height,
                  enabled ? c_border : c_disable, c_bg,
                  (placement != "top"), (placement != "left"),
                  (placement != "bottom"), (placement != "right"));
      }
      Object tabcontent = getItemImpl(component, "component", selected);
      if (tabcontent != null) {
        paint(g, clipx, clipy, clipwidth, clipheight, tabcontent, enabled);
      }
    }
    else if (("panel" == classname) || ("dialog" == classname)) {
      if ("dialog" == classname) {
        int titleheight = getInteger(component, "titleheight", 0);
        paintRect(g, 0, 0, bounds.width, 3 + titleheight,
                  c_border, c_ctrl, true, true, false, true);
        paintRect(g, 0, 3 + titleheight, bounds.width, bounds.height - 3 - titleheight,
                  c_border, c_press, false, true, true, true);
        paintContent(component, g, clipx, clipy, clipwidth, clipheight,
                     3, 2, bounds.width - 6, titleheight, c_text, "left", false);
        paintRect(g, 3, 3 + titleheight, bounds.width - 6, bounds.height - 6 - titleheight,
                  c_border, c_bg, true, true, true, true);
      }
      else {
        paintRect(g, 0, 0, bounds.width, bounds.height,
                  c_border, c_bg, false, false, false, false);
      }
      for (Object comp = get(component, "component");
           comp != null; comp = get(comp, ":next")) {
        paint(g, clipx, clipy, clipwidth, clipheight, comp, enabled);
      }
    }
    else if ("desktop" == classname) {
      paintReverse(g, clipx, clipy, clipwidth, clipheight,
                   get(component, "component"), enabled);
      //g.setColor(Color.red); if (clip != null) g.drawRect(clipx, clipy, clipwidth, clipheight);
      if (tooltipowner != null) {
        Rectangle r = getRectangle(tooltipowner, "tooltipbounds");
        paintRect(g, r.x, r.y, r.width, r.height,
                  c_border, c_bg, true, true, true, true);
        String text = getString(tooltipowner, "tooltip", null);
        g.setColor(c_text);
        g.drawString(text, r.x + 2, r.y + g.getFontMetrics().getAscent() + 2); //java
        //midp g.drawString(text, r.x + 2, r.y + (r.height - font.getHeight()) / 2, Graphics.LEFT | Graphics.TOP);
      }
    }
    else if ("spinbox" == classname) {
      paintField(g, clipx, clipy, clipwidth, clipheight, component,
                 bounds.width - block, bounds.height,
                 inside, pressed, focus, enabled, false, 0);
      paintArrow(g, bounds.width - block, 0, block, bounds.height / 2,
                 'N', enabled, inside, pressed, "up", true, false, false, true);
      paintArrow(g, bounds.width - block, bounds.height / 2,
                 block, bounds.height - (bounds.height / 2),
                 'S', enabled, inside, pressed, "down", true, false, true, true);
    }
    else if ("progressbar" == classname) {
      int minimum = getInteger(component, "minimum", 0);
      int maximum = getInteger(component, "maximum", 100);
      int value = getInteger(component, "value", 0);
      boolean horizontal = ("vertical" != get(component, "orientation"));
      int length = (value - minimum) *
                   ((horizontal ? bounds.width : bounds.height) - 1) / (maximum - minimum);
      paintRect(g, 0, 0, horizontal ? length : bounds.width,
                horizontal ? bounds.height : length, enabled ? c_border : c_disable,
                c_select, true, true, horizontal, !horizontal);
      paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
                horizontal ? (bounds.width - length) : bounds.width,
                horizontal ? bounds.height : (bounds.height - length),
                enabled ? c_border : c_disable, c_bg, true, true, true, true);
    }
    else if ("slider" == classname) {
      int minimum = getInteger(component, "minimum", 0);
      int maximum = getInteger(component, "maximum", 100);
      int value = getInteger(component, "value", 0);
      boolean horizontal = ("vertical" != get(component, "orientation"));
      int length = (value - minimum) *
                   ((horizontal ? bounds.width : bounds.height) - block) /
                   (maximum - minimum);
      paintRect(g, horizontal ? 0 : 3, horizontal ? 3 : 0,
                horizontal ? length : (bounds.width - 6),
                horizontal ? (bounds.height - 6) : length,
                enabled ? c_border : c_disable,
                c_bg, true, true, horizontal, !horizontal);
      paintRect(g, horizontal ? length : 0, horizontal ? 0 : length,
                horizontal ? block : bounds.width, horizontal ? bounds.height : block,
                enabled ? c_border : c_disable,
                enabled ? c_ctrl : c_bg, true, true, true, true);
      if (focus) {
        g.setColor(c_focus);
        g.drawRect(horizontal ? (length + 2) : 2, horizontal ? 2 : (length + 2),
                   (horizontal ? block : bounds.width) - 5,
                   (horizontal ? bounds.height : block) - 5);
        //g.drawRect(length + 1, 1, block - 3, bounds.height - 3);
      }
      paintRect(g, horizontal ? (block + length) : 3,
                horizontal ? 3 : (block + length),
                bounds.width - (horizontal ? (block + length) : 6),
                bounds.height - (horizontal ? 6 : (block + length)),
                enabled ? c_border : c_disable,
                c_bg, horizontal, !horizontal, true, true);
    }
    else if ("splitpane" == classname) {
      boolean horizontal = ("vertical" != get(component, "orientation"));
      int divider = getInteger(component, "divider", -1);
      paintRect(g, horizontal ? divider : 0, horizontal ? 0 : divider,
                horizontal ? 5 : bounds.width, horizontal ? bounds.height : 5,
                c_border, c_bg, false, false, false, false);
      g.setColor(enabled ? (focus ? c_focus : c_border) : c_disable);
      int xy = horizontal ? bounds.height : bounds.width;
      int xy1 = Math.max(0, xy / 2 - 12);
      int xy2 = Math.min(xy / 2 + 12, xy - 1);
      for (int i = divider + 1; i < divider + 4; i += 2) {
        if (horizontal) {
          g.drawLine(i, xy1, i, xy2);
        }
        else {
          g.drawLine(xy1, i, xy2, i);
        }
      }
      Object comp1 = get(component, "component");
      if (comp1 != null) {
        paint(g, clipx, clipy, clipwidth, clipheight, comp1, enabled);
        Object comp2 = get(comp1, ":next");
        if (comp2 != null) {
          paint(g, clipx, clipy, clipwidth, clipheight, comp2, enabled);
        }
      }
    }
    else if (("list" == classname) ||
             ("table" == classname) || ("tree" == classname)) {
      Rectangle view = getRectangle(component, ":view");
      Rectangle viewport = getRectangle(component, ":port");
      int[] columnwidths = null;
      int lx = clipx - viewport.x - 1;
      int xfrom = view.x + Math.max(0, lx);
      int xto = view.x + Math.min(viewport.width - 2, lx + clipwidth);
      if ("table" == classname) {
        columnwidths = new int[getItemCountImpl(component, "column")];
        int i = 0;
        int x = 0;
        boolean drawheader = (clipy < viewport.y);
        if (drawheader) {
          g.setClip(viewport.x, 0, viewport.width, viewport.y);
        }
        for (Object column = get(component, "column");
             column != null; column = get(column, ":next")) {
          boolean lastcolumn = (i == columnwidths.length - 1);
          int width = getInteger(column, "width", 80);
          if (lastcolumn) {
            width = Math.max(width, viewport.width - x);
          }
          columnwidths[i] = width;
          if (drawheader && (xfrom < x + width) && (xto > x)) {
            paintRect(g, x - view.x, 0, width, viewport.y,
                      enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg,
                      true, true, false, lastcolumn);
            paintContent(column, g, clipx, clipy, clipwidth, clipheight,
                         x + 2 - view.x, 1, width - 2,
                         viewport.y - 2, enabled ? c_text : c_disable, "left", false);
          }
          i++;
          x += width;
        }
        if (drawheader) {
          g.setClip(clipx, clipy, clipwidth, clipheight);
        }
      }
      paintRect(g, viewport.x, viewport.y, viewport.width, viewport.height,
                enabled ? c_border : c_disable, c_textbg, true, true, true, true);
      if (paintScrollPane(g, clipx, clipy, clipwidth, clipheight, bounds,
                          view, viewport, enabled, inside, pressed)) {
        Object lead = get(component, "lead");
        int ly = clipy - viewport.y - 1;
        int yfrom = view.y + Math.max(0, ly);
        int yto = view.y + Math.min(viewport.height - 2, ly + clipheight);
        for (Object item = get(component, ("list" == classname) ? "item" :
                                          (("table" == classname) ? "row" : "node")); item != null; ) {
          Rectangle r = getRectangle(item, "bounds");
          if (lead == null) {
            set(component, "lead", lead = item); // draw first item focused when lead is null
          }
          if (yto <= r.y) {
            break;
          } // the clip bounds are above

          Object next = ("tree" == classname) ? get(item, "node") : null;
          boolean expanded = (next != null) &&
                             getBoolean(item, "expanded", true);
          if (yfrom < r.y + r.height) { // the clip rectangle is not bellow the current item
            boolean selected = getBoolean(item, "selected", false);
            paintRect(g, 0, r.y, view.width, r.height,
                      c_bg, selected ? c_select : c_textbg, false, false, true, false);
            if (focus && (lead == item)) {
              g.setColor(c_focus);
              g.drawRect(0, r.y, view.width - 1, r.height - 2);
            }
            if ("table" == classname) {
              int x = 0;
              int i = 0;
              for (Object cell = get(item, "cell");
                   cell != null; cell = get(cell, ":next")) {
                if (xto <= x) {
                  break;
                }
                int iwidth = (i < columnwidths.length) ? columnwidths[i] : 80;
                if (xfrom < x + iwidth) {
                  boolean cellenabled = enabled && getBoolean(cell, "enabled", true);
                  paintContent(cell, g, xfrom, yfrom, xto - xfrom, yto - yfrom,
                               r.x + x + 1, r.y + 1, iwidth - 2, r.height - 3,
                               cellenabled ? c_text : c_disable, "left", false);
                }
                x += iwidth;
                i++;
              }
            }
            else {
              boolean itemenabled = enabled && getBoolean(item, "enabled", true);
              paintContent(item, g, xfrom, yfrom, xto - xfrom, yto - yfrom,
                           r.x + 1, r.y + 1, view.width - r.x - 2,
                           r.height - 3, itemenabled ? c_text : c_disable, "left", false);

              if (next != null) {
                int x = r.x - block / 2;
                int y = r.y + (r.height - 1) / 2;
                //g.drawRect(x - 4, y - 4, 8, 8);
                paintRect(g, x - 4, y - 4, 9, 9, itemenabled ? c_border : c_disable,
                          itemenabled ? c_ctrl : c_bg, true, true, true, true);
                g.setColor(itemenabled ? c_text : c_disable);
                g.drawLine(x - 2, y, x + 2, y);
                if (!expanded) {
                  g.drawLine(x, y - 2, x, y + 2);
                }
              }
            }
          }
          if ("tree" == classname) {
            if ((next == null) || !expanded) {
              while ((item != component) && ((next = get(item, ":next")) == null)) {
                item = getParent(item);
              }
            }
            item = next;
          }
          else {
            item = get(item, ":next");
          }
        }
                /*if (columnwidths != null) {
                    g.setColor(c_bg);
                    for (int i = 0, cx = -1; i < columnwidths.length - 1; i++) {
                        cx += columnwidths[i];
                        g.drawLine(cx, 0, cx, view.height);
                    }
                }*/
        resetScrollPane(g, clipx, clipy, clipwidth, clipheight, view, viewport);
      }
    }
    else if ("separator" == classname) {
      g.setColor(enabled ? c_border : c_disable);
      g.fillRect(0, 0, bounds.width, bounds.height);
    }
    else if ("menubar" == classname) {
      Object selected = get(component, "selected");
      int lastx = 0;
      for (Object menu = get(component, "menu");
           menu != null; menu = get(menu, ":next")) {
        Rectangle mb = getRectangle(menu, "bounds");
        if (clipx + clipwidth <= mb.x) {
          break;
        }
        if (clipx >= mb.x + mb.width) {
          continue;
        }
        boolean armed = (selected == menu);
        boolean hoover = (selected == null) && (insidepart == menu);
        paintRect(g, mb.x, 0, mb.width, bounds.height, enabled ? c_border : c_disable,
                  enabled ? (armed ? c_select : (hoover ? c_hover : c_ctrl)) : c_bg,
                  armed, armed, true, armed);
        paintContent(menu, g, clipx, clipy, clipwidth, clipheight,
                     mb.x + 4, 1, mb.width, bounds.height,
                     (enabled && getBoolean(menu, "enabled", true)) ? c_text : c_disable,
                     "left", true);
        lastx = mb.x + mb.width;
      }
      paintRect(g, lastx, 0, bounds.width - lastx, bounds.height,
                enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg,
                false, false, true, false);
    }
    else if ("popupmenu" == classname) {
      paintRect(g, 0, 0, bounds.width, bounds.height,
                c_border, c_bg, true, true, true, true);
      Object selected = get(component, "selected");
      for (Object menu = get(get(component, "menu"), "menu");
           menu != null; menu = get(menu, ":next")) {
        Rectangle r = getRectangle(menu, "bounds");
        if (clipy + clipheight <= r.y) {
          break;
        }
        if (clipy >= r.y + r.height) {
          continue;
        }
        String itemclass = getClass(menu);
        if (itemclass == "separator") {
          g.setColor(c_border);
          g.fillRect(r.x, r.y, bounds.width - 2, r.height);
        }
        else {
          boolean armed = (selected == menu);
          boolean menuenabled = getBoolean(menu, "enabled", true);
          paintRect(g, r.x, r.y, bounds.width - 2, r.height, c_border,
                    armed ? c_select : c_bg, false, false, false, false);
          int tx = r.x;
          if (itemclass == "checkboxmenuitem") {
            tx += block + 3;
            boolean checked = getBoolean(menu, "selected", false);
            String group = getString(menu, "group", null);
            g.translate(r.x + 4, r.y + 2);
            g.setColor(menuenabled ? c_border : c_disable);
            if (group == null) {
              g.drawRect(1, 1, block - 3, block - 3);
            }
            else {
              g.drawOval(1, 1, block - 3, block - 3); //java
            }
            if (checked) {
              g.setColor(menuenabled ? c_text : c_disable);
              if (group == null) {
                g.fillRect(3, block - 9, 2, 6);
                g.drawLine(3, block - 4, block - 4, 3);
                g.drawLine(4, block - 4, block - 4, 4);
              }
              else {
                g.fillOval(5, 5, block - 10, block - 10); //java
                g.drawOval(4, 4, block - 9, block - 9); //java
              }
            }
            g.translate(-r.x - 4, -r.y - 2);
          }
          paintContent(menu, g, clipx, clipy, clipwidth, clipheight,
                       tx + 4, r.y + 2, bounds.width - 10,
                       r.height - 4, menuenabled ? c_text : c_disable, "left", true);
          if (itemclass == "menu") {
            paintArrow(g, r.x + bounds.width - block, r.y, block, r.height, 'E');
          }
        }
      }
    }
    //java>
    else if ("bean" == classname) {
      g.clipRect(0, 0, bounds.width, bounds.height);
      ((Component)get(component, "bean")).paint(g);
      g.setClip(clipx, clipy, clipwidth, clipheight);
    }
    //<java
    else {
      throw new IllegalArgumentException((String)classname);
    }
    g.translate(-bounds.x, -bounds.y);
    clipx += bounds.x;
    clipy += bounds.y;
  }

  /**
   *
   */
  private void paintReverse(Graphics g,
                            int clipx, int clipy, int clipwidth, int clipheight,
                            Object component, boolean enabled) {
    if (component != null) {
      Rectangle bounds = getRectangle(component, "bounds");
      if ((clipx < bounds.x) ||
          (clipx + clipwidth > bounds.x + bounds.width) ||
          (clipy < bounds.y) ||
          (clipy + clipheight > bounds.y + bounds.height)) {
        paintReverse(g, clipx, clipy, clipwidth, clipheight,
                     get(component, ":next"), enabled);
      }
      paint(g, clipx, clipy, clipwidth, clipheight, component, enabled);
    }
  }

  /**
   *
   */
  private void paintField(Graphics g,
                          int clipx, int clipy, int clipwidth, int clipheight, Object component,
                          int width, int height, boolean inside, boolean pressed,
                          boolean focus, boolean enabled, boolean hidden, int left) {
    java.lang.ref.Reference.reachabilityFence(inside);
    java.lang.ref.Reference.reachabilityFence(pressed);
    boolean editable = getBoolean(component, "editable", true);
    paintRect(g, 0, 0, width, height, enabled ? c_border : c_disable,
              editable ? c_textbg : c_bg, true, true, true, true);
    g.clipRect(1 + left, 1, width - left - 2, height - 2);

    String text = getString(component, "text", "");
    int offset = getInteger(component, "offset", 0);
    FontMetrics fm = g.getFontMetrics(); //java

    int caret = 0;
    if (focus) {
      int start = getInteger(component, "start", 0);
      int end = getInteger(component, "end", 0);
      caret = hidden ? (fm.charWidth('*') * end) :
              fm.stringWidth(text.substring(0, end)); //java
      //midp font.substringWidth(text, 0, end);
      if (start != end) {
        int is = hidden ? (fm.charWidth('*') * start) :
                 fm.stringWidth(text.substring(0, start)); //java
        //midp font.substringWidth(text, 0, start);
        g.setColor(c_select);
        g.fillRect(2 + left - offset + Math.min(is, caret), 1,
                   Math.abs(caret - is), height - 2);
      }
    }

    if (focus) {
      g.setColor(c_focus);
      g.fillRect(1 + left - offset + caret, 1, 1, height - 2);
    }

    g.setColor(enabled ? c_text : c_disable);
    int fx = 2 + left - offset;
    int fy = (height + fm.getAscent() - fm.getDescent()) / 2; //java
    //midp int fy = (height - font.getHeight()) / 2;
    if (hidden) {
      int fh = fm.charWidth('*');
      for (int i = text.length(); i > 0; i--) {
        g.drawString("*", fx, fy); //java
        //midp g.drawChar('*', fx, fy, Graphics.LEFT | Graphics.TOP);
        fx += fh;
      }
    }
    else {
      g.drawString(text, fx, fy); //java
      //midp g.drawString(text, fx, fy, Graphics.LEFT | Graphics.TOP);
    }
    g.setClip(clipx, clipy, clipwidth, clipheight);
  }

  /**
   *
   */
  private boolean paintScrollPane(Graphics g,
                                  int clipx, int clipy, int clipwidth, int clipheight, Rectangle bounds,
                                  Rectangle view, Rectangle viewport,
                                  boolean enabled, boolean inside, boolean pressed) {
    if ((viewport.y + viewport.height < bounds.height) &&
        (clipy + clipheight > viewport.y + viewport.height)) { // need horizontal
      int x = viewport.x;
      int y = viewport.y + viewport.height;
      int height = bounds.height - y;
      int button = Math.min(block, viewport.width / 2);
      int track = viewport.width - (2 * button); //max 10
      int knob = Math.min(track, Math.max(track * (viewport.width - 2) / view.width, 6));
      int decrease = view.x * (track - knob) /
                     (view.width - viewport.width + 2);
      int increase = track - decrease - knob;
      paintArrow(g, x, y, button, height,
                 'W', enabled, inside, pressed, "left", false, true, true, false);
      paintRect(g, x + button, y, decrease, height,
                enabled ? c_border : c_disable, c_bg, false, true, true, false);
      paintRect(g, x + button + decrease, y, knob, height,
                enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, false, true, true, true);
      int n = Math.min(5, (knob - 4) / 3);
      g.setColor(enabled ? c_border : c_disable);
      int cx = (x + button + decrease) + (knob + 2 - n * 3) / 2;
      for (int i = 0; i < n; i++) {
        g.drawLine(cx + i * 3, y + 2, cx + i * 3, y + height - 4);
      }
      paintRect(g, x + button + decrease + knob, y, increase, height,
                enabled ? c_border : c_disable, c_bg, false, false, true, true);
      paintArrow(g, x + button + track, y, button, height,
                 'E', enabled, inside, pressed, "right", false, false, true, true);
    }
    if ((viewport.x + viewport.width < bounds.width) &&
        (clipx + clipwidth > viewport.x + viewport.width)) { // need vertical
      int x = viewport.x + viewport.width;
      int y = viewport.y;
      int width = bounds.width - x;
      //if (y > 0) {
      //    paintRect(g, x - 1, 0, width + 1, y,
      //        enabled ? c_border : c_disable, c_bg, false, true, false, false);
      //}
      int track = viewport.height - (2 * block);
      int knob = track * (viewport.height - 2) / view.height;
      int decrease = view.y * (track - knob) /
                     (view.height - viewport.height + 2);
      int increase = track - decrease - knob;
      paintArrow(g, x, y, width, block,
                 'N', enabled, inside, pressed, "up", true, false, false, true);
      paintRect(g, x, y + block, width, decrease,
                enabled ? c_border : c_disable, c_bg, true, false, false, true);
      paintRect(g, x, y + block + decrease, width, knob,
                enabled ? c_border : c_disable, enabled ? c_ctrl : c_bg, true, false, true, true);
      int n = Math.min(5, (knob - 4) / 3);
      g.setColor(enabled ? c_border : c_disable);
      int cy = (y + block + decrease) + (knob + 2 - n * 3) / 2;
      for (int i = 0; i < n; i++) {
        g.drawLine(x + 2, cy + i * 3, x + width - 4, cy + i * 3);
      }
      paintRect(g, x, y + block + decrease + knob, width, increase,
                enabled ? c_border : c_disable, c_bg, false, false, true, true);
      paintArrow(g, x, y + block + track, width, block,
                 'S', enabled, inside, pressed, "down", false, false, true, true);
    }
    if ((clipx + clipwidth > viewport.x) && (clipy + clipheight > viewport.y) &&
        (clipx < viewport.x + viewport.width) && (clipy < viewport.y + viewport.height)) {
      g.clipRect(viewport.x + 1, viewport.y + 1, viewport.width - 2, viewport.height - 2);
      g.translate(viewport.x + 1 - view.x, viewport.y + 1 - view.y);
      return true;
    }
    return false;
  }

  /**
   *
   */
  private void resetScrollPane(Graphics g,
                               int clipx, int clipy, int clipwidth, int clipheight,
                               Rectangle view, Rectangle viewport) {
    g.translate(view.x - viewport.x - 1, view.y - viewport.y - 1);
    g.setClip(clipx, clipy, clipwidth, clipheight);
  }

  /**
   *
   */
  private void paintRect(Graphics g, int x, int y, int width, int height,
                         Color border, Color bg,
                         boolean top, boolean left, boolean bottom, boolean right) {
    if ((width <= 0) || (height <= 0)) return;
    g.setColor(border);
    if (top) {
      g.drawLine(x + width - 1, y, x, y);
      y++;
      height--;
      if (height <= 0) return;
    }
    if (left) {
      g.drawLine(x, y, x, y + height - 1);
      x++;
      width--;
      if (width <= 0) return;
    }
    if (bottom) {
      g.drawLine(x, y + height - 1, x + width - 1, y + height - 1);
      height--;
      if (height <= 0) return;
    }
    if (right) {
      g.drawLine(x + width - 1, y + height - 1, x + width - 1, y);
      width--;
      if (width <= 0) return;
    }

    //java>
    if (bg == c_ctrl) {
      if (height > block) {
        g.setColor(c_bg);
        g.fillRect(x, y, width, height - block);
      }
      for (int i = 0; i < width; i += block) {
        g.drawImage(gradient, x + i, (height > block) ? (y + height - block) : y,
                    x + Math.min(i + block, width), y + height,
                    0, 0, Math.min(block, width - i), Math.min(block, height), null);
      }
            /*if (width > block) {
                g.setColor(c_bg);
                g.fillRect(x, y, width - block, height);
            }
            for (int i = 0; i < height; i += block) {
                g.drawImage(gradient, (width > block) ? (x + width - block) : x, y + i,
                    x + width, y + Math.min(i + block, height),
                    0, 0, Math.min(block, width), Math.min(block, height - i), null);
            }*/
    }
    else {
      g.setColor(bg);
      g.fillRect(x, y, width, height);
    }
    //<java
    //midp g.setColor(bg);
    //midp g.fillRect(x, y, width, height);
  }

  /**
   *
   */
  private void paintArrow(Graphics g, int x, int y, int width, int height,
                          char dir, boolean enabled, boolean inside, boolean pressed, String part,
                          boolean top, boolean left, boolean bottom, boolean right) {
    inside = inside && (insidepart == part);
    pressed = pressed && (pressedpart == part);
    paintRect(g, x, y, width, height, enabled ? c_border : c_disable,
              enabled ? ((inside != pressed) ? c_hover :
                         (pressed ? c_press : c_ctrl)) : c_bg,
              top, left, bottom, right);
    g.setColor(enabled ? c_text : c_disable);
    paintArrow(g, x, y, width, height, dir);
  }

  /**
   *
   */
  private void paintArrow(Graphics g,
                          int x, int y, int width, int height, char dir) {
    int cx = x + width / 2 - 2;
    int cy = y + height / 2 - 2;
    for (int i = 0; i < 4; i++) {
      if (dir == 'N') { // north
        g.drawLine(cx + 1 - i, cy + i, cx + 1/*2*/ + i, cy + i);
      }
      else if (dir == 'W') { // west
        g.drawLine(cx + i, cy + 1 - i, cx + i, cy + 1/*2*/ + i);
      }
      else if (dir == 'S') { // south
        g.drawLine(cx + 1 - i, cy + 4 - i, cx + 1/*2*/ + i, cy + 4 - i);
      }
      else { // east
        g.drawLine(cx + 4 - i, cy + 1 - i, cx + 4 - i, cy + 1/*2*/ + i);
      }
    }
  }

  /**
   *
   */
  private void paintContent(Object component, Graphics g,
                            int clipx, int clipy, int clipwidth, int clipheight,
                            int x, int y, int width, int height, Color fg, String defaultalignment,
                            boolean checkmnemonic) {
    String text = getString(component, "text", null);
    Image icon = getIcon(component, "icon", null);
    if ((text == null) && (icon == null)) {
      return;
    }
    String alignment = getString(component, "alignment", defaultalignment);

    FontMetrics fm = null; //java
    int tw = 0, th = 0;
    int ta = 0; //java
    if (text != null) {
      fm = g.getFontMetrics(); //java
      tw = fm.stringWidth(text);
      ta = fm.getAscent(); //java
      th = fm.getDescent() + ta; //java
      //midp th = font.getHeight();
      g.setColor(fg);
    }
    int iw = 0, ih = 0;
    if (icon != null) {
      iw = icon.getWidth(this);
      ih = icon.getHeight(this); //java
    }

    boolean clipped = (tw + iw > width) || (th > height) || (ih > height);
    int cx = x;
    if ("center" == alignment) {
      cx += (width - tw - iw) / 2;
    }
    else if ("right" == alignment) {
      cx += width - tw - iw;
    }

    if (clipped) {
      g.clipRect(x, y, width, height);
    }
    if (icon != null) {
      g.drawImage(icon, cx, y + (height - ih) / 2, this); //java
      //midp g.drawImage(icon, cx, y + height / 2, Graphics.LEFT | Graphics.VCENTER);
      cx += iw;
    }
    if (text != null) {
      int ty = y + (height - th) / 2 + ta; //java
      g.drawString(text, cx, ty); //java
      //midp g.drawString(text, cx, y + (height - th) / 2, Graphics.LEFT | Graphics.TOP);
      if (checkmnemonic) {
        int mnemonic = getInteger(component, "mnemonic", -1);
        if ((mnemonic != -1) && (mnemonic < text.length())) {
          int mx = cx + fm.stringWidth(text.substring(0, mnemonic)); //java
          //midp int mx = cx + font.substringWidth(text, 0, mnemonic);
          //midp int ty = (height + th) / 2;
          g.drawLine(mx, ty + 1, mx + fm.charWidth(text.charAt(mnemonic)), ty + 1);
        }
      }
    }
    if (clipped) {
      g.setClip(clipx, clipy, clipwidth, clipheight);
    }
  }
  //midp private void setTimer(long delay) {}
  //java>

  /**
   *
   */
  public synchronized void run() {
    while (timer == Thread.currentThread()) {
      try {
        if (watch == 0) {
          wait(0);
        }
        else {
          long current = System.currentTimeMillis();
          if (watch > current) {
            wait(watch - current);
          }
          else {
            watch = 0;
            if ((watchdelay == 300L) || (watchdelay == 60L)) {
              if (processScroll(mousepressed, pressedpart)) {
                setTimer(60L);
              }
            }
            else if ((watchdelay == 375L) || (watchdelay == 75L)) {
              if (processSpin(mousepressed, pressedpart)) {
                setTimer(75L);
              }
            }
            else if (watchdelay == 750L) {
              //System.out.println("> tip: " + getClass(mouseinside) + " : " + ((insidepart instanceof Object[]) ? getClass(insidepart) : insidepart));
              showTip();
            }
          }
        }
      }
      catch (InterruptedException ie) {
      } //ie.printStackTrace();
    }
  }

  /**
   *
   */
  private void setTimer(long delay) {
    watchdelay = delay;
    if (delay == 0) {
      watch = 0;
    }
    else {
      long prev = watch;
      watch = System.currentTimeMillis() + delay;
      if (timer == null) {
        timer = new Thread(this);
        timer.setPriority(Thread.MIN_PRIORITY);
        timer.setDaemon(true);
        timer.start();
      }
      if ((prev == 0) || (watch < prev)) {
        synchronized (this) {
          notify();
        } //try {}catch (IllegalMonitorStateException imse) {}
      }
    }
  }

  /**
   *
   */
  public boolean isFocusTraversable() {
    return true;
  }

  //<java
    /*midp
    private static final int MOUSE_ENTERED = 1;
    private static final int MOUSE_EXITED = 2; //
    private static final int MOUSE_PRESSED = 3;
    private static final int MOUSE_DRAGGED = 4;
    private static final int MOUSE_RELEASED = 5;
    private static final int DRAG_ENTERED = 6;
    private static final int DRAG_EXITED = 7;

    protected void pointerPressed(int x, int y) {
        findComponent(content, x, y);
        if (popupowner != null) {
            String classname = getClass(mouseinside);
            if ((popupowner != mouseinside) &&
                    (classname != "popupmenu") && (classname != "combolist")) {
                closeup();
            }
        }
        handleMouseEvent(x, y, 1, false, false, false,
            MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
        mousepressed = mouseinside;
        pressedpart = insidepart;
        handleMouseEvent(x, y, 1, false, false, false,
            MouseEvent.MOUSE_PRESSED, mousepressed, pressedpart);
    }

    protected void pointerReleased(int x, int y) {
        Object mouserelease = mousepressed;
        Object releasepart = pressedpart;
        mousepressed = pressedpart = null;
        handleMouseEvent(x, y, 1, false, false, false,
            MouseEvent.MOUSE_RELEASED, mouserelease, releasepart);
    }

    protected void pointerDragged(int x, int y) {
        Object previnside = mouseinside;
        Object prevpart = insidepart;
        findComponent(content, x, y);
        boolean same = (previnside == mouseinside) && (prevpart == insidepart);
        boolean isin = (mousepressed == mouseinside) && (pressedpart == insidepart);
        boolean wasin = (mousepressed == previnside) && (pressedpart == prevpart);

        if (wasin && !isin) {
            handleMouseEvent(x, y, 1, false, false, false,
                MouseEvent.MOUSE_EXITED, mousepressed, pressedpart);
        }
        else if (!same && (popupowner != null) && !wasin) {
            handleMouseEvent(x, y, 1, false, false, false,
                DRAG_EXITED, previnside, prevpart);
        }
        if (isin && !wasin) {
            handleMouseEvent(x, y, 1, false, false, false,
                MouseEvent.MOUSE_ENTERED, mousepressed, pressedpart);
        }
        else if (!same && (popupowner != null) && !isin) {
            handleMouseEvent(x, y, 1, false, false, false,
                DRAG_ENTERED, mouseinside, insidepart);
        }
        if (isin == wasin) {
            handleMouseEvent(x, y, 1, false, false, false,
                MouseEvent.MOUSE_DRAGGED, mousepressed, pressedpart);
        }
    }

    protected void keyPressed(int keyCode) {
        if ((popupowner != null) || (focusowner != null)) {
            hideTip();
            if (keyCode > 0) {
                processKeyPress((popupowner != null) ? popupowner : focusowner,
                    false, false, 1, keyCode, 0);
            }
            else {
                int keychar = 0, key = 0;
                switch (getGameAction(keyCode)) {
                    case UP: key = KeyEvent.VK_UP; break;
                    case LEFT: key = KeyEvent.VK_LEFT; break;
                    case DOWN: key = KeyEvent.VK_DOWN; break;
                    case RIGHT: key = KeyEvent.VK_RIGHT; break;
                    case FIRE: key = KeyEvent.VK_ENTER; keychar = KeyEvent.VK_SPACE; break;
                    case GAME_A: key = KeyEvent.VK_ESCAPE; break;
                }
                if (key != 0) {
                    processKeyPress((popupowner != null) ? popupowner : focusowner,
                        false, false, 1, keychar, key);
                }
                //if (keyCode == getKeyCode(LEFT)) {
            }
        }
    }

    protected void keyRepeated(int keyCode) {
        keyPressed(keyCode);
    }

    private static final Command nextcommand = new Command("Next", Command.SCREEN, 0);
    //private static final Command prevcommand = new Command("Previous", Command.SCREEN, 0);
    {
        addCommand(nextcommand);
        //addCommand(prevcommand);
        setCommandListener(this);
    }

    public void commandAction(Command command, Displayable displayable) {
        if (command == nextcommand) {
            setNextFocusable(focusowner, false);
            repaint(focusowner);
            closeup();
        }
        //else if (command == prevcommand) {
            //setPreviousFocusable(focusowner, null, true, true, false);
            //repaint(focusowner);
            //closeup();
        //}
    }
    midp*/
  //java>

  /**
   *
   */
  protected void processEvent(AWTEvent e) {
    int id = e.getID();
    if ((id >= MouseEvent.MOUSE_FIRST) && (id <= MouseEvent.MOUSE_LAST)) {
      MouseEvent me = (MouseEvent)e;
      int x = me.getX();
      int y = me.getY();
      int clickcount = me.getClickCount();
      boolean shiftdown = me.isShiftDown();
      boolean controldown = me.isControlDown();
      boolean popuptrigger = me.isPopupTrigger();
      if (id == MouseEvent.MOUSE_ENTERED) {
        if (mousepressed == null) {
          findComponent(content, x, y);
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
        }
      }
      else if (id == MouseEvent.MOUSE_MOVED) {
        Object previnside = mouseinside;
        Object prevpart = insidepart;
        findComponent(content, x, y);
        if ((previnside == mouseinside) && (prevpart == insidepart)) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_MOVED, mouseinside, insidepart);
        }
        else {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_EXITED, previnside, prevpart);
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
        }
      }
      else if (id == MouseEvent.MOUSE_EXITED) {
        if (mousepressed == null) {
          Object mouseexit = mouseinside;
          Object exitpart = insidepart;
          mouseinside = insidepart = null;
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_EXITED, mouseexit, exitpart);
        }
      }
      else if (id == MouseEvent.MOUSE_PRESSED) {
        if (popupowner != null) {
          String classname = getClass(mouseinside);
          if ((popupowner != mouseinside) &&
              (classname != "popupmenu") && (classname != "combolist")) {
            closeup();
          }
        }
        mousepressed = mouseinside;
        pressedpart = insidepart;
        handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                         MouseEvent.MOUSE_PRESSED, mousepressed, pressedpart);
      }
      else if (id == MouseEvent.MOUSE_DRAGGED) {
        Object previnside = mouseinside;
        Object prevpart = insidepart;
        findComponent(content, x, y);
        boolean same = (previnside == mouseinside) && (prevpart == insidepart);
        boolean isin = (mousepressed == mouseinside) && (pressedpart == insidepart);
        boolean wasin = (mousepressed == previnside) && (pressedpart == prevpart);

        if (wasin && !isin) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_EXITED, mousepressed, pressedpart);
        }
        else if (!same && (popupowner != null) && !wasin) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           DRAG_EXITED, previnside, prevpart);
        }
        if (isin && !wasin) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_ENTERED, mousepressed, pressedpart);
        }
        else if (!same && (popupowner != null) && !isin) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           DRAG_ENTERED, mouseinside, insidepart);
        }
        if (isin == wasin) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_DRAGGED, mousepressed, pressedpart);
        }
      }
      else if (id == MouseEvent.MOUSE_RELEASED) {
        Object mouserelease = mousepressed;
        Object releasepart = pressedpart;
        mousepressed = pressedpart = null;
        handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                         MouseEvent.MOUSE_RELEASED, mouserelease, releasepart);
        if ((mouseinside != null) &&
            ((mouserelease != mouseinside) || (releasepart != insidepart))) {
          handleMouseEvent(x, y, clickcount, shiftdown, controldown, popuptrigger,
                           MouseEvent.MOUSE_ENTERED, mouseinside, insidepart);
        }
      }
    }
    else if (id == MOUSE_WHEEL) {
      Rectangle port = getRectangle(mouseinside, ":port");
      if (port != null) { // is scrollable
        Rectangle bounds = getRectangle(mouseinside, "bounds");
        try { // mouse wheel is supported since 1.4 thus it use reflection
          if (wheelrotation == null) {
            wheelrotation = e.getClass().getMethod("getWheelRotation", null);
          }
          int rotation = ((Integer)wheelrotation.invoke(e, null)).intValue();

          if (port.x + port.width < bounds.width) { // has vertical scrollbar
            processScroll(mouseinside, (rotation > 0) ? "down" : "up");
          }
          else if (port.y + port.height < bounds.height) { // has horizontal scrollbar
            processScroll(mouseinside, (rotation > 0) ? "right" : "left");
          }
        }
        catch (Exception exc) { /* never */ }
      }
    }
    else if ((id == KeyEvent.KEY_PRESSED) || (id == KeyEvent.KEY_TYPED)) {
      if (focusinside && ((popupowner != null) || (focusowner != null))) {
        hideTip();
        KeyEvent ke = (KeyEvent)e;
        int keychar = ke.getKeyChar();
        boolean control = (keychar <= 0x1f) ||
                          ((keychar >= 0x7f) && (keychar <= 0x9f)) ||
                          (keychar >= 0xffff) || ke.isControlDown();
        if (control == (id == KeyEvent.KEY_PRESSED)) {
          int keycode = control ? ke.getKeyCode() : 0;
          if (!processKeyPress((popupowner != null) ? popupowner : focusowner,
                               ke.isShiftDown(), ke.isControlDown(), ke.getModifiers(),
                               control ? 0 : keychar, keycode)) {
            if ((keycode == KeyEvent.VK_TAB) ||
                ((keycode == KeyEvent.VK_F6) && (ke.isAltDown() || ke.isControlDown()))) {
              boolean outgo = (keycode == KeyEvent.VK_F6);
              if (!ke.isShiftDown() ? setNextFocusable(focusowner, outgo) :
                  setPreviousFocusable(focusowner, outgo)) {
                ke.consume();
              }
              else if (MOUSE_WHEEL != 0) { // 1.4
                if (!ke.isShiftDown()) {
                  transferFocus();
                }
                else {
                  try {
                    getClass().getMethod("transferFocusBackward", null).invoke(this, null);
                  }
                  catch (Exception exc) { /* never */ }
                }
              }
              repaint(focusowner);
              closeup();
            }
            else if (keycode == KeyEvent.VK_F8) {
              for (Object splitpane = focusowner;
                   splitpane != null; splitpane = getParent(splitpane)) {
                if (getClass(splitpane) == "splitpane") {
                  setFocus(splitpane);
                  repaint(splitpane);
                  break; //middle
                }
              }
            }
          }
          else {
            ke.consume();
          }
                    /*else if (keycode == KeyEvent.VK_F10) {
                        Object menubar = null; // find("class", "menubar")
                        if ((menubar != null) && (get(menubar, "selected") == null)) {
                            set(menubar, "selected", getMenu(menubar, null, true, false));
                            Object popup = popup(menubar, "menubar");
                            set(popup, "selected", getMenu(popup, null, true, true));
                            repaint(menubar); // , selected
                        }
                    }*/
        }
      }
    }
        /*else if (id == KeyEvent.KEY_RELEASED) {
            if (focusinside && (focusowner != null)) {
                KeyEvent ke = (KeyEvent) e;
                //pressedkey = 0;
                processKeyRelease(focusowner, ke, ke.getKeyCode());
            }
        }*/
    else if (id == FocusEvent.FOCUS_LOST) {
      focusinside = false;
      if (focusowner != null) {
        repaint(focusowner);
      }
      closeup();
    }
    else if (id == FocusEvent.FOCUS_GAINED) {
      focusinside = true;
      if (focusowner == null) {
        setFocus(content);
      }
      else {
        repaint(focusowner);
      }
    }
    else if ((id == ComponentEvent.COMPONENT_RESIZED) ||
             (id == ComponentEvent.COMPONENT_SHOWN)) {
      Dimension d = getSize();
      //System.out.println(id + ": " + d.width + ", " + d.height);
      setRectangle(content, "bounds", 0, 0, d.width, d.height);
      validate(content);
      closeup();
      if (!focusinside) {
        requestFocus();
      }
    }
  }

  /**
   *
   */
    /*private boolean processKeyPress(Object component,
            KeyEvent e, int keycode, Object invoker) {
        if (processKeyPress(component, e, keycode)) { return true; }
        for (Object comp = get(component, "component");
                comp != null; comp = get(comp, ":next")) {
            if ((comp != invoker) && processKeyPress(comp, e, keycode, null)) {
                return true;
            }
        }
        if ((invoker != null) && (component != content)) {
            Object parent = getParent(component);
            if (parent != null) {
                return processKeyPress(parent, e, keycode, component);
            }
        }
        return false;
    }*/
  //<java

  /**
   *
   */
  private boolean processKeyPress(Object component,
                                  boolean shiftdown, boolean controldown, int modifiers, int keychar, int keycode) {
    String classname = getClass(component);
    if ("button" == classname) {
      if (keychar == KeyEvent.VK_SPACE ||
          ((keycode == KeyEvent.VK_ENTER) &&
           (getString(component, "type", null) == "default")) ||
          ((keycode == KeyEvent.VK_ESCAPE) && //...
           (getString(component, "type", null) == "cancel"))) {
        //pressedkey = keychar;
        invoke(component, "action");
        repaint(component);
        return true;
      }
    }
    else if ("checkbox" == classname) {
      if (keychar == KeyEvent.VK_SPACE) {
        changeCheck(component, true);
        repaint(component);
        return true;
      }
    }
    else if ("combobox" == classname) {
      Object combolist = get(component, "combolist");
      if (combolist == null) {
        if (getBoolean(component, "editable", true) &&
            processField(component, shiftdown, controldown, modifiers,
                         keychar, keycode, false, false)) {
          setInteger(component, "selected", -1, -1);
          return true;
        }
        if ((keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_DOWN)) {
          combolist = popup(component, classname);
          int selected = getInteger(component, "selected", -1);
          set(combolist, "inside", (selected != -1) ?
                                   getItemImpl(component, "choice", selected) :
                                   get(component, "choice")); //scroll to it!
        }
        else {
          return false;
        }
      }
      else {
        if ((keycode == KeyEvent.VK_UP) ||
            (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
            (keycode == KeyEvent.VK_PAGE_DOWN) ||
            (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
          Object selected = get(combolist, "inside");
          Object next = getListItem(component, combolist,
                                    keycode, selected, "choice", null);
          if (next != null) {
            set(combolist, "inside", next);
            Rectangle r = getRectangle(next, "bounds");
            scrollToVisible(combolist, r.x, r.y, 0, r.height);
            if (selected != null) {
              repaint(combolist, "combolist", selected);
            }
            repaint(combolist, "combolist", next);
          }
        }
        else if ((keycode == KeyEvent.VK_ENTER) || (keychar == KeyEvent.VK_SPACE)) {
          closeup(component, combolist, get(combolist, "inside")); //Alt+Up
        }
        else if (keycode == KeyEvent.VK_ESCAPE) {
          closeup(component, combolist, null);
        }
        else {
          return processField(component, shiftdown, controldown, modifiers,
                              keychar, keycode, false, false);
        }
      }
      return true;
    }
    else if (("textfield" == classname) || ("passwordfield" == classname)) {
      return processField(component, shiftdown, controldown, modifiers,
                          keychar, keycode, false, ("passwordfield" == classname));
    }
    else if ("textarea" == classname) {
      String text = getString(component, "text", "");
      int start = getInteger(component, "start", 0);
      int end = getInteger(component, "end", 0);

      int istart = start;
      int iend = end;
      String insert = null;
      if ((keycode == KeyEvent.VK_HOME) && !controldown) {
        while ((iend > 0) && (text.charAt(iend - 1) != '\n')) {
          iend--;
        }
        if (!shiftdown) {
          istart = iend;
        }
      }
      else if ((keycode == KeyEvent.VK_END) && !controldown) {
        iend = text.indexOf('\n', end);
        if (iend == -1) {
          iend = text.length();
        }
        if (!shiftdown) {
          istart = iend;
        }
      }
      else if ((keycode == KeyEvent.VK_UP) ||
               (keycode == KeyEvent.VK_PAGE_UP)) {
        int prev = end;
        while ((prev > 0) && (text.charAt(prev - 1) != '\n')) {
          prev--;
        }
        if (prev != 0) {
          int dx = end - prev;
          int lines = (keycode == KeyEvent.VK_PAGE_UP) ?
                      (getRectangle(component, ":port").height /
                       getFontMetrics(getFont()).getHeight()) : 1;
          int first = prev;
          do {
            prev = first;
            first--;
            lines--;
            while ((first > 0) && (text.charAt(first - 1) != '\n')) {
              first--;
            }
          }
          while ((first > 0) && (lines > 0));
          iend = Math.min(first + dx, prev - 1);
          if (!shiftdown) {
            istart = iend;
          }
        }
      }
      else if ((keycode == KeyEvent.VK_DOWN) ||
               (keycode == KeyEvent.VK_PAGE_DOWN)) {
        int next = text.indexOf('\n', end);
        if (next != -1) {
          int prev = end;
          while ((prev > 0) && (text.charAt(prev - 1) != '\n')) {
            prev--;
          }
          if (keycode == KeyEvent.VK_PAGE_DOWN) {
            int lines = getRectangle(component, ":port").height /
                        getFontMetrics(getFont()).getHeight();
            for (int more = 0; (lines > 1) &&
                               ((more = text.indexOf('\n', next + 1)) != -1); next = more) {
              lines--;
            }
          }
          int last = text.indexOf('\n', next + 1);
          iend = Math.min(next + 1 + end - prev,
                          (last == -1) ? (text.length() + 1) : last);
          if (!shiftdown) {
            istart = iend;
          }
        }
      }
      return changeField(component, text, insert, istart, iend, start, end) ?
             true : processField(component, shiftdown, controldown, modifiers,
                                 keychar, keycode, true, false);
    }
    else if ("tabbedpane" == classname) {
      if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN) ||
          (keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
        int selected = getInteger(component, "selected", 0);
        boolean increase = (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN);
        int newvalue = selected;
        int n = increase ? getItemCountImpl(component, "tab") : 0;
        int d = (increase ? 1 : -1);
        for (int i = selected + d; increase ? (i < n) : (i >= 0); i += d) {
          if (getBoolean(getItemImpl(component, "tab", i), "enabled", true)) {
            newvalue = i;
            break;
          }
        }
        if (newvalue != selected) {
          setInteger(component, "selected", newvalue, 0);
          repaint(component);
          invoke(component, "action");
        }
      }
    }
    else if ("spinbox" == classname) {
      if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
        processSpin(component, (keycode == KeyEvent.VK_UP) ? "up" : "down");
        return true;
      }
      return processField(component, shiftdown, controldown, modifiers,
                          keychar, keycode, false, false);
    }
    else if ("slider" == classname) {
      int value = getInteger(component, "value", 0);
      int d = 0;
      if ((keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_LEFT) ||
          (keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_PAGE_UP)) {
        d = getInteger(component, "minimum", 0) - value;
        if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
          d = Math.max(d, -getInteger(component, "unit", 5));
        }
        else if (keycode == KeyEvent.VK_PAGE_UP) {
          d = Math.max(d, -getInteger(component, "block", 25));
        }
      }
      else if ((keycode == KeyEvent.VK_END) || (keycode == KeyEvent.VK_RIGHT) ||
               (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_DOWN)) {
        d = getInteger(component, "maximum", 100) - value;
        if ((keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
          d = Math.min(d, getInteger(component, "unit", 5));
        }
        else if (keycode == KeyEvent.VK_PAGE_DOWN) {
          d = Math.min(d, getInteger(component, "block", 25));
        }
      }
      if (d != 0) {
        setInteger(component, "value", value + d, 0);
        repaint(component);
        invoke(component, "action");
      }
    }
    else if ("splitpane" == classname) {
      int divider = getInteger(component, "divider", -1);
      int d = 0;
      if (keycode == KeyEvent.VK_HOME) {
        d = -divider;
      }
      else if ((keycode == KeyEvent.VK_LEFT) || (keycode == KeyEvent.VK_UP)) {
        d = Math.max(-10, -divider);
      }
      else if ((keycode == KeyEvent.VK_END) ||
               (keycode == KeyEvent.VK_RIGHT) || (keycode == KeyEvent.VK_DOWN)) {
        boolean horizontal = ("vertical" != get(component, "orientation"));
        Rectangle bounds = getRectangle(component, "bounds");
        int max = (horizontal ? bounds.width : bounds.height) - 5;
        d = max - divider;
        if (keycode != KeyEvent.VK_END) {
          d = Math.min(d, 10);
        }
      }
      if (d != 0) {
        setInteger(component, "divider", divider + d, -1);
        validate(component);
      }
    }
    else if ("list" == classname) {
      return processList(component, shiftdown, controldown, keychar, keycode, "item", null);
    }
    else if ("table" == classname) {
      return processList(component, shiftdown, controldown, keychar, keycode, "row", null);
    }
    else if ("tree" == classname) {
      //? clear childs' selection, select this is its     subnode was selected
      if (keycode == KeyEvent.VK_LEFT) {
        Object lead = get(component, "lead");
        if ((get(lead, "node") != null) && getBoolean(lead, "expanded", true)) {
          setBoolean(lead, "expanded", false, true);
          selectItem(component, lead, "node", "node");
          validate(component);
          invoke(component, "collapse"); //lead
          return true;
        }
        else {
          Object parent = getParent(lead);
          if (parent != component) {
            selectItem(component, parent, "node", "node");
            setLead(component, lead, parent);
            return true;
          }
        }
      }
      //? for interval mode select its all subnode or deselect all after
      else if (keycode == KeyEvent.VK_RIGHT) {
        Object lead = get(component, "lead");
        Object node = get(lead, "node");
        if (node != null) {
          if (getBoolean(lead, "expanded", true)) {
            selectItem(component, node, "node", "node");
            setLead(component, lead, node);
          }
          else {
            setBoolean(lead, "expanded", true, true);
            selectItem(component, lead, "node", "node");
            validate(component);
            invoke(component, "expand"); //lead
          }
          return true;
        }
      }
      return processList(component, shiftdown, controldown, keychar, keycode, "node", "node");
    }
    else if ("menubar" == classname) {
      Object previous = null;
      Object last = null;
      for (Object i = get(component, "popupmenu");
           i != null; i = get(i, "popupmenu")) {
        previous = last;
        last = i;
      }
      Object selected = get(last, "selected");
      Object hotpopup = ((selected != null) || (previous == null)) ?
                        last : previous;
      if ((selected == null) && (previous != null)) {
        selected = get(previous, "selected");
      }

      if ((keycode == KeyEvent.VK_UP) || (keycode == KeyEvent.VK_DOWN)) {
        set(hotpopup, "selected", null);
        popup(hotpopup, "popupmenu");
        selected = getMenu(hotpopup,
                           selected, keycode == KeyEvent.VK_DOWN, true);
        set(hotpopup, "selected", selected);
        repaint(hotpopup);
      }
      else if (keycode == KeyEvent.VK_LEFT) {
        if (previous != null) {
          selected = get(previous, "selected");
          set(previous, "selected", null);
          popup(previous, "popupmenu");
          set(previous, "selected", selected);
          repaint(previous); // , selected
        }
        else {
          selected = getMenu(component, get(component, "selected"), false, false);
          set(component, "selected", selected);
          Object popup = popup(component, "menubar");
          set(popup, "selected", getMenu(popup, null, true, true));
          repaint(component); // , selected
        }
      }
      else if (keycode == KeyEvent.VK_RIGHT) {
        if ((previous != null) && (selected == null)) {
          set(last, "selected", get(get(last, "menu"), "menu"));
          repaint(last); // , selected
        }
        else if ((selected != null) && (getClass(selected) == "menu")) {
          Object popup = popup(last, "popupmenu");
          set(popup, "selected", get(get(popup, "menu"), "menu"));
        }
        else {
          selected = getMenu(component, get(component, "selected"), true, false);
          set(component, "selected", selected);
          Object popup = popup(component, "menubar");
          set(popup, "selected", getMenu(popup, null, true, true));
          repaint(component); // , selected
        }
      }
      else if ((keycode == KeyEvent.VK_ENTER) ||
               (keychar == KeyEvent.VK_SPACE) || (keycode == KeyEvent.VK_ESCAPE)) {
        if ((keycode != KeyEvent.VK_ESCAPE) &&
            getBoolean(selected, "enabled", true)) {
          if ((selected != null) && (getClass(selected) == "checkboxmenuitem")) {
            changeCheck(selected, false);
          }
          else {
            invoke(selected, "action");
          }
        }
        closeup(component);
      }
      else {
        return false;
      }
      return true;
    }
    return false;
  }

  /**
   *
   */
  private boolean changeCheck(Object component, boolean box) {
    String group = getString(component, "group", null);
    if (group != null) {
      if (getBoolean(component, "selected", false)) {
        return false;
      }
      for (Object comp = get(getParent(component),
                             box ? "component" : "menu"); comp != null; comp = get(comp, ":next")) {
        if (comp == component) {
          setBoolean(component, "selected", true);
        }
        else if (group.equals(get(comp, "group")) &&
                 getBoolean(comp, "selected", false)) {
          setBoolean(comp, "selected", false);
          if (box) {
            repaint(comp);
          } //checkbox only
        }
      }
    }
    else {
      setBoolean(component, "selected",
                 !getBoolean(component, "selected", false), false);
    }
    invoke(component, "action");
    return true;
  }

  /**
   *
   */
  private Object getMenu(Object component, Object part,
                         boolean forward, boolean popup) {
    if (forward) {
      if (part != null) {
        part = get(part, ":next");
      }
      if (part == null) {
        part = get(popup ? get(component, "menu") : component, "menu");
      }
    }
    else {
      Object prev = get(popup ? get(component, "menu") : component, "menu");
      for (Object next = get(prev, ":next");
           (next != null) && (next != part); next = get(next, ":next")) {
        prev = next;
      }
      part = prev;
    }
    return (getClass(part) == "separator") ?
           getMenu(component, part, forward, popup) : part;
  }

  /**
   *
   */
    /*private boolean processKeyRelease(Object component, KeyEvent e, int keycode) {
        return true;
    }*/

  /**
   *
   */
  private boolean processField(Object component,
                               boolean shiftdown, boolean controldown, int modifiers,
                               int keychar, int keycode, boolean multiline, boolean hidden) {
    String text = getString(component, "text", "");
    int start = getInteger(component, "start", 0);
    int end = getInteger(component, "end", 0);
    boolean editable = getBoolean(component, "editable", true);

    int istart = start;
    int iend = end;
    String insert = null;
    //midp if (editable && (keychar != 0)) {
    if (editable && (keychar != 0) && //java
        ((modifiers == 0) || (modifiers == InputEvent.SHIFT_MASK))) { //java
      insert = String.valueOf((char)keychar);
    }
    else if (multiline && editable && (keycode == KeyEvent.VK_ENTER)) {
      insert = "\n";
    }
    else if (editable && (keycode == KeyEvent.VK_BACK_SPACE)) {
      insert = "";
      if (start == end) {
        istart -= 1;
      }
    }
    else if (keycode == KeyEvent.VK_END) {
      iend = text.length();
      if (!shiftdown) {
        istart = iend;
      }
    }
    else if (keycode == KeyEvent.VK_HOME) {
      iend = 0;
      if (!shiftdown) {
        istart = iend;
      }
    }
    else if (keycode == KeyEvent.VK_LEFT) {
      //java>
      if (controldown) {
        for (int i = 0; i < 2; i++) {
          while ((iend > 0) && ((i != 0) ==
                                Character.isLetterOrDigit(text.charAt(iend - 1)))) {
            iend--;
          }
        }
      }
      else {
        iend -= 1;
      }
      //<java
      //midp iend -= 1;
      if (!shiftdown) {
        istart = iend;
      }
    }
    else if (keycode == KeyEvent.VK_RIGHT) {
      //java>
      if (controldown) {
        for (int i = 0; i < 2; i++) {
          while ((iend < text.length()) && ((i == 0) ==
                                            Character.isLetterOrDigit(text.charAt(iend)))) {
            iend++;
          }
        }
      }
      else {
        iend += 1;
      }
      //<java
      //midp iend += 1;
      if (!shiftdown) {
        istart = iend;
      }
    }
    else if (editable && (keycode == KeyEvent.VK_DELETE)) {
      insert = "";
      if (start == end) {
        iend += 1;
      }
    }
    else if (controldown &&
             ((keycode == KeyEvent.VK_A) || (keycode == 0xBF))) {
      istart = 0; // KeyEvent.VK_SLASH
      iend = text.length();
    }
    else if (controldown && (keycode == 0xDC)) {
      istart = iend = text.length(); // KeyEvent.VK_BACK_SLASH
    }
    else if ((editable && !hidden && controldown && (keycode == KeyEvent.VK_X)) ||
             (!hidden && controldown && (keycode == KeyEvent.VK_C))) {
      if (start != end) {
        clipboard = text.substring(
          Math.min(start, end), Math.max(start, end));
        //java>
        try {
                    /* Personal Basis Profile doesn't contain datatransfer package
                    Toolkit toolkit = getToolkit();
                    Object systemclipboard = toolkit.getClass().getMethod("getSystemClipboard", null).invoke(toolkit, null);
                    Class selectionclass = Class.forName("java.awt.datatransfer." + "StringSelection");
                    Object selection = selectionclass.getConstructor(new Class[] { String.class }).
                        newInstance(new Object[] { clipboard });
                    systemclipboard.getClass().getMethod("setContents", new Class[] {
                            Class.forName("java.awt.datatransfer." + "Transferable"),
                            Class.forName("java.awt.datatransfer." + "ClipboardOwner") }).
                        invoke(systemclipboard, new Object[] { selection, null });*/
          getToolkit().getSystemClipboard().setContents(
            new StringSelection(clipboard), null);
        }
        catch (Exception exc) {
        }
        //<java
        if (keycode == KeyEvent.VK_X) {
          insert = "";
        }
      }
    }
    else if (editable && controldown && (keycode == KeyEvent.VK_V)) {
      //java>
      try {
                /* no datatransfer package in PBP
                Toolkit toolkit = getToolkit();
                Object systemclipboard = toolkit.getClass().getMethod("getSystemClipboard", null).invoke(toolkit, null);
                Object contents = systemclipboard.getClass().getMethod("getContents", new Class[] { Object.class }).
                    invoke(systemclipboard, new Object[] { this });
                Class dataflavor = Class.forName("java.awt.datatransfer." + "DataFlavor");
                insert = (String) (contents.getClass().getMethod("getTransferData", new Class[] { dataflavor }).
                    invoke(contents, new Object[] { dataflavor.getField("stringFlavor").get(null) }));*/
        insert = (String)getToolkit().getSystemClipboard().
          getContents(this).getTransferData(DataFlavor.stringFlavor);
      }
      catch (Exception exc) {
        insert = clipboard;
      }
      //<java
      //midp insert = clipboard;
      StringBuffer filtered = new StringBuffer(insert.length());
      for (int i = 0; i < insert.length(); i++) {
        char ckey = insert.charAt(i);
        if (((ckey > 0x1f) && (ckey < 0x7f)) ||
            ((ckey > 0x9f) && (ckey < 0xffff)) ||
            (multiline && (ckey == '\n'))) {
          filtered.append(ckey);
        }
      }
      if (filtered.length() != insert.length()) {
        insert = filtered.toString();
      }
    }
    return changeField(component, text, insert, istart, iend, start, end);
  }

  /**
   *
   */
  private boolean changeField(Object component, String text, String insert,
                              int movestart, int moveend, int start, int end) {
    if ((insert == null) && (start == movestart) && (end == moveend)) {
      return false;
    }
    movestart = Math.max(0, Math.min(movestart, text.length()));
    moveend = Math.max(0, Math.min(moveend, text.length()));
    if (insert != null) {
      int min = Math.min(movestart, moveend);
      set(component, "text", text.substring(0, min) + insert +
                             text.substring(Math.max(movestart, moveend)));
      movestart = moveend = min + insert.length();
      invoke(component, "action");
    }
    if (start != movestart) {
      setInteger(component, "start", movestart, 0);
    }
    if (end != moveend) {
      setInteger(component, "end", moveend, 0);
    }
    if ((insert != null) || (start != movestart) || (end != moveend)) {
      validate(component);
    }
    return true;
  }

  /**
   *
   */
  private boolean processList(Object component, boolean shiftdown, boolean controldown,
                              int keychar, int keycode, String itemname, String leafname) {
    if ((keycode == KeyEvent.VK_UP) ||
        (keycode == KeyEvent.VK_DOWN) || (keycode == KeyEvent.VK_PAGE_UP) ||
        (keycode == KeyEvent.VK_PAGE_DOWN) ||
        (keycode == KeyEvent.VK_HOME) || (keycode == KeyEvent.VK_END)) {
      Object lead = get(component, "lead");
      Object row = getListItem(component, component,
                               keycode, lead, itemname, leafname);
      if (row != null) {
        String selection = getString(component, "selection", "single");
        if (shiftdown && (selection != "single") && (lead != null)) {
          extend(component, lead, row, itemname, leafname);
        }
        else if (!controldown) {
          selectItem(component, row, itemname, leafname);
        }
        setLead(component, lead, row);
        Rectangle r = getRectangle(row, "bounds");
        scrollToVisible(component, r.x, r.y, 0, r.height - 1);
        return true;
      }
    }
    else if (keychar == KeyEvent.VK_SPACE) {
      select(component, get(component, "lead"),
             itemname, leafname, shiftdown, controldown); //...
      return true;
    }
    else if (controldown) {
      if (((keycode == KeyEvent.VK_A) || (keycode == 0xBF)) && //KeyEvent.VK_SLASH
          (getString(component, "selection", "single") != "single")) {
        selectAll(component, true, itemname, leafname);
        return true;
      }
      else if (keycode == 0xDC) { //KeyEvent.VK_BACK_SLASH
        selectAll(component, false, itemname, leafname);
        return true;
      }
    }
    return false;
  }

  /**
   *
   */
  private Object getListItem(Object component, Object scrollpane,
                             int keycode, Object lead, String itemname, String leafname) {
    Object row = null;
    if (keycode == KeyEvent.VK_UP) {
      for (Object prev = get(component, itemname); prev != lead;
           prev = getNextItem(component, prev, leafname)) {
        row = prev; // component -> getParent(lead)
      }
    }
    else if (keycode == KeyEvent.VK_DOWN) {
      row = (lead == null) ? get(component, itemname) :
            getNextItem(component, lead, leafname);
    }
    else if ((keycode == KeyEvent.VK_PAGE_UP) ||
             (keycode == KeyEvent.VK_PAGE_DOWN)) {
      Rectangle view = getRectangle(scrollpane, ":view");
      Rectangle port = getRectangle(scrollpane, ":port");
      Rectangle rl = (lead != null) ? getRectangle(lead, "bounds") : null;
      int vy = (keycode == KeyEvent.VK_PAGE_UP) ?
               view.y : (view.y + port.height - 2);
      if ((keycode == KeyEvent.VK_PAGE_UP) &&
          (rl != null) && (rl.y <= view.y)) {
        vy -= port.height - 2;
      }
      if ((keycode == KeyEvent.VK_PAGE_DOWN) &&
          (rl != null) && (rl.y + rl.height >= view.y + port.height - 2)) {
        vy += port.height - 2;
      }
      for (Object item = get(component, itemname); item != null;
           item = getNextItem(component, item, leafname)) {
        Rectangle r = getRectangle(item, "bounds");
        if (keycode == KeyEvent.VK_PAGE_UP) {
          row = item;
          if (r.y + r.height > vy) {
            break;
          }
        }
        else {
          if (r.y > vy) {
            break;
          }
          row = item;
        }
      }
    }
    else if (keycode == KeyEvent.VK_HOME) {
      row = get(component, itemname);
    }
    else if (keycode == KeyEvent.VK_END) {
      for (Object last = lead; last != null;
           last = getNextItem(component, last, leafname)) {
        row = last;
      }
    }
    return row;
  }

  /**
   *
   */
  private void selectAll(Object component,
                         boolean selected, String itemname, String leafname) {
    boolean changed = false;
    for (Object item = get(component, itemname);
         item != null; item = getNextItem(component, item, leafname)) {
      if (setBoolean(item, "selected", selected, false)) {
        repaint(component, null, item);
        changed = true;
      }
    }
    set(component, "anchor", null);
    if (changed) {
      invoke(component, "action");
    }
  }

  /**
   *
   */
  private void selectItem(Object component,
                          Object row, String itemname, String leafname) {
    boolean changed = false;
    for (Object item = get(component, itemname);
         item != null; item = getNextItem(component, item, leafname)) {
      if (setBoolean(item, "selected", (item == row), false)) {
        repaint(component, null, item);
        changed = true;
      }
    }
    set(component, "anchor", null);
    if (changed) {
      invoke(component, "action");
    }
  }

  /**
   *
   */
  private void extend(Object component, Object lead,
                      Object row, String itemname, String leafname) {
    Object anchor = get(component, "anchor");
    if (anchor == null) {
      set(component, "anchor", anchor = lead);
    }
    char select = 'n';
    boolean changed = false;
    for (Object item = get(component, itemname); // anchor - row
         item != null; item = getNextItem(component, item, leafname)) {
      if (item == anchor) select = (select == 'n') ? 'y' : 'r';
      if (item == row) select = (select == 'n') ? 'y' : 'r';
      if (setBoolean(item, "selected", (select != 'n'), false)) {
        repaint(component, null, item);
        changed = true;
      }
      if (select == 'r') select = 'n';
    }
    if (changed) {
      invoke(component, "action");
    }
  }

  /**
   *
   */
  private void setLead(Object component, Object oldlead, Object lead) {
    if (oldlead != lead) { //?
      if (oldlead != null) {
        repaint(component, null, oldlead);
      }
      set(component, "lead", lead);
      repaint(component, null, lead);
    }
  }

    /*public void repaint(int x, int y, int width, int height) {
        System.out.println("repaint(" + x + ", " + y + ", " + width + ", " + height + ")");
        super.repaint(x, y, width, height);
    }*/

  /**
   *
   */
  private void handleMouseEvent(int x, int y, int clickcount,
                                boolean shiftdown, boolean controldown, boolean popuptrigger,
                                int id, Object component, Object part) {
    if (id == MouseEvent.MOUSE_ENTERED) {
      setTimer(750L);
    }
    else if (id == MouseEvent.MOUSE_EXITED) {
      hideTip();
    }
    if (!getBoolean(component, "enabled", true)) {
      return;
    }
    String classname = getClass(component);
    if (("button" == classname) || ("checkbox" == classname)) {
      if ((id == MouseEvent.MOUSE_ENTERED) ||
          (id == MouseEvent.MOUSE_EXITED) ||
          (id == MouseEvent.MOUSE_PRESSED) ||
          (id == MouseEvent.MOUSE_RELEASED)) {
        if (id == MouseEvent.MOUSE_PRESSED) {
          setFocus(component);
        }
        else if ((id == MouseEvent.MOUSE_RELEASED) &&
                 (mouseinside == component)) {
          if ("checkbox" == classname) {
            changeCheck(component, true);
          }
          else {
            invoke(component, "action");
          }
        }
        repaint(component);
      }
    }
    else if ("combobox" == classname) {
      boolean editable = getBoolean(component, "editable", true);
      if (editable && (part == null)) {
        Image icon = null;
        int left = ((id == MouseEvent.MOUSE_PRESSED) &&
                    ((icon = getIcon(component, "icon", null)) != null)) ?
                   icon.getWidth(this) : 0;
        processField(x, y, clickcount, id, component, part, false, false, left);
      }
      else if (part != "icon") { // part = "down"
        if (((id == MouseEvent.MOUSE_ENTERED) ||
             (id == MouseEvent.MOUSE_EXITED)) && (mousepressed == null)) {
          if (editable) {
            repaint(component, "combobox", part);
          }
          else {
            repaint(component);
          }
        }
        else if (id == MouseEvent.MOUSE_PRESSED) {
          Object combolist = get(component, "combolist");
          if (combolist == null) {
            setFocus(component);
            repaint(component);
            popup(component, classname);
          }
          else {
            closeup(component, combolist, null);
          }
        }
        else if (id == MouseEvent.MOUSE_RELEASED) {
          if (mouseinside != component) {
            Object combolist = get(component, "combolist");
            closeup(component, combolist,
                    (mouseinside == combolist) ? insidepart : null);
          }
          else {
            repaint(component);
          }
        }
      }
    }
    else if ("combolist" == classname) {
      if (!processScroll(x, y, id, component, part)) {
        if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) {
          if (part != null) {
            // repaint previous inside
            set(component, "inside", part);
            repaint(component, classname, part);
          }
        }
        else if ((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) {
          if (part != null) {
            set(component, "inside", null);
            repaint(component, classname, part);
          }
        }
        else if (id == MouseEvent.MOUSE_RELEASED) {
          closeup(get(component, "combobox"), component, part);
        }
      }
    }
    else if (("textfield" == classname) || ("passwordfield" == classname)) {
      processField(x, y, clickcount, id, component, part,
                   false, ("passwordfield" == classname), 0);
    }
    else if ("textarea" == classname) {
      if (!processScroll(x, y, id, component, part)) {
        processField(x, y, clickcount, id, component, part, true, false, 0);
      }
    }
    //java>
    else if ("desktop" == classname) {
      if (id == MouseEvent.MOUSE_ENTERED) {
        setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
      }
      else if (id == MouseEvent.MOUSE_EXITED) {
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
    }
    //<java
    else if ("spinbox" == classname) {
      if (part == null) {
        processField(x, y, clickcount, id, component, part, false, false, 0);
      }
      else { // part = "up" || "down"
        if ((id == MouseEvent.MOUSE_ENTERED) ||
            (id == MouseEvent.MOUSE_EXITED) ||
            (id == MouseEvent.MOUSE_PRESSED) ||
            (id == MouseEvent.MOUSE_RELEASED)) {
          if (id == MouseEvent.MOUSE_PRESSED) {
            setFocus(component);
            if (processSpin(component, part)) {
              setTimer(375L);
            }
            //settext: start end selection, parse exception...
          }
          else {
            if (id == MouseEvent.MOUSE_RELEASED) {
              setTimer(0L);
            }
          }
          repaint(component, classname, part);
        }
      }
    }
    else if ("tabbedpane" == classname) {
      if ((id == MouseEvent.MOUSE_ENTERED) ||
          (id == MouseEvent.MOUSE_EXITED)) {
        if ((part != null) && getBoolean(part, "enabled", true) &&
            (getInteger(component, "selected", 0) != getIndex(component, "tab", part))) {
          repaint(component, "tabbedpane", part);
        }
      }
      else if ((part != null) && (id == MouseEvent.MOUSE_PRESSED) &&
               getBoolean(part, "enabled", true)) {
        int selected = getInteger(component, "selected", 0);
        int current = getIndex(component, "tab", part);
        if (selected == current) {
          setFocus(component);
          repaint(component, "tabbedpane", part);
        }
        else {
          setInteger(component, "selected", current, 0);
          //Object tabcontent = getItemImpl(component, "component", current);
          //setFocus((tabcontent != null) ? tabcontent : component);
          setNextFocusable(component, false);
          repaint(component);
          invoke(component, "action");
        }
      }
    }
    else if ("slider" == classname) {
      if ((id == MouseEvent.MOUSE_PRESSED) ||
          (id == MouseEvent.MOUSE_DRAGGED)) {
        if (id == MouseEvent.MOUSE_PRESSED) {
          setReference(component, block / 2, block / 2);
          setFocus(component);
        }
        int minimum = getInteger(component, "minimum", 0);
        int maximum = getInteger(component, "maximum", 100);
        int value = getInteger(component, "value", 50);
        Rectangle bounds = getRectangle(component, "bounds");
        boolean horizontal = ("vertical" != get(component, "orientation"));
        int newvalue = minimum +
                       (horizontal ? (mousex - referencex) : (mousey - referencey)) *
                       (maximum - minimum) /
                       ((horizontal ? bounds.width : bounds.height) - block); //... +0.5
        newvalue = Math.max(minimum, Math.min(newvalue, maximum));
        if (value != newvalue) {
          setInteger(component, "value", newvalue, 50);
          invoke(component, "action");
        }
        if ((value != newvalue) || (id == MouseEvent.MOUSE_PRESSED)) {
          repaint(component);
        }
      }
    }
    else if ("splitpane" == classname) {
      if (id == MouseEvent.MOUSE_PRESSED) {
        setReference(component, 2, 2);
      }
      else if (id == MouseEvent.MOUSE_DRAGGED) {
        int divider = getInteger(component, "divider", -1);
        boolean horizontal = ("vertical" != get(component, "orientation"));
        int moveto = horizontal ? (mousex - referencex) :
                     (mousey - referencey);
        Rectangle bounds = getRectangle(component, "bounds");
        moveto = Math.max(0, Math.min(moveto,
                                      Math.abs(horizontal ? bounds.width : bounds.height) - 5));
        if (divider != moveto) {
          setInteger(component, "divider", moveto, -1);
          validate(component);
        }
      }
      //java>
      else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
        boolean horizontal = ("vertical" != get(component, "orientation"));
        setCursor(Cursor.getPredefinedCursor(horizontal ?
                                             Cursor.E_RESIZE_CURSOR : Cursor.S_RESIZE_CURSOR));
      }
      else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
               ((id == MouseEvent.MOUSE_RELEASED) && (mouseinside != component))) {
        setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
      }
      //<java
    }
    else if (("list" == classname) ||
             ("table" == classname) || ("tree" == classname)) {
      if (!processScroll(x, y, id, component, part)) {
        if (((id == MouseEvent.MOUSE_PRESSED) ||
             ((id == MouseEvent.MOUSE_DRAGGED) &&
              !shiftdown && !controldown)) &&
            !popuptrigger) { // e.getClickCount()
          Rectangle view = getRectangle(component, ":view");
          Rectangle viewport = getRectangle(component, ":port");
          java.lang.ref.Reference.reachabilityFence(viewport);
          int my = mousey + view.y - referencey;
          String itemname = ("list" == classname) ? "item" :
                            (("table" == classname) ? "row" : "node");
          String subitem = ("tree" == classname) ? "node" : null;
          for (Object item = get(component, itemname); item != null; ) {
            Rectangle r = getRectangle(item, "bounds");
            if (my < r.y + r.height) {
              if (id == MouseEvent.MOUSE_DRAGGED) { //!!!
                scrollToVisible(component, r.x, r.y, 0, r.height - 1);
              }
              else if ("tree" == classname) {
                int mx = mousex + view.x - referencex;
                if (mx < r.x) {
                  if ((mx >= r.x - block) && (get(item, "node") != null)) {
                    boolean expanded = getBoolean(item, "expanded", true);
                    setBoolean(item, "expanded", !expanded, true);
                    selectItem(component, item, "node", "node");
                    setLead(component, get(component, "lead"), item);
                    setFocus(component);
                    validate(component);
                    invoke(component, expanded ? "collapse" : "expand"); //item
                  }
                  break;
                }
              }
              if ((id != MouseEvent.MOUSE_DRAGGED) ||
                  !getBoolean(item, "selected", false)) {
                select(component, item, itemname, subitem, shiftdown, controldown);
                if (id != MouseEvent.MOUSE_DRAGGED) {
                  if (setFocus(component)) {
                    repaint(component, classname, item);
                  } //?
                }
              }
              break;
            }
            item = getNextItem(component, item, subitem);
          }
        }
      }
    }
    else if ("menubar" == classname) {
      Object selected = get(component, "selected");
      if (((id == MouseEvent.MOUSE_ENTERED) || (id == MouseEvent.MOUSE_EXITED)) &&
          (part != null) && (selected == null)) {
        repaint(component, classname, part);
      }
      else if ((part != null) && ((selected == null) ?
                                  (id == MouseEvent.MOUSE_PRESSED) :
                                  ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)))) {
        set(component, "selected", part);
        popup(component, classname);
        repaint(component, classname, part);
      }
      else if ((id == MouseEvent.MOUSE_PRESSED) && (selected != null)) {
        closeup(component);
      }
      else if (id == MouseEvent.MOUSE_RELEASED) {
        if ((part != insidepart) &&
            ((insidepart == null) || (getClass(insidepart) != "menu"))) {
          if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
            if (getClass(insidepart) == "checkboxmenuitem") {
              changeCheck(insidepart, false);
            }
            else {
              invoke(insidepart, "action");
            }
          }
          closeup(component);
        }
      }
    }
    else if ("popupmenu" == classname) {
      if (part != null) {
        if ((id == MouseEvent.MOUSE_ENTERED) || (id == DRAG_ENTERED)) {
          set(component, "selected", part);
          popup(component, classname);
          repaint(component, classname, part);
        }
        else if (id == MouseEvent.MOUSE_RELEASED) {
          if ((insidepart == null) || (getClass(insidepart) != "menu")) {
            Object menubar = part;
            do {
              menubar = getParent(menubar);
            }
            while (getClass(menubar) != "menubar");
            if ((insidepart != null) && getBoolean(insidepart, "enabled", true)) {
              if (getClass(insidepart) == "checkboxmenuitem") {
                changeCheck(insidepart, false);
              }
              else {
                invoke(insidepart, "action");
              }
            }
            closeup(menubar);
          }
        }
        else if ((id == MouseEvent.MOUSE_EXITED) || (id == DRAG_EXITED)) {
          if (getClass(part) != "menu") {
            set(component, "selected", null);
          }
          repaint(component, classname, part);
        }
      }
    }
    else if ("dialog" == classname) {
      if (part == "header") {
        if (id == MouseEvent.MOUSE_PRESSED) {
          referencex = mousex;
          referencey = mousey;
          if (!getBoolean(component, "modal", false) &&
              (get(content, "component") != component)) {
            removeItemImpl(content, "component", component);
            insertItem(content, "component", component, 0);
            set(component, ":parent", content);
            repaint(component);
          }
        }
        else if (id == MouseEvent.MOUSE_DRAGGED) {
          Rectangle bounds = getRectangle(component, "bounds");
          int dx = mousex - referencex;
          int dy = mousey - referencey;
          repaint(component,
                  bounds.x + Math.min(0, dx), bounds.y + Math.min(0, dy),
                  bounds.width + Math.abs(dx), bounds.height + Math.abs(dy));
          bounds.x += dx;
          bounds.y += dy;
          referencex = mousex;
          referencey = mousey;
        }
      }
    }
  }

  /**
   *
   */
  private void setReference(Object component, int x, int y) {
    referencex = x;
    referencey = y;
    for (; component != null; component = getParent(component)) {
      Rectangle bounds = getRectangle(component, "bounds");
      referencex += bounds.x;
      referencey += bounds.y;
    }
  }

  /**
   *
   */
  private void select(Object component, Object row, String first,
                      String child, boolean shiftdown, boolean controldown) {
    String selection = getString(component, "selection", "single");
    Object lead = null;
    if (shiftdown && (selection != "single") &&
        ((lead = get(component, "lead")) != null)) {
      extend(component, lead, row, first, child);
    }
    else {
      if (controldown && (selection == "multiple")) {
        setBoolean(row, "selected",
                   !getBoolean(row, "selected", false), false);
        repaint(component, null, row);
        invoke(component, "action");
        set(component, "anchor", null);
      }
      else if (controldown && getBoolean(row, "selected", false)) {
        for (Object item = row;
             item != null; item = getNextItem(component, item, child)) {
          if (setBoolean(item, "selected", false, false)) {
            repaint(component, null, item);
          }
        }
        invoke(component, "action");
        set(component, "anchor", null);
      }
      else {
        selectItem(component, row, first, child);
      }
    }
    setLead(component, (lead != null) ? lead : get(component, "lead"), row);
  }

  /**
   *
   */
  private Object getNextItem(Object component,
                             Object item, String subitem) {
    if (subitem == null) {
      return get(item, ":next");
    }
    Object next = get(item, subitem);
    if ((next == null) || !getBoolean(item, "expanded", true)) {
      while ((item != component) && ((next = get(item, ":next")) == null)) {
        item = getParent(item);
      }
    }
    return next;
  }

  /**
   *
   */
  private void processField(int x, int y, int clickcount,
                            int id, Object component,
                            Object part, boolean multiline, boolean hidden, int left) {
    java.lang.ref.Reference.reachabilityFence(part);
    if (id == MouseEvent.MOUSE_PRESSED) {
      setReference(component, 2 + left, 2);
      int mx = x - referencex;
      int my = 0;
      if (!multiline) {
        mx += getInteger(component, "offset", 0);
      }
      else {
        Rectangle view = getRectangle(component, ":view");
        mx += view.x - 1;
        my = y - referencey + view.y - 1;
      }
      int caretstart = getCaretLocation(component, mx, my, hidden);
      int caretend = caretstart;
      //java>
      if (clickcount > 1) {
        String text = getString(component, "text", "");
        while ((caretstart > 0) && ((clickcount == 2) ?
                                    Character.isLetterOrDigit(text.charAt(caretstart - 1)) :
                                    (text.charAt(caretstart - 1) != '\n'))) {
          caretstart--;
        }
        while ((caretend < text.length()) && ((clickcount == 2) ?
                                              Character.isLetterOrDigit(text.charAt(caretend)) :
                                              (text.charAt(caretend) != '\n'))) {
          caretend++;
        }
      }
      //<java
      setInteger(component, "start", caretstart, 0);
      setInteger(component, "end", caretend, 0);
      setFocus(component);
      validate(component); // caret check only
    }
    else if (id == MouseEvent.MOUSE_DRAGGED) {
      int mx = x - referencex;
      int my = 0;
      if (!multiline) {
        mx += getInteger(component, "offset", 0);
      }
      else {
        Rectangle view = getRectangle(component, ":view");
        mx += view.x - 1;
        my = y - referencey + view.y - 1;
      }
      int dragcaret = getCaretLocation(component, mx, my, hidden);
      if (dragcaret != getInteger(component, "end", 0)) {
        setInteger(component, "end", dragcaret, 0);
        validate(component); // caret check only
      }
    }
    //java>
    else if ((id == MouseEvent.MOUSE_ENTERED) && (mousepressed == null)) {
      setCursor(Cursor.getPredefinedCursor(Cursor.TEXT_CURSOR));
    }
    else if (((id == MouseEvent.MOUSE_EXITED) && (mousepressed == null)) ||
             ((id == MouseEvent.MOUSE_RELEASED) &&
              ((mouseinside != component) || (insidepart != null)))) {
      setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
    }
    //<java
  }

  /**
   *
   */
  private int getCaretLocation(Object component, int x, int y, boolean hidden) {
    String text = getString(component, "text", "");
    FontMetrics fm = getFontMetrics(getFont());
    for (int i = 0, j = 0; true; i = j + 1) {
      j = text.indexOf('\n', i);
      if ((j == -1) || y < fm.getHeight()) {
        if (j == -1) {
          j = text.length();
        }
        for (int k = i; k < j; k++) {
          int charwidth = fm.charWidth(hidden ? '*' : text.charAt(k));
          if (x <= (charwidth / 2)) {
            return k;
          }
          x -= charwidth;
        }
        return j;
      }
      y -= fm.getHeight();
    }
  }

  /**
   *
   */
  private boolean processScroll(int x, int y,
                                int id, Object component, Object part) {
    if ((part == "up") || (part == "down") ||
        (part == "left") || (part == "right")) {
      if ((id == MouseEvent.MOUSE_ENTERED) ||
          (id == MouseEvent.MOUSE_EXITED) ||
          (id == MouseEvent.MOUSE_PRESSED) ||
          (id == MouseEvent.MOUSE_RELEASED)) {
        if (id == MouseEvent.MOUSE_PRESSED) {
          if (processScroll(component, part)) {
            setTimer(300L);
            return true;
          }
        }
        else {
          if (id == MouseEvent.MOUSE_RELEASED) {
            setTimer(0L);
          }
          repaint(component, null, part);
        }
      }
    }
    else if ((part == "uptrack") || (part == "downtrack") ||
             (part == "lefttrack") || (part == "righttrack")) {
      if (id == MouseEvent.MOUSE_PRESSED) {
        if (processScroll(component, part)) {
          setTimer(300L);
        }
      }
      else if (id == MouseEvent.MOUSE_RELEASED) {
        setTimer(0L);
      }
    }
    else if ((part == "vknob") || (part == "hknob")) {
      if (id == MouseEvent.MOUSE_PRESSED) {
        Rectangle port = getRectangle(component, ":port");
        Rectangle view = getRectangle(component, ":view");
        if (part == "hknob") {
          referencex = x -
                       view.x * (port.width - 2 * block) / view.width;
        }
        else {
          referencey = y -
                       view.y * (port.height - 2 * block) / view.height;
        }
      }
      else if (id == MouseEvent.MOUSE_DRAGGED) {
        Rectangle port = getRectangle(component, ":port");
        Rectangle view = getRectangle(component, ":view");
        if (part == "hknob") {
          int viewx = (x - referencex) *
                      view.width / (port.width - 2 * block);
          viewx = Math.max(0, Math.min(viewx, view.width - port.width + 2));
          if (view.x != viewx) {
            view.x = viewx;
            repaint(component, null, "horizontal");
          }
        }
        else {
          int viewy = (y - referencey) *
                      view.height / (port.height - 2 * block);
          viewy = Math.max(0, Math.min(viewy, view.height - port.height + 2));
          if (view.y != viewy) {
            view.y = viewy;
            repaint(component, null, "vertical");
          }
        }
      }
    }
    else if (part == "corner") {
      part = "corner"; // compiler bug
    }
    else {
      if (id == MouseEvent.MOUSE_PRESSED) {
        Rectangle port = getRectangle(component, ":port");
        setReference(component, port.x + 1, port.y + 1);
      }
      return false;
    }
    return true;
  }

  /**
   *
   */
  private boolean processScroll(Object component, Object part) {
    Rectangle view = getRectangle(component, ":view");
    Rectangle port = ((part == "left") || (part == "up")) ? null :
                     getRectangle(component, ":port");
    int dx = 0;
    int dy = 0;
    if (part == "left") {
      dx = -10;
    }
    else if (part == "lefttrack") {
      dx = -port.width;
    }
    else if (part == "right") {
      dx = 10;
    }
    else if (part == "righttrack") {
      dx = port.width;
    }
    else if (part == "up") {
      dy = -10;
    }
    else if (part == "uptrack") {
      dy = -port.height;
    }
    else if (part == "down") {
      dy = 10;
    }
    else if (part == "downtrack") {
      dy = port.height;
    }
    if (dx != 0) {
      dx = (dx < 0) ? Math.max(-view.x, dx) :
           Math.min(dx, view.width - port.width + 2 - view.x);
    }
    else if (dy != 0) {
      dy = (dy < 0) ? Math.max(-view.y, dy) :
           Math.min(dy, view.height - port.height + 2 - view.y);
    }
    else {
      return false;
    }
    view.x += dx;
    view.y += dy;
    repaint(component, null, (dx != 0) ? "horizontal" : "vertical");
    return (((part == "left") || (part == "lefttrack")) && (view.x > 0)) ||
           (((part == "right") || (part == "righttrack")) &&
            (view.x < view.width - port.width + 2)) ||
           (((part == "up") || (part == "uptrack")) && (view.y > 0)) ||
           (((part == "down") || (part == "downtrack")) &&
            (view.y < view.height - port.height + 2));
  }

  /**
   *
   */
  private boolean processSpin(Object component, Object part) {
    String text = getString(component, "text", null);
    if (text != null) {
      try {
        String value = String.valueOf(
          Integer.parseInt(text) + ((part == "up") ? 1 : -1));
        setString(component, "text", value, null);
        setInteger(component, "start", value.length(), 0);
        setInteger(component, "end", 0, 0);
        repaint(component, "spinbox", null);
        invoke(component, "action");
        return true;
      }
      catch (NumberFormatException nfe) {
      }
    }
    return false;
  }

  //java>
    /*public void setEventHandler(Object component, Object eventhandler) {
        set(component, ":handler", eventhandler);
    }*/

  /**
   *
   */
  private void invoke(Object component, String event) {
    Method method = (Method)get(component, event);
    if (method != null) {
      try {
        method.invoke(this, null);
      }
      catch (InvocationTargetException ite) {
        ite.getTargetException().printStackTrace();
      }
      catch (Exception exc) {
        exc.printStackTrace();
      }
    }
  }
  //<java
    /*midp
    private void invoke(Object component, String event) {
            String action = (String) get(component, event);
            if (action != null) { handle(component, action); }
    }
    protected void handle(Object source, String action) {
    }
    midp*/

  /**
   *
   */
  private boolean findComponent(Object component, int x, int y) {
    if (component == content) {
      mouseinside = insidepart = null;
      mousex = x;
      mousey = y;
    }
    if (!getBoolean(component, "visible", true)) {
      return false;
    }
    Rectangle bounds = getRectangle(component, "bounds");
    if ((bounds == null) || !(bounds.contains(x, y))) {
      return false;
    } //java
    //midp if ((bounds == null) || (x < bounds.x) || (x - bounds.x >= bounds.width) ||
    //midp     (y < bounds.y) || (y - bounds.y >= bounds.height)) { return false; }
    mouseinside = component;
    x -= bounds.x;
    y -= bounds.y;
    String classname = getClass(component);

    if ("combobox" == classname) {
      if (getBoolean(component, "editable", true) && (x <= bounds.width - block)) {
        Image icon = getIcon(component, "icon", null);
        insidepart = ((icon != null) && (x <= 2 + icon.getWidth(this))) ?
                     "icon" : null;
      }
      else {
        insidepart = "down";
      }
    }
    else if ("combolist" == classname) {
      if (!findScrollPane(component, x, y, bounds)) {
        y += getRectangle(component, ":view").y;
        for (Object choice = get(get(component, "combobox"), "choice");
             choice != null; choice = get(choice, ":next")) {
          Rectangle r = getRectangle(choice, "bounds");
          if ((y >= r.y) && (y < r.y + r.height)) {
            insidepart = choice;
            break;
          }
        }
      }
    }
    else if ("textarea" == classname) {
      findScrollPane(component, x, y, bounds);
    }
    else if ("tabbedpane" == classname) {
      Object tabcontent = getItemImpl(component,
                                      "component", getInteger(component, "selected", 0));
      if ((tabcontent == null) || !findComponent(tabcontent, x, y)) {
        for (Object comp = get(component, "tab");
             comp != null; comp = get(comp, ":next")) {
          Rectangle r = getRectangle(comp, "bounds");
          if (r.contains(x, y)) { //java
            //midp if ((x >= r.x) && (x - r.x < r.width) && (y >= r.y) && (y - r.y < r.height)) {
            insidepart = comp;
            break;
          }
        }
      }
    }
    else if (("panel" == classname) || ("desktop" == classname) ||
             ("dialog" == classname)) {
      if (("dialog" == classname) &&
          (y < 4 + getInteger(component, "titleheight", 0))) {
        insidepart = "header";
      }
      else {
        for (Object comp = get(component, "component");
             comp != null; comp = get(comp, ":next")) {
          if (findComponent(comp, x, y)) {
            break;
          }
          if (("desktop" == classname) &&
              getBoolean(comp, "modal", false)) {
            break;
          } // && dialog
        }
      }
    }
    else if ("spinbox" == classname) {
      insidepart = (x <= bounds.width - block) ? null :
                   ((y <= bounds.height / 2) ? "up" : "down");
    }
    else if ("splitpane" == classname) {
      Object comp1 = get(component, "component");
      if (comp1 != null) {
        if (!findComponent(comp1, x, y)) {
          Object comp2 = get(comp1, ":next");
          if (comp2 != null) {
            findComponent(comp2, x, y);
          }
        }
      }
    }
    else if ("list" == classname) {
      findScrollPane(component, x, y, bounds);
    }
    else if ("table" == classname) {
      findScrollPane(component, x, y, bounds);
    }
    else if ("tree" == classname) {
      findScrollPane(component, x, y, bounds);
    }
    else if ("menubar" == classname) {
      for (Object menu = get(component, "menu");
           menu != null; menu = get(menu, ":next")) {
        Rectangle r = getRectangle(menu, "bounds");
        if ((x >= r.x) && (x < r.x + r.width)) {
          insidepart = menu;
          break;
        }
      }
    }
    else if ("popupmenu" == classname) {
      for (Object menu = get(get(component, "menu"), "menu");
           menu != null; menu = get(menu, ":next")) {
        Rectangle r = getRectangle(menu, "bounds");
        if ((y >= r.y) && (y < r.y + r.height)) {
          insidepart = menu;
          break;
        }
      }
    }
    return true;
  }

  /**
   *
   */
  private boolean findScrollPane(Object component,
                                 int x, int y, Rectangle bounds) {
    java.lang.ref.Reference.reachabilityFence(bounds);
    Rectangle port = getRectangle(component, ":port");
    if ((x < port.x) || (y < port.y) ||
        ((x >= port.x + port.width) && (y >= port.y + port.height))) {
      insidepart = "corner";
    }
    else if ((x >= port.x + port.width) || (y >= port.y + port.height)) {
      boolean horizontal = (y >= port.y + port.height);
      int p = horizontal ? (x - port.x) : (y - port.y);
      int portsize = horizontal ? port.width : port.height;
      int button = Math.min(block, portsize / 2);
      if (p < button) {
        insidepart = horizontal ? "left" : "up";
      }
      else if (p >= portsize - button) {
        insidepart = horizontal ? "right" : "down";
      }
      else {
        Rectangle view = getRectangle(component, ":view");
        int viewp = horizontal ? view.x : view.y;
        int viewsize = horizontal ? view.width : view.height;
        int track = portsize - (2 * button);
        int knob = Math.min(track,
                            Math.max(track * (portsize - 2) / viewsize, 6));
        int decrease = viewp * (track - knob) / (viewsize - portsize + 2);
        if (p < button + decrease) {
          insidepart = horizontal ? "lefttrack" : "uptrack";
        }
        else if (p < button + decrease + knob) {
          insidepart = horizontal ? "hknob" : "vknob";
        }
        else {
          insidepart = horizontal ? "righttrack" : "downtrack";
        }
      }
    }
    else {
      return false;
    }
    return true;
  }

  /**
   *
   */
  private void repaint(Object component, Object classname, Object part) {
    Rectangle b = getRectangle(component, "bounds");
    if ((classname == "combobox") || (classname == "spinbox")) {
      boolean down = (part == "up") || (part == "down"); // else text
      repaint(component, down ? (b.x + b.width - block) : b.x, b.y,
              down ? block : (b.width - block), b.height);
    }
    //else if (classname == "dialog") {}
    //int titleheight = getInteger(component, "titleheight", 0);
    //else if (classname == "splitpane") {}
    else if ((classname == "tabbedpane") ||
             (classname == "menubar") || (classname == "popupmenu")) {
      Rectangle r = getRectangle(part, "bounds");
      repaint(component, b.x + r.x, b.y + r.y,
              (classname == "popupmenu") ? b.width : r.width, r.height);
    }
    else //if ((classname == "combolist") || (classname == "textarea") ||
    {//(classname == "list") || (classname == "table") || (classname == "tree")) {
      Rectangle port = getRectangle(component, ":port");
      if (part == "left") {
        repaint(component, b.x + port.x, b.y + b.height - block, block, block);
      }
      else if (part == "right") {
        repaint(component, b.x + port.x + port.width - block, b.y + b.height - block, block, block);
      }
      else if (part == "up") {
        repaint(component, b.x + b.width - block, b.y + port.y, block, block);
      }
      else if (part == "down") {
        repaint(component, b.x + b.width - block, b.y + port.y + port.height - block, block, block);
      }
      else if (part == "horizontal") { // horizontaly center part
        repaint(component, b.x + port.x, b.y, port.width, b.height);
      }
      else if (part == "vertical") {
        repaint(component, b.x, b.y + port.y, b.width, port.height);
      }
      else if (part == "text") { //textarea
        repaint(component, b.x + port.x, b.y + port.y, port.width, port.height);
      }
      else {
        Rectangle view = getRectangle(component, ":view");
        Rectangle r = getRectangle(part, "bounds");
        if ((r.y + r.height >= view.y) && (r.y <= view.y + port.height)) {
          repaint(component, b.x + port.x, b.y + port.y - view.y + 1 + r.y,
                  port.width, r.height);
          //? need cut item rectangle above/bellow viewport
        }
      }
    }
  }

  /**
   *
   */
  private void validate(Object component) {
    repaint(component);
    Rectangle bounds = getRectangle(component, "bounds");
    bounds.width = -1 * Math.abs(bounds.width);
  }

  /**
   *
   */
  private void repaint(Object component) {
    Rectangle bounds = getRectangle(component, "bounds");
    if (bounds != null) {
      repaint(component, bounds.x, bounds.y, bounds.width, bounds.height);
    }
  }

  /**
   *
   */
  private void repaint(Object component, int x, int y, int width, int height) {
    while ((component = getParent(component)) != null) {
      Rectangle bounds = getRectangle(component, "bounds");
      x += bounds.x;
      y += bounds.y;
    }
    repaint(x, y, width, height);
  }

    /*private void clip(Graphics g,
            Rectangle clip, int x, int y, int width, int height) {
        int x1 = Math.max(clip.x, x);
        int y1 = Math.max(clip.y, y);
        int x2 = Math.min(clip.x + clip.width, x + width);
        int y2 = Math.min(clip.y + clip.height, y + height);
        g.setClip(x1, y1, x2 - x1, y2 - y1);
    }*/

  /**
   *
   */
  public boolean requestFocus(Object component) {
    if (checkFocusable(component, true)) {
      setFocus(component);
      return true;
    }
    return false;
  }

  /**
   *
   */
  private boolean setFocus(Object component) {
    if (!focusinside) { //java
      requestFocus(); //java
    } //java
    if (focusowner != component) {
      Object focused = focusowner;
      focusowner = component;
      if (focused != null) {
        //mouseEvent(null, FocusEvent.FOCUS_LOST, focused, null, null);
        repaint(focused);
        //focusGained(component);
      }
      return true;
    }
    return false;
  }

  /**
   * @return next focusable component is found (not the first of the desktop/dialog)
   */
  private boolean setNextFocusable(Object current, boolean outgo) {
    boolean consumed = true;
    for (Object next = null, component = current; true; component = next) {
      next = get(component, "component"); // check first subcomponent
      if (next == null) {
        next = get(component, ":next");
      } // check next component
      while (next == null) { // find the next of the parents, or the topmost
        component = getParent(component); // current is not on the desktop
        if (component == null) {
          return false;
        }
        if ((component == content) || ((getClass(component) == "dialog") &&
                                       (!outgo || getBoolean(component, "modal", false)))) {
          consumed = false; // find next focusable but does not consume event
          next = component; // the topmost (desktop or modal dialog)
        }
        else {
          next = get(component, ":next");
        }
      }
      if (next == current) {
        return false;
      } // one fucusable, no loop
      if (checkFocusable(next, false)) {
        setFocus(next);
        return consumed;
      }
    }
  }

  //java>

  /**
   * @return previous focusable component is found (not the last of the desktop/dialog)
   */
  private boolean setPreviousFocusable(Object component, boolean outgo) {
    for (int i = 0; i < 2; i++) { // 0 is backward direction
      Object previous = getPreviousFocusable(component, null, true, false, (i == 0), outgo);
      if (previous != null) {
        setFocus(previous);
        return (i == 0);
      }
    }
    return false;
  }

  /**
   * For the starting component search its parent direction for a focusable component, and then
   * its next component (if not search backward from the component).<br>
   * For its parent components check its first component, the current one, and its parent direction
   * (backward search), or its parent, then next component (forward direction).<br>
   * For the rest components check the next, then the first subcomponent direction, and finally
   * check whether the component is focusable.
   */
  private Object getPreviousFocusable(Object component,
                                      Object block, boolean start, boolean upward, boolean backward, boolean outgo) {
    Object previous = null;
    if ((component != null) && (component != block)) {
      boolean go = ((getClass(component) != "dialog") ||
                    (outgo && !getBoolean(component, "modal", false)));
      if (!start && !upward && go) {
        previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
      }
      if ((previous == null) && ((upward && backward) || (!start && !upward))) {
        previous = getPreviousFocusable(get(component, "component"), block, false, false, backward, outgo);
        if ((previous == null) && checkFocusable(component, false)) {
          previous = component;
        }
      }
      if ((previous == null) && (start || upward) && go) {
        previous = getPreviousFocusable(getParent(component), component, false, true, backward, outgo);
      }
      if ((previous == null) && (start || upward) && !backward && go) {
        previous = getPreviousFocusable(get(component, ":next"), block, false, false, backward, outgo);
      }
    }
    return previous;
  }
  //<java

  /**
   *
   */
  private boolean checkFocusable(Object component, boolean forced) {
    String classname = getClass(component);
    //midp forced=true;
    if ((classname == "button") || (classname == "checkbox") ||
        (classname == "combobox") || (classname == "textfield") ||
        (classname == "passwordfield") || (classname == "textarea") ||
        (classname == "spinbox") || (classname == "slider") ||
        (classname == "list") || (classname == "table") || (classname == "tree") ||
        (classname == "tabbedpane") || (forced && (classname == "splitpane"))) {
      for (Object comp = component; comp != null; ) {
        if (!getBoolean(comp, "enabled", true) || !getBoolean(comp, "visible", true)) {
          return false;
        }
        Object parent = getParent(comp);
        if ((getClass(parent) == "tabbedpane") && (getItemImpl(parent,
                                                               "component", getInteger(parent, "selected", 0)) != comp)) {
          return false;
        }
        comp = parent;
      }
      return true;
    }
    return false;
  }

    /*if (cliparea == null) { add(cliparea = new TextArea()); }
    cliparea.setText(content);
    cliparea.selectAll();
    cliparea.dispatchEvent(new KeyEvent(this, KeyEvent.KEY_TYPED, 0L, KeyEvent.CTRL_MASK, 0, (char) 3));
    cliparea.selectAll();

    if (cliparea == null) { add(cliparea = new TextArea()); }
    cliparea.dispatchEvent(new KeyEvent(this, KeyEvent.KEY_TYPED, 0L, KeyEvent.CTRL_MASK, 0, (char) 22));
    cliparea.getText(); return cliparea.getText();*/

  // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----

  /**
   *
   */
  public Object create(String classname) {
    for (int i = 0; i < dtd.length; i += 3) {
      if (dtd[i].equals(classname)) {
        return createImpl((String)dtd[i]);
      }
    }
    throw new IllegalArgumentException("unknown " + classname);
  }

  /**
   * @return classname
   */
  public String getClass(Object component) {
    return (String)get(component, ":class");
  }

  /**
   *
   */
  public Object getDesktop() {
    return content;
  }

  /**
   *
   */
  private Object createImpl(String classname) {
    return new Object[]{":class", classname, null};
  }

  /**
   *
   */
  private boolean set(Object component, Object key, Object value) {
    Object[] previous = (Object[])component;
    for (Object[] entry = previous; entry != null;
         entry = (Object[])entry[2]) {
      if (entry[0] == key) {
        if (value != null) { // set the row's value
          Object oldvalue = entry[1];
          entry[1] = value;
          return !value.equals(oldvalue);
        }
        else { // remove the row
          previous[2] = entry[2];
          entry[2] = null;
          return true;
        }
      }
      previous = entry;
    }
    if (value != null) { // append a new row
      previous[2] = new Object[]{key, value, null};
      return true;
    }
    return false;
  }

  /**
   *
   */
  private Object get(Object component, Object key) {
    for (Object[] entry = (Object[])component; entry != null;
         entry = (Object[])entry[2]) {
      if (entry[0] == key) {
        return entry[1];
      }
    }
    return null;
  }

  /**
   *
   */
  public int getCount(Object component) {
    return getCount(component, null);
  }

  /**
   *
   */
  public int getCount(Object component, String key) {
    return getItemCountImpl(component, getComponentName(component, key));
  }

  /**
   *
   */
  public Object getParent(Object component) {
    return get(component, ":parent");
  }

  /**
   * @return the first selected index or -1
   */
  public int getSelectedIndex(Object component) {
    int i = 0;
    for (Object item = get(component, getComponentName(component, null));
         item != null; item = get(item, ":next")) {
      if (getBoolean(item, "selected", false)) {
        return i;
      }
      i++;
    }
    return -1;
  }

  /**
   *
   */
  public void removeAll(Object component) {
    removeAll(component, null);
  }

  /**
   *
   */
  public void removeAll(Object component, String key) {
    String list = getComponentName(component, key);
    if (get(component, list) != null) {
      set(component, list, null);
      update(component, "validate");
    }
  }

  /**
   *
   */
  private int getItemCountImpl(Object component, Object key) {
    int i = 0;
    for (component = get(component, key); component != null;
         component = get(component, ":next")) {
      i++;
    }
    return i;
  }

  /**
   *
   */
  public Object getItem(Object component, int index) {
    return getItem(component, null, index);
  }

  /**
   *
   */
  public Object[] getItems(Object component) {
    return getItems(component, null);
  }

  /**
   *
   */
  public Object getItem(Object component, String key, int index) {
    return getItemImpl(component, getComponentName(component, key), index);
  }

  /**
   *
   */
  public Object[] getItems(Object component, String key) {
    key = getComponentName(component, key);
    Object[] items = new Object[getItemCountImpl(component, key)];
    component = get(component, key);
    for (int i = 0; i < items.length; i++) {
      items[i] = component;
      component = get(component, ":next");
    }
    return items;
  }

  /**
   *
   */
  private Object getItemImpl(Object component, Object key, int index) {
    int i = 0;
    for (Object item = get(component, key);
         item != null; item = get(item, ":next")) {
      if (i == index) {
        return item;
      }
      i++;
    }
    return null;
  }

  /**
   *
   */
  private int getIndex(Object component, Object key, Object value) {
    int index = 0;
    for (Object item = get(component, key);
         item != null; item = get(item, ":next")) {
      if (value == item) {
        return index;
      }
      index++;
    }
    return -1;
  }

  /**
   *
   */
  public void add(Object component) {
    add(content, component, 0);
  }

  /**
   *
   */
  public void add(Object parent, Object component) {
    add(parent, component, -1);
  }

  /**
   *
   */
  public void add(Object parent, Object component, int index) {
    addImpl(parent, component, index);
    update(component, "validate");
    if (parent == content) {
      setNextFocusable(component, false);
    }
  }

  /**
   *
   */
  //private void addItem(Object parent, Object key, Object component) {
  //  insertItem(parent, key, component, -1);
  //}

  /**
   *
   */
  private void insertItem(Object parent,
                          Object key, Object component, int index) {
    Object target = get(parent, key);
    if (index == -1) {
      while (target != null) {
        target = get(parent = target, key = ":next");
      }
    }
    else {
      for (int i = 0; i < index; i++) {
        target = get(parent = target, key = ":next");
      }
      set(component, ":next", get(parent, key));
    }
    set(parent, key, component);
  }

  /**
   *
   */
  public void remove(Object component) {
    Object parent = getParent(component);
    String parentclass = getClass(parent);
    String classname = getClass(component);
    String listkey = ("combobox" == parentclass) ? "choice" :
                     (("tabbedpane" == parentclass) && ("tab" == classname)) ? "tab" :
                     ("list" == parentclass) ? "item" :
                     (("table" == parentclass) && ("column" == classname)) ? "column" :
                     (("table" == parentclass) && ("row" == classname)) ? "row" :
                     ("row" == parentclass) ? "cell" :
                     (("tree" == parentclass) || ("node" == parentclass)) ? "node" :
                     (("menubar" == parentclass) || ("menu" == parentclass)) ? "menu" :
                     (("panel" == parentclass) || ("desktop" == parentclass) ||
                      ("splitpane" == parentclass) || ("dialog" == parentclass) ||
                      ("tabbedpane" == parentclass)) ? "component" : null;
    if (listkey == null) {
      throw new IllegalArgumentException("unknown " + classname);
    }
    update(component, "validate");
    removeItemImpl(parent, listkey, component);
    // reuest focus for its parent if the component (or subcomponent) is currently focused
    for (Object comp = focusowner; comp != null; comp = getParent(comp)) {
      if (comp == component) {
        setNextFocusable(parent, false);
        break;
      }
    }
  }

  /**
   *
   */
  private void removeItemImpl(Object parent, Object key, Object component) {
    Object target = get(parent, key);
    while (target != component) {
      target = get(parent = target, key = ":next"); // (target != null)
    }
    set(parent, key, get(target, ":next"));
    set(target, ":next", null);
    set(target, ":parent", null);
  }

    /*
    private void removeItemImpl(Object parent, String key, int index) {
        Object target = get(parent, key);
        for (int i = 0; i < index; i++) {
            target = get(parent = target, key = ":next");
        }
        set(parent, key, get(target, ":next"));
        set(target, ":next", null);
    }*/

  /**
   *
   */
  private String getComponentName(Object parent, Object classname) {
    String parentclass = getClass(parent);
    String compname = ("combobox" == parentclass) ? "choice" :
                      ("list" == parentclass) ? "item" :
                      ("row" == parentclass) ? "cell" :
                      (("tree" == parentclass) || ("node" == parentclass)) ? "node" :
                      (("menubar" == parentclass) || ("menu" == parentclass)) ? "menu" :
                      (("panel" == parentclass) || ("desktop" == parentclass) ||
                       ("splitpane" == parentclass) || ("dialog" == parentclass)) ?
                      "component" : null;
    if ((compname != null) && ((classname == null) ||
                               compname.equals(classname))) {
      return compname;
    }
    if ("tabbedpane" == parentclass) {
      if ("tab".equals(classname)) {
        return "tab";
      }
      if ((classname == null) || "component".equals(classname)) {
        return "component";
      }
    }
    else if ("table" == parentclass) {
      if ("column".equals(classname)) {
        return "column";
      }
      if ((classname == null) || "row".equals(classname)) {
        return "row";
      }
    }
    throw new IllegalArgumentException("unknown " + classname);
  }

  /**
   *
   */
  public Object find(String name) {
    return find(content, name);
  }

  /**
   *
   */
  public Object find(Object component, String name) {
    if (name.equals(get(component, "name"))) {
      return component;
    }
    String classname = getClass(component);
    String childname = null;
    String childname2 = null;
    if (("panel" == classname) || ("desktop" == classname) ||
        ("splitpane" == classname) || ("dialog" == classname)) {
      childname = "component";
    }
    else if ("combobox" == classname) {
      childname = "choice";
    }
    else if ("tabbedpane" == classname) {
      childname = "tab";
      childname2 = "component";
    }
    else if ("list" == classname) {
      childname = "item";
    }
    else if ("table" == classname) {
      childname = "column";
      childname2 = "row";
    }
    else if ("row" == classname) {
      childname = "cell";
    }
    else if (("tree" == classname) || ("node" == classname)) {
      childname = "node";
    }
    else if (("menubar" == classname) || ("menu" == classname)) {
      childname = "menu";
    }
    while (childname != null) {
      for (Object comp = get(component, childname);
           comp != null; comp = get(comp, ":next")) {
        Object found = find(comp, name);
        if (found != null) {
          return found;
        }
      }
      childname = childname2;
      childname2 = null;
    }
    return null;
  }

  // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----

    /*public String toXML(Object component) {
        StringBuffer xml = new StringBuffer();
        Object[] entry = (Object[]) component;
        String classname = (String) entry[1];
        while ((entry = (Object[]) entry[2]) != null) {
            try {
                Object[] definition = getDefinition(component, (String) entry[0], null);
                if (definition != null) {
                    xml.append(" " + entry[0] + "=\"" + entry[1] + "\"");
                }
            } catch (IllegalArgumentException exc) {}
        }
        return xml.toString();
    }*/

  /**
   *
   */
  public Object parse(String path) throws Exception {
    InputStream inputstream = null;
    try { //java
      //midp inputstream = getClass().getResourceAsStream(path);
      inputstream = getClass().getResourceAsStream(path); //java
      //System.out.println("> " + path + " " + inputstream);
    }
    catch (Throwable e) {
    } //java
    //if (inputstream == null) { // applet code
    //    inputstream = new URL(getCodeBase(), path).openStream();
    //}
    return parse(inputstream); //, Object handler
  }

  /**
   *
   */
  public Object parse(InputStream inputstream) throws Exception {
    return parse(inputstream, true);
  }

  /**
   *
   */
  protected void parseXML(InputStream inputstream) throws Exception {
    parse(inputstream, false);
  }

  /**
   *
   */
  protected void startElement(String name, Hashtable attributelist) {
    java.lang.ref.Reference.reachabilityFence(name);
    java.lang.ref.Reference.reachabilityFence(attributelist);
  }

  /**
   *
   */
  protected void characters(String text) {
    java.lang.ref.Reference.reachabilityFence(text);
  }

  /**
   *
   */
  protected void endElement() {
  }

  /**
   *
   */
  private Object parse(InputStream inputstream, boolean validate) throws Exception {
    BufferedReader reader = new BufferedReader(new InputStreamReader(inputstream)); //java
    //midp InputStreamReader reader = new InputStreamReader(inputstream);
    Object[] parentlist = null;
    Object current = null;
    Hashtable attributelist = null;
    StringBuffer text = new StringBuffer();
    for (int c = reader.read(); c != -1; ) {
      if (c == '<') {
        if ((c = reader.read()) == '/') { //endtag
          if (text.length() > 0) {
            if (text.charAt(text.length() - 1) == ' ') {
              text.setLength(text.length() - 1);
            }
            if (!validate) {
              characters(text.toString());
            }// else {
            //addContent(current, text.toString());
            //}
            text.setLength(0);
          }
          String tagname = (String)parentlist[2]; //getClass(current);
          for (int i = 0; i < tagname.length(); i++) { // current-tag
            if ((c = reader.read()) != tagname.charAt(i)) {
              throw new IllegalArgumentException(tagname);
            }
          }
          while (" \t\n\r".indexOf(c = reader.read()) != -1) ; // whitespace
          if (c != '>') throw new IllegalArgumentException(); // '>'
          c = reader.read();
          if (!validate) {
            endElement();
          }
          if (parentlist[0] == null) {
            reader.close();
            return current;
          }
          current = parentlist[0];
          parentlist = (Object[])parentlist[1];
        }
        else { //start or standalone tag
          boolean instruction = (c == '?'); // Processing Instructions
          if (c == '!') {
            while ((c = reader.read()) != '>') ;
            continue;
          } // DOCTYPE
          if (instruction) {
            c = reader.read();
          }
          text.setLength(0);
          boolean iscomment = false;
          while (">/ \t\n\r".indexOf(c) == -1) {
            text.append((char)c);
            if ((text.length() == 3) && (text.charAt(0) == '!') &&
                (text.charAt(1) == '-') && (text.charAt(2) == '-')) {
              int m = 0;
              while (true) {
                c = reader.read();
                if (c == '-') {
                  m++;
                }
                else if ((c == '>') && (m >= 2)) {
                  break;
                }
                else {
                  m = 0;
                }
              }
              iscomment = true;
            }
            c = reader.read();
          }
          if (iscomment) {
            continue;
          }
          if (!instruction) {
            String tagname = text.toString();
            parentlist = new Object[]{current, parentlist, tagname};
            if (validate) {
              current = (current != null) ?
                        addElement(current, tagname) : create(tagname);
            }
            else {
              current = tagname;
            }
          }
          text.setLength(0);
          while (true) {
            boolean whitespace = false;
            while (" \t\n\r".indexOf(c) != -1) {
              c = reader.read();
              whitespace = true;
            }
            if (c == '>') {
              if (instruction) throw new IllegalArgumentException(); // '?>'
              if (!validate) {
                startElement((String)current, attributelist);
                attributelist = null;
              }
              c = reader.read();
              break;
            }
            else if (c == '/') {
              if (instruction) throw new IllegalArgumentException(); // '?>'
              if ((c = reader.read()) != '>') {
                throw new IllegalArgumentException(); // '>'
              }
              if (!validate) {
                startElement((String)current, attributelist);
                attributelist = null;
                endElement();
              }
              if (parentlist[0] == null) {
                reader.close();
                return current;
              }
              current = parentlist[0];
              parentlist = (Object[])parentlist[1];
              c = reader.read();
              break;
            }
            else if (instruction && (c == '?')) {
              if ((c = reader.read()) != '>') {
                throw new IllegalArgumentException(); // '>'
              }
              c = reader.read();
              break;
            }
            else if (whitespace) {
              while ("= \t\n\r".indexOf(c) == -1) {
                text.append((char)c);
                c = reader.read();
              }
              String key = text.toString();
              text.setLength(0);
              while (" \t\n\r".indexOf(c) != -1) c = reader.read();
              if (c != '=') throw new IllegalArgumentException();
              while (" \t\n\r".indexOf(c = reader.read()) != -1) ;
              char quote = (char)c;
              if ((c != '\"') && (c != '\'')) throw new IllegalArgumentException();
              while (quote != (c = reader.read())) {
                if (c == '&') {
                  StringBuffer eb = new StringBuffer();
                  while (';' != (c = reader.read())) {
                    eb.append((char)c);
                  }
                  String entity = eb.toString();
                  if ("lt".equals(entity)) {
                    text.append('<');
                  }
                  else if ("gt".equals(entity)) {
                    text.append('>');
                  }
                  else if ("amp".equals(entity)) {
                    text.append('&');
                  }
                  else if ("quot".equals(entity)) {
                    text.append('"');
                  }
                  else if ("apos".equals(entity)) {
                    text.append('\'');
                  }
                  else if (entity.startsWith("#")) {
                    text.append((char)Integer.parseInt(entity.substring(1)));
                  }
                  else {
                    throw new IllegalArgumentException("unknown " + "entity " + entity);
                  }
                }
                else {
                  text.append((char)c);
                }
              }
              if (!instruction) {
                if (validate) {
                  addAttribute(current, key, text.toString());
                }
                else {
                  if (attributelist == null) {
                    attributelist = new Hashtable();
                  }
                  attributelist.put(key, text.toString());
                }
              }
              text.setLength(0);
              c = reader.read();
            }
            else {
              throw new IllegalArgumentException();
            }
          }
        }
      }
      else {
        if (" \t\n\r".indexOf(c) != -1) {
          if ((text.length() > 0) && (text.charAt(text.length() - 1) != ' ')) {
            text.append(' ');
          }
        }
        else {
          text.append((char)c);
        }
        c = reader.read();
      }
    }
    throw new IllegalArgumentException();
  }

    /*private InputStream inputreader;
    private byte[] data;
    private int inputfrom = 0, inputto = 0;
    private int read() throws Exception {
        if (data == null) { data = new byte[1024]; }
        if (inputfrom >= inputto) {
            inputfrom = 0;
            inputto = inputreader.read(data);
        }
        inputfrom++;
        return data[inputfrom - 1];
    }*/

  /**
   *
   */
  private void addImpl(Object parent, Object component, int index) {
    String parentclass = getClass(parent);
    String classname = getClass(component);
    //System.out.println("add " + classname + " -> " + parentclass);
    if ((("combobox" == parentclass) && ("choice" == classname)) ||
        (("tabbedpane" == parentclass) && ("tab" == classname)) ||
        (("list" == parentclass) && ("item" == classname)) ||
        (("table" == parentclass) && (("column" == classname) || ("row" == classname))) ||
        (("row" == parentclass) && ("cell" == classname)) ||
        ((("tree" == parentclass) || ("node" == parentclass)) && ("node" == classname)) ||
        (("menubar" == parentclass) && ("menu" == classname))) {
      classname = classname; // compiler bug
    }
    else if (("menu" == parentclass) && (("menu" == classname) || ("menuitem" == classname) ||
                                         ("checkboxmenuitem" == classname) || ("separator" == classname))) {
      classname = "menu";
    }
    else if (("panel" == parentclass) || ("desktop" == parentclass) ||
             ("splitpane" == parentclass) || ("dialog" == parentclass) ||
             ("tabbedpane" == parentclass)) {
      while ("component" != classname) {
        String extendclass = null;
        for (int i = 0; i < dtd.length; i += 3) {
          if (classname == dtd[i]) {
            extendclass = (String)dtd[i + 1];
            break;
          }
        }
        if (extendclass == null) throw new IllegalArgumentException(classname + " not component");
        classname = extendclass;
      }
    }
    else {
      throw new IllegalArgumentException(classname + " add " + parentclass);
    }
    insertItem(parent, (String)classname, component, index);
    set(component, ":parent", parent);
    //if (parent == content) System.out.println(getClass(parent) + ".add(" + getClass(component) + ") : " + classname);
  }

  /**
   *
   */
  private Object addElement(Object parent, String name) {
    //System.out.println("create '" + name + "'");
    Object component = create(name);
    addImpl(parent, component, -1);
    return component;
  }

  /**
   *
   */
  private void addAttribute(Object component, String key, String value) {
    //System.out.println("attribute '" + key + "'='" + value + "'");
    Object[] definition = getDefinition(component, key, null);
    key = (String)definition[1];
    if ("string" == definition[0]) {
      setString(component, key, value, (String)definition[3]);
    }
    else if ("choice" == definition[0]) {
      String[] values = (String[])definition[3];
      setChoice(component, key, value, values, values[0]);
    }
    else if ("boolean" == definition[0]) {
      if ("true".equals(value)) {
        if (definition[3] == Boolean.FALSE) {
          set(component, key, Boolean.TRUE);
        }
      }
      else if ("false".equals(value)) {
        if (definition[3] == Boolean.TRUE) {
          set(component, key, Boolean.FALSE);
        }
      }
      else {
        throw new IllegalArgumentException(value);
      }
    }
    else if ("integer" == definition[0]) {
      set(component, key, Integer.valueOf(value));
    }
    else if ("icon" == definition[0]) {
      set(component, key, getIcon(value));
    }
    else if ("method" == definition[0]) {
      try { //java
        set(component, key, getClass().getMethod(value, null)); //java
      }
      catch (Exception exc) {
        System.err.println(value);
        exc.printStackTrace();
      } //java
      //midp setMethod(component, key, value);
    }
    //java>
    else if ("bean" == definition[0]) {
      try {
        set(component, key, (Component)Class.forName(value).newInstance());
      }
      catch (Exception exc) {
        System.err.println(value);
        exc.printStackTrace();
      }
    }
    //<java
    else {
      throw new IllegalArgumentException((String)definition[0]);
    }
  }

  /**
   *
   */
  private Object[] getDefinition(Object component, String key, String type) {
    Object classname = getClass(component);
    Object currentname = classname;
    while (classname != null) {
      for (int i = 0; i < dtd.length; i += 3) {
        if (dtd[i] == classname) {
          Object[][] attributes = (Object[][])dtd[i + 2];
          for (int j = 0; j < attributes.length; j++) {
            if (attributes[j][1].equals(key)) {
              if ((type != null) && (type != attributes[j][0])) {
                throw new IllegalArgumentException(attributes[j][0].toString());
              }
              return attributes[j];
            }
          }
          classname = dtd[i + 1];
          break;
        }
      }
    }
    throw new IllegalArgumentException("unknown " + key + " " + type +
                                       " for " + currentname);
  }

  /**
   *
   */
  //private void addContent(Object component, String content) {
  //set(component, "content", content);
  //}

  // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----

  /**
   *
   */
  public void setString(Object component, String key, String value) {
    Object[] definition = getDefinition(component, key, "string");
    if (setString(component, (String)definition[1],
                  value, (String)definition[3])) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public String getString(Object component, String key) {
    Object[] definition = getDefinition(component, key, "string");
    return getString(component, (String)definition[1],
                     (String)definition[3]);
  }

  /**
   *
   */
  public void setChoice(Object component, String key, String value) {
    Object[] definition = getDefinition(component, key, "choice");
    String[] values = (String[])definition[3];
    if (setChoice(component, (String)definition[1],
                  value, values, values[0])) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public String getChoice(Object component, String key) {
    Object[] definition = getDefinition(component, key, "choice");
    return getString(component, (String)definition[1],
                     ((String[])definition[3])[0]);
  }

  /**
   *
   */
  public void setBoolean(Object component, String key, boolean value) {
    Object[] definition = getDefinition(component, key, "boolean");
    if (setBoolean(component, (String)definition[1],
                   value, (definition[3] == Boolean.TRUE))) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public boolean getBoolean(Object component, String key) {
    Object[] definition = getDefinition(component, key, "boolean");
    return getBoolean(component, (String)definition[1],
                      (definition[3] == Boolean.TRUE));
  }

  /**
   *
   */
  public void setInteger(Object component, String key, int value) {
    Object[] definition = getDefinition(component, key, "integer");
    if (setInteger(component, (String)definition[1],
                   value, ((Integer)definition[3]).intValue())) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public int getInteger(Object component, String key) {
    Object[] definition = getDefinition(component, key, "integer");
    return getInteger(component, (String)definition[1],
                      ((Integer)definition[3]).intValue());
  }

  /**
   *
   */
  public void setIcon(Object component, String key, Image icon) {
    Object[] definition = getDefinition(component, key, "icon");
    if (set(component, (String)definition[1], icon)) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public Image getIcon(Object component, String key) {
    Object[] definition = getDefinition(component, key, "icon");
    return getIcon(component, (String)definition[1], (Image)definition[3]);
  }

  /**
   *
   */
  public void setMethod(Object component, String key, Method method) { //java
    //midp public void setMethod(Object component, String key, String method) {
    Object[] definition = getDefinition(component, key, "method");
    if (set(component, (String)definition[1], method)) {
      update(component, definition[2]);
    }
  }

  /**
   *
   */
  public Method getMethod(Object component, String key) { //java
    //midp public String getMethod(Object component, String key) {
    Object[] definition = getDefinition(component, key, "method");
    return (Method)get(component, (String)definition[1]); //java
    //midp return (String) get(component, (String) definition[1]);
  }

  /**
   *
   */
  private void update(Object component, Object mode) {
    if ("parent" == mode) {
      component = getParent(component);
      mode = "validate";
    }
    boolean firstpaint = true;
    int x = 0;
    int y = 0;
    int width = 0;
    int height = 0;
    while (component != null) {
      if (!getBoolean(component, "visible", true)) {
        break;
      }
      if ("paint" == mode) {//|| (firstpaint && (component == content))
        Rectangle bounds = getRectangle(component, "bounds");
        if (bounds == null) {
          return;
        }
        if (firstpaint) {
          x = bounds.x;
          y = bounds.y;
          width = Math.abs(bounds.width);
          height = bounds.height;
          firstpaint = false;
        }
        else {
          x += bounds.x;
          y += bounds.y;
        }
        if (component == content) {
          repaint(x, y, width, height);
        }
      }
      Object parent = getParent(component);
      String classname = getClass(parent);
      if ("combobox" == classname) {
        parent = get(parent, "combolist");
      }
      else if ("menu" == classname) {
        parent = get(parent, "popupmenu");
      }
      else if (("paint" == mode) && ("tabbedpane" == classname)) {
        if (getItemImpl(parent, "component",
                        getInteger(parent, "selected", 0)) != component) {
          break;
        }
      }
      if (("layout" == mode) || (("validate" == mode) &&
                                 (("list" == classname) || ("table" == classname) ||
                                  ("tree" == classname) || ("dialog" == classname) || (parent == content)))) {
        Rectangle bounds = getRectangle(parent, "bounds");
        if (bounds == null) {
          return;
        }
        bounds.width = -1 * Math.abs(bounds.width);
        mode = "paint";
      }
      component = parent;
    }
  }

  // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----

  /**
   *
   */
  private boolean setString(Object component,
                            String key, String value, String defaultvalue) {
    java.lang.ref.Reference.reachabilityFence(defaultvalue);
        boolean changed = set(component, key, value);
        if (changed && ("name" == key)) {
            //for (Class cls = getClass(); cls != null; cls = cls.getSuperclass()) {
            Field[] fields = getClass().getDeclaredFields();
            for (int i = 0; i < fields.length; i++) {
                if ((fields[i].getType() == Object.class) &&
                        fields[i].getName().equals(value)) {
                    try {
                        fields[i].set(this, component);
                        return true;
                    } catch (IllegalAccessException iae) {}
                }
            }
            //}
            try {
                getClass().getField(value).set(this, component);
            } catch (Exception exc) {}
        }
        return changed;
  }

  /**
   *
   */
  private String getString(Object component,
                           String key, String defaultvalue) {
    Object value = get(component, key);
    return (value == null) ? defaultvalue : (String)value;
  }

  /**
   *
   */
  private boolean setChoice(Object component,
                            String key, String value, String[] values, String defaultvalue) {
    if (value == null) {
      return set(component, key, defaultvalue);
    }
    for (int i = 0; i < values.length; i++) {
      if (value.equals(values[i])) {
        return set(component, key, values[i]);
      }
    }
    throw new IllegalArgumentException("unknown " + value + " for " + key);
  }

  /**
   *
   */
  private Image getIcon(Object component, String key, Image defaultvalue) {
    Object value = get(component, key);
    return (value == null) ? defaultvalue : (Image)value;
  }

  /**
   *
   */
  private boolean setBoolean(Object component,
                             String key, boolean value, boolean defaultvalue) {
    return set(component, key, (value == defaultvalue) ? null :
                               (value ? Boolean.TRUE : Boolean.FALSE));
  }

  /**
   *
   */
  private boolean getBoolean(Object component,
                             String key, boolean defaultvalue) {
    Object value = get(component, key);
    return (value == null) ? defaultvalue : ((Boolean)value).booleanValue();
  }

  /**
   *
   */
  private boolean setInteger(Object component,
                             String key, int value, int defaultvalue) {
    return set(component, key, (value == defaultvalue) ? null : new Integer(value));
  }

  /**
   *
   */
  private int getInteger(Object component, String key, int defaultvalue) {
    Object value = get(component, key);
    return (value == null) ? defaultvalue : ((Integer)value).intValue();
  }

  /**
   *
   */
  private void setRectangle(Object component,
                            String key, int x, int y, int width, int height) {
    Rectangle rectangle = getRectangle(component, key);
    if (rectangle != null) {
      rectangle.x = x;
      rectangle.y = y;
      rectangle.width = width;
      rectangle.height = height;
    }
    else {
      set(component, key, new Rectangle(x, y, width, height)); //java
      //midp set(component, key, new int[] { width, height, x, y });
    }
  }

  /**
   *
   */
  private Rectangle getRectangle(Object component, String key) {
    return (Rectangle)get(component, key); //java
    //midp return (int[]) get(component, key);
  }

  // ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- ----- -----

  /**
   *
   */
  public Image getIcon(String path) {
    return getIcon(path, true);
  }

  /**
   *
   */
  public Image getIcon(String path, boolean preload) {
    if ((path == null) || (path.length() == 0)) {
      return null;
    }
    Image image = null; //(Image) imagepool.get(path);
    //midp try {
    //midp     image = Image.createImage(path);
    //midp } catch (IOException ioe) {}
    //java>
    try {
      image = Toolkit.getDefaultToolkit().getImage(getClass().getResource(path));
      //image = Toolkit.getDefaultToolkit().getImage(ClassLoader.getSystemResource(path));
    }
    catch (Throwable e) {
    }
    if (image == null) {
      try {
        InputStream is = getClass().getResourceAsStream(path);
        //InputStream is = ClassLoader.getSystemResourceAsStream(path);
        if (is != null) {
          byte[] data = new byte[is.available()];
          is.read(data, 0, data.length);
          image = getToolkit().createImage(data);
          is.close();
        }
      }
      catch (Throwable e) {
      }
    }
    //if (image == null) { // applet code
    //    try {
    //        image = getImage(getCodeBase(), path);
    //    } catch (Throwable e) {}
    //}
    if (preload && (image != null)) {
      MediaTracker mediatracker = new MediaTracker(this);
      mediatracker.addImage(image, 1);
      try {
        mediatracker.waitForID(1, 50);
      }
      catch (InterruptedException ie) {
      }
      //imagepool.put(path, image);
    }
    //<java
    return image;
  }

  /**
   *
   */
  public boolean destroy() {
    return true;
  }

  private static Object[] dtd;

  static {
    Integer integer_1 = new Integer(-1);
    Integer integer0 = new Integer(0);
    Integer integer1 = new Integer(1);
    String[] orientation = {"horizontal", "vertical"};
    String[] leftcenterright = {"left", "center", "right"};
    String[] selections = {"single", "interval", "multiple"};
    dtd = new Object[]{
      "component", null, new Object[][]{
      {"string", "name", "", null},
      {"boolean", "enabled", "paint", Boolean.TRUE},
      {"boolean", "visible", "parent", Boolean.TRUE},
      {"string", "tooltip", "", null},
      {"integer", "width", "validate", integer0},
      {"integer", "height", "validate", integer0},
      {"integer", "colspan", "validate", integer1},
      {"integer", "rowspan", "validate", integer1},
      {"integer", "weightx", "validate", integer0},
      {"integer", "weighty", "validate", integer0},
      {"choice", "halign", "validate",
        new String[]{"fill", "center", "left", "right"}},
      {"choice", "valign", "validate",
        new String[]{"fill", "center", "top", "bottom"}}
      // component class String null*
      // parent Object null
      // (bounds) Rectangle 0 0 0 0
    },
      "label", "component", new Object[][]{
      {"string", "text", "validate", null},
      {"icon", "icon", "validate", null},
      {"choice", "alignment", "validate", leftcenterright},
      {"integer", "mnemonic", "paint", integer_1}},
      "button", "label", new Object[][]{
      {"choice", "alignment", "validate", new String[]{"center", "left", "right"}},
      {"method", "action", "", null},
      //{ "choice", "type", "", new String[] { "normal", "default", "cancel" } }
    },//...
      "checkbox", "label", new Object[][]{
      {"boolean", "selected", "paint", Boolean.FALSE}, //...group
      {"string", "group", "paint", null}, //...group
      {"method", "action", "", null}},
      "combobox", "textfield", new Object[][]{
      {"icon", "icon", "validate", null},
      {"integer", "selected", "layout", integer_1}},
      "choice", null, new Object[][]{
      {"string", "name", "", null},
      {"boolean", "enabled", "paint", Boolean.TRUE},
      {"string", "text", "", null},
      {"icon", "icon", "", null},
      {"choice", "alignment", "", leftcenterright},
      {"string", "tooltip", "", null}},
      "textfield", "component", new Object[][]{
      {"string", "text", "layout", ""},
      {"integer", "columns", "validate", integer0},
      {"boolean", "editable", "paint", Boolean.TRUE},
      {"integer", "start", "layout", integer0},
      {"integer", "end", "layout", integer0},
      {"method", "action", "", null}},
      "passwordfield", "textfield", new Object[][]{},
      "textarea", "textfield", new Object[][]{
      {"integer", "rows", "validate", integer0},
      {"boolean", "wrap", "layout", Boolean.FALSE}},
      "tabbedpane", "component", new Object[][]{
      {"choice", "placement", "validate",
        new String[]{"top", "left", "bottom", "right"}},
      {"integer", "selected", "paint", integer0},
      {"method", "action", "", null}}, //...focus
      "tab", "choice", new Object[][]{
      {"integer", "mnemonic", "paint", integer_1}},
      "panel", "component", new Object[][]{
      {"integer", "columns", "validate", integer0},
      {"integer", "top", "validate", integer0},
      {"integer", "left", "validate", integer0},
      {"integer", "bottom", "validate", integer0},
      {"integer", "right", "validate", integer0},
      {"integer", "gap", "validate", integer0}},
      "desktop", "component", new Object[][]{},
      "dialog", "panel", new Object[][]{
      {"string", "text", "", null},
      {"icon", "icon", "", null},
      {"boolean", "modal", "", Boolean.FALSE}},
      "spinbox", "textfield", new Object[][]{}, //...
      "progressbar", "component", new Object[][]{
      {"choice", "orientation", "validate", orientation},
      {"integer", "minimum", "paint", integer0}, //...checkvalue
      {"integer", "maximum", "paint", new Integer(100)},
      {"integer", "value", "paint", integer0}},
      // change stringpainted
      "slider", "progressbar", new Object[][]{
      {"integer", "unit", "", new Integer(5)},
      {"integer", "block", "", new Integer(25)},
      {"method", "action", "", null}},
      // minor/majortickspacing
      // inverted
      // labelincrement labelstart
      "splitpane", "component", new Object[][]{
      {"choice", "orientation", "validate", orientation},
      {"integer", "divider", "layout", integer_1}},
      "list", "component", new Object[][]{
      {"choice", "selection", "paint", selections},
      {"method", "action", "", null}}, //...?
      "item", "choice", new Object[][]{
      {"boolean", "selected", "", Boolean.FALSE}},
      "table", "component", new Object[][]{
      {"choice", "selection", "paint", selections},
      {"method", "action", "", null}
                /*{ "choice", "selection",
                    new String[] { "singlerow", "rowinterval", "multiplerow",
                        "cell", "cellinterval",
                        "singlecolumn", "columninterval", "multiplecolumn" } }*/},
      "column", "choice", new Object[][]{
      {"integer", "width", "", new Integer(80)}},
      "row", null, new Object[][]{
      {"boolean", "selected", "", Boolean.FALSE}},
      "cell", "choice", new Object[][]{},
      "tree", "component", new Object[][]{
      {"choice", "selection", "paint", selections},
      {"method", "action", "", null},
      {"method", "expand", "", null},
      {"method", "collapse", "", null}},
      "node", "choice", new Object[][]{
      {"boolean", "selected", "", Boolean.FALSE},
      {"boolean", "expanded", "", Boolean.TRUE}},
      "separator", "component", new Object[][]{},
      "menubar", "component", new Object[][]{},
      "menu", "choice", new Object[][]{
      {"integer", "mnemonic", "paint", integer_1}},
      "menuitem", "choice", new Object[][]{
      {"string", "accelerator", "", null},
      {"method", "action", "", null},
      {"integer", "mnemonic", "paint", integer_1}
      //... KeyStroke=keyCode+modifiers(SHIFT CTRL META ALT_MASK)
    },
      "checkboxmenuitem", "menuitem", new Object[][]{
      {"boolean", "selected", "paint", Boolean.FALSE}, //...group
      {"string", "group", "paint", null}}, //...group
      "bean", "component", new Object[][]{
      {"bean", "bean", "", null}
    }
    };
  }
}
