GWT Java

/*
 * Copyright 2006 Google Inc.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
 * use this file except in compliance with the License. You may obtain a copy of
 * the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations under
 * the License.
 */
 
package com.rntsoft.gwt.client;
import java.util.Iterator;
import java.util.Vector;
import java.util.EventListener;
import java.util.List;
import java.util.ArrayList;
import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.KeyboardListener;
import com.google.gwt.user.client.ui.KeyboardListenerCollection;
import com.google.gwt.user.client.ui.MouseListener;
import com.google.gwt.user.client.ui.MouseListenerCollection;
import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.user.client.ui.HasFocus;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.Tree;
import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.RootPanel;
public class GWTClient implements EntryPoint{
  /**
   * This is the entry point method.
   */
  public void onModuleLoad() {
    HorizontalPanel p = new HorizontalPanel();
    TreeTable fileTreeTable = createFileTreeTable();
    p.add(fileTreeTable);
    TreeTable treeTable = createToDoTreeTable();
    p.add(treeTable);
    treeTable = createSimpleTreeTable();
    p.add(treeTable);
    RootPanel.get("slot1").add(p);
    // Adds a few items after display
    fileTreeTable.addItem(new File("File E", "1 TB", "Jan 1, 2005"));
    fileTreeTable.getItem(0).addItem(new File("File E", "1 TB", "Jan 1, 2005"));
  }
  
  /**
   * Creates an example tree using the default renderer. Wigdets are 
   * rendered, objects are rendered with toString(), and array values
   * are inserted across the table. 
   */
  public TreeTable createSimpleTreeTable() {
    TreeTable treeTable = new TreeTable();
    
    TreeItem item = treeTable.addItem("I'm text");
    item.addItem("I'm HTML");
    item.setState(true);
    item = treeTable.addItem(new CheckBox("I'm a Widget!!!"));
    item.addItem("Child");
    item = treeTable.addItem("Parent");
    item.addItem("Child");
    treeTable.addItem(new Object[] {new CheckBox("I'm in an array"), "1", "2", new CheckBox("3")});
    
    return treeTable;
  }
  
  /**
   * Creates an example tree using a custom renderer. File objects are 
   * added as user objects and the renderer displays values. 
   * @return
   */
  public TreeTable createFileTreeTable() {
    TreeTable treeTable = new TreeTable();
    treeTable.setBorderWidth(1);
    treeTable.setCellPadding(3);
    treeTable.setCellSpacing(0);
    
    TreeItem item = treeTable.addItem(new File("Folder A", "-", "Apr 4, 2007"));
    item.addItem(new File("File AA", "128 kb", "Apr 4, 2007"));
    item.addItem(new File("File AB", "64 kb", "Apr 1, 2007"));
    item = treeTable.addItem(new File("Folder B", "-", "Jan 21, 2006"));
    item.addItem(new File("File BA", "256 kb", "Jan 18, 2006"));
    item = item.addItem(new File("Folder BB", "-", "Jan 21, 2006"));
    item.addItem(new File("File BBA", "256 kb", "Jan 18, 2006"));
    item.addItem(new File("File BBB", "256 kb", "Jan 18, 2006"));
    item.addItem(new File("File BBC", "256 kb", "Jan 18, 2006"));
    item.addItem(new File("File BBD", "256 kb", "Jan 21, 2006"));
    treeTable.addItem(new File("File C", "256 kb", "Jan 18, 2006"));
    treeTable.addItem(new File("File D", "256 kb", "Jan 18, 2006"));
    
    treeTable.setRenderer(new ExampleRenderer());
    
    return treeTable;
  }
  
  /**
   * Creates an example tree using a custom renderer. ToDo objects
   * are added as the user objects of TreeItems. The renderer turns
   * them into Widgets. 
   * @return
   */
  public TreeTable createToDoTreeTable() {
    TreeTable treeTable = new TreeTable();
    TreeItem grp1 = treeTable.addItem("Group 1");
    grp1.addItem(new ToDo("Garbage", "3 days", "Matt"));
    grp1.addItem(new ToDo("Dishes", "1 day", "Matt"));
    grp1.addItem(new ToDo("Laundry", "2 days", "Matt"));
    TreeItem grp2 = treeTable.addItem("Group 2");
    grp2.addItem(new ToDo("Task 1", "2 days", "Unassigned"));
    grp2.addItem(new ToDo("Task 2", "3 day", "Unassigned"));
    grp2.addItem(new ToDo("Task 3", "7 days", "Unassigned"));
    
    treeTable.setRenderer(new ExampleRenderer());
    
    return treeTable;
  }
  
  class ExampleRenderer implements TreeTableRenderer {
    public void renderTreeItem(TreeTable table, TreeItem item, int row) {
      Object obj = item.getUserObject();
      if (obj instanceof ToDo) {
        ToDo todo = (ToDo) obj;
        item.setWidget(new CheckBox(todo.name));
        table.setText(row, 1, todo.due);
        table.setText(row, 2, todo.who);
      } else if (obj instanceof File) {
        File f = (File) obj;
        item.setText(f.name);
        table.setText(row, 1, f.size);
        table.setText(row, 2, f.date);
      } else if (obj != null) {
        item.setText(obj.toString());
      }
    }
  }
  
  public class File {
    public String name;
    public String size;
    public String date;
    public File(String n, String s, String d) {
      name = n;
      size = s;
      date = d;
    }
    
    public String toString() {
      return name;
    }
  }
  
  public class ToDo {
    public String name;
    public String due;
    public String who;
    public ToDo(String n, String d, String w) {
      name = n;
      due = d;
      who = w;
    }
    
    public String toString() {
      return name;
    }
  }
}
/**
 * Shameless copy of com.google.gwt.user.client.ui.TreeItem. GWT's TreeItem does
 * not expose enough to allow a simple subclass. If that changes, this class
 * will be greatly simplified.
 * 
 * Changes:
 * 
  • Removed the DOM hierarchy of tree nodes. Each node is
     * independent and therefore placable is a table cell.

  •  * 
  • Changed subclass to Widget so the TreeItem could be added to a table.
  •  
     * 
  • Changed parent Tree to TreeTable.

  •  * 
  • Worked around package scope methods from the GWT ui package.

  •  * 
  • Removed ContentPanel.

  •  * 
  • Added row index cache.

  •  * 
     * 
     * @author Matt Boyd (modifications to GWT's classes)
     */
    class TreeItem extends Widget implements HasHTML {
      private Vector children = new Vector();
      private Element itemTable, contentElem, imgElem;
      private boolean open;
      private TreeItem parentItem;
      private boolean selected;
      private Object userObject;
      private TreeTable table;
      private int row;
      
      private Widget widget;
      /**
       * Creates an empty tree item.
       */
      public TreeItem() {
        setElement(DOM.createDiv());
        itemTable = DOM.createTable();
        contentElem = DOM.createSpan();
        imgElem = DOM.createImg();
        // Uses the following Element hierarchy:
        // 
        // 
        // 
        // 
        // 
        // 
        // 
        // 

        Element tbody = DOM.createTBody(), tr = DOM.createTR();
        Element tdImg = DOM.createTD(), tdContent = DOM.createTD();
        DOM.appendChild(itemTable, tbody);
        DOM.appendChild(tbody, tr);
        DOM.appendChild(tr, tdImg);
        DOM.appendChild(tr, tdContent);
        DOM.setStyleAttribute(tdImg, "verticalAlign", "middle");
        DOM.setStyleAttribute(tdContent, "verticalAlign", "middle");
        DOM.appendChild(getElement(), itemTable);
        DOM.appendChild(tdImg, imgElem);
        DOM.appendChild(tdContent, contentElem);
        DOM.setAttribute(getElement(), "position", "relative");
        DOM.setStyleAttribute(contentElem, "display", "inline");
        DOM.setStyleAttribute(getElement(), "whiteSpace", "nowrap");
        DOM.setAttribute(itemTable, "whiteSpace", "nowrap");
        setStyleName(contentElem, "gwt-TreeItem", true);
      }
      
      public TreeItem(Object userObj) {
        this();
        setUserObject(userObj);
      }
      /**
       * Adds a child tree item containing the specified text.
       * 
       * @param itemText
       *            the text to be added
       * @return the item that was added
       */
      public TreeItem addItem(String itemText) {
        TreeItem ret = new TreeItem(itemText);
        addItem(ret);
        return ret;
      }
      
      public TreeItem addItem(Object userObj) {
        TreeItem ret = new TreeItem(userObj);
        addItem(ret);
        return ret;
      }
      
      /**
       * Adds another item as a child to this one.
       * 
       * @param item
       *            the item to be added
       */
      public void addItem(TreeItem item) {
        // If this element already belongs to a tree or tree item, it should be
        // removed.
        if ((item.getParentItem() != null) || (item.getTreeTable() != null)) {
          item.remove();
        }
        item.setTreeTable(table);
        item.setParentItem(this);
        children.add(item);
        int d = item.getDepth();
        if (d != 0) {
          DOM.setStyleAttribute(item.getElement(), "marginLeft", (d * 16) + "px");
        }
        if (table != null) {
          table.insertItem(item, getRow() + getChildCount());
          table.updateRowCache();
          table.updateVisibility(item);
        }
        if (children.size() == 1) {
          updateState();
        }
      }
      public int getRow() {
        return row;
      }
      void setRow(int r) {
        row = r;
      }
      /**
       * Returns the depth of this item. Depth of root child is 0.
       * 
       * @return
       */
      public int getDepth() {
        if (parentItem == null) {
          return 0;
        }
        return parentItem.getDepth() + 1;
      }
      /**
       * Returns the count of all descendents; includes this item in the count.
       * 
       * @return
       */
      int getDescendentCount() {
        int d = 1;
        for (int i = getChildCount() - 1; i >= 0; i--) {
          d += getChild(i).getDescendentCount();
        }
        return d;
      }
      /**
       * Adds a child tree item containing the specified widget.
       * 
       * @param widget
       *            the widget to be added
       * @return the item that was added
       */
      public TreeItem addItem(Widget widget) {
        TreeItem ret = new TreeItem(widget);
        addItem(ret);
        return ret;
      }
      /**
       * Gets the child at the specified index.
       * 
       * @param index
       *            the index to be retrieved
       * @return the item at that index
       */
      public TreeItem getChild(int index) {
        if ((index < 0) || (index >= children.size())) {
          return null;
        }
        return (TreeItem) children.get(index);
      }
      /**
       * Gets the number of children contained in this item.
       * 
       * @return this item's child count.
       */
      public int getChildCount() {
        return children.size();
      }
      /**
       * Gets the index of the specified child item.
       * 
       * @param child
       *            the child item to be found
       * @return the child's index, or -1 if none is found
       */
      public int getChildIndex(TreeItem child) {
        return children.indexOf(child);
      }
      public String getHTML() {
        return DOM.getInnerHTML(contentElem);
      }
      /**
       * Gets this item's parent.
       * 
       * @return the parent item
       */
      public TreeItem getParentItem() {
        return parentItem;
      }
      /**
       * Gets whether this item's children are displayed.
       * 
       * @return true if the item is open
       */
      public boolean getState() {
        return open;
      }
      public boolean isOpen() {
        return getState();
      }
      public String getText() {
        return DOM.getInnerText(contentElem);
      }
      /**
       * Gets the tree that contains this item.
       * 
       * @return the containing tree
       */
      public TreeTable getTreeTable() {
        return table;
      }
      /**
       * Gets the user-defined object associated with this item.
       * 
       * @return the item's user-defined object
       */
      public Object getUserObject() {
        return userObject;
      }
      /**
       * Gets the Widget associated with this tree item.
       * 
       * @return the widget
       */
      public Widget getWidget() {
        return widget;
      }
      /**
       * Determines whether this item is currently selected.
       * 
       * @return true if it is selected
       */
      public boolean isSelected() {
        return selected;
      }
      /**
       * Removes this item from its tree.
       */
      public void remove() {
        if (parentItem != null) {
          // If this item has a parent, remove self from it.
          parentItem.removeItem(this);
        } else if (table != null) {
          // If the item has no parent, but is in the Tree, it must be a
          // top-level
          // element.
          table.removeItem(this);
        }
      }
      /**
       * Removes one of this item's children.
       * 
       * @param item
       *            the item to be removed
       */
      public void removeItem(TreeItem item) {
        if (!children.contains(item)) {
          return;
        }
        // Update Item state.
        item.setTreeTable(null);
        item.setParentItem(null);
        children.remove(item);
        if (table != null) {
          table.removeItemFromTable(item);
        }
        if (children.size() == 0) {
          updateState();
        }
      }
      /**
       * Removes all of this item's children.
       */
      public void removeItems() {
        while (getChildCount() > 0) {
          removeItem(getChild(0));
        }
      }
      public void setHTML(String html) {
        DOM.setInnerHTML(contentElem, html);
    //    if (widget != null) {
    //      DOM.removeChild(contentElem, widget.getElement());
    //      widget = null;
    //    }
      }
      /**
       * Selects or deselects this item.
       * 
       * @param selected
       *            true to select the item, false
       *            to deselect it
       */
      public void setSelected(boolean selected) {
        if (this.selected == selected) {
          return;
        }
        this.selected = selected;
        setStyleName(contentElem, "gwt-TreeItem-selected", selected);
      }
      /**
       * Sets whether this item's children are displayed.
       * 
       * @param open
       *            whether the item is open
       */
      public void setState(boolean open) {
        setState(open, true);
      }
      /**
       * Sets whether this item's children are displayed.
       * 
       * @param open
       *            whether the item is open
       * @param fireEvents
       *            true to allow open/close events to be fired
       */
      public void setState(boolean open, boolean fireEvents) {
        if (open && children.size() == 0) {
          return;
        }
        this.open = open;
        if (open) {
          table.showChildren(this);
        } else {
          table.hideChildren(this);
        }
        updateState();
        if (fireEvents) {
          table.fireStateChanged(this);
        }
      }
      public void setText(String text) {
        DOM.setInnerText(contentElem, text);
    //    if (widget != null) {
    //      DOM.removeChild(contentElem, widget.getElement());
    //      widget = null;
    //    }
      }
      /**
       * Sets the user-defined object associated with this item.
       * 
       * @param userObj
       *            the item's user-defined object
       */
      public void setUserObject(Object userObj) {
        userObject = userObj;
      }
      /**
       * Sets the current widget. Any existing child widget will be removed.
       * 
       * @param widget
       *            Widget to set
       */
      public void setWidget(Widget w) {
        if (widget != null) {
          DOM.removeChild(contentElem, widget.getElement());
    //      widget.setParent(null);
        }
        
        if (w != null) {
          widget = w;
          DOM.setInnerText(contentElem, null);
          DOM.appendChild(contentElem, w.getElement());
    //      widget.setParent(this);
        }
      }
      /**
       * Returns the widget, if any, that should be focused on if this TreeItem is
       * selected.
       * 
       * @return widget to be focused.
       */
      protected HasFocus getFocusableWidget() {
        Widget widget = getWidget();
        if (widget instanceof HasFocus) {
          return (HasFocus) widget;
        } else {
          return null;
        }
      }
      void addTreeItems(List accum) {
        for (int i = 0; i < children.size(); i++) {
          TreeItem item = (TreeItem) children.get(i);
          accum.add(item);
          item.addTreeItems(accum);
        }
      }
      Vector getChildren() {
        return children;
      }
      Element getContentElem() {
        return contentElem;
      }
      int getContentHeight() {
        return DOM.getIntAttribute(itemTable, "offsetHeight");
      }
      Element getImageElement() {
        return imgElem;
      }
      int getTreeTop() {
        TreeItem item = this;
        int ret = 0;
        while (item != null) {
          ret += DOM.getIntAttribute(item.getElement(), "offsetTop");
          item = item.getParentItem();
        }
        return ret;
      }
      String imgSrc(String img) {
        if (table == null) {
          return img;
        }
        return table.getImageBase() + img;
      }
      void setParentItem(TreeItem parent) {
        this.parentItem = parent;
      }
      void setTreeTable(TreeTable table) {
        if (this.table == table) {
          return;
        }
        if (this.table != null) {
          if (this.table.getSelectedItem() == this) {
            this.table.setSelectedItem(null);
          }
        }
        this.table = table;
        for (int i = 0, n = children.size(); i < n; ++i) {
          ((TreeItem) children.get(i)).setTreeTable(table);
        }
        updateState();
      }
      void updateState() {
        if (children.size() == 0) {
          // UIObject.setVisible(childSpanElem, false);
          DOM.setAttribute(imgElem, "src", imgSrc("tree_white.gif"));
          return;
        }
        // We must use 'display' rather than 'visibility' here,
        // or the children will always take up space.
        if (open) {
          // UIObject.setVisible(childSpanElem, true);
          DOM.setAttribute(imgElem, "src", imgSrc("tree_open.gif"));
        } else {
          // UIObject.setVisible(childSpanElem, false);
          DOM.setAttribute(imgElem, "src", imgSrc("tree_closed.gif"));
        }
        
    //    if (getParentItem() != null) {
    //      table.updateVisibility(getParentItem());
    //    }
      }
      void updateStateRecursive() {
        updateState();
        for (int i = 0, n = children.size(); i < n; ++i) {
          ((TreeItem) children.get(i)).updateStateRecursive();
        }
      }
    }
    /**
     * Shameless copy of com.google.gwt.user.client.ui.TreeListener. 
     * Changed to replace GWT's TreeItem with the altered TreeItem. 
     * 
     * Event listener interface for tree events.
     */
    public interface TreeTableListener extends EventListener {
      /**
       * Fired when a tree item is selected.
       * 
       * @param item the item being selected.
       */
      void onTreeItemSelected(TreeItem item);
      /**
       * Fired when a tree item is opened or closed.
       * 
       * @param item the item whose state is changing.
       */
      void onTreeItemStateChanged(TreeItem item);
    }
    /**
     * Shameless copy of com.google.gwt.user.client.ui.TreeListenerCollection. 
     * Changed to replace TreeListener with TreeTableListener. 
     * 
     * A helper class for implementers of the SourcesClickEvents interface. This
     * subclass of Vector assumes that all objects added to it will be of type
     * {@link com.google.gwt.user.client.ui.ClickListener}.
     */
     class TreeTableListenerCollection extends Vector {
      /**
       * Fires a "tree item selected" event to all listeners.
       * 
       * @param item the tree item being selected.
       */
      public void fireItemSelected(TreeItem item) {
        for (Iterator it = iterator(); it.hasNext();) {
          TreeTableListener listener = (TreeTableListener) it.next();
          listener.onTreeItemSelected(item);
        }
      }
      /**
       * Fires a "tree item state changed" event to all listeners.
       * 
       * @param item the tree item whose state has changed.
       */
      public void fireItemStateChanged(TreeItem item) {
        for (Iterator it = iterator(); it.hasNext();) {
          TreeTableListener listener = (TreeTableListener) it.next();
          listener.onTreeItemStateChanged(item);
        }
      }
    }
    interface TreeTableRenderer {
      /**
       * Called to render a tree item row. 
       * @param table
       * @param item
       * @param row
       */
      void renderTreeItem(TreeTable table, TreeItem item, int row);
    }
    /*
     * Copyright 2006 Google Inc.
     * 
     * Licensed under the Apache License, Version 2.0 (the "License"); you may not
     * use this file except in compliance with the License. You may obtain a copy of
     * the License at
     * 
     * http://www.apache.org/licenses/LICENSE-2.0
     * 
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
     * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     * License for the specific language governing permissions and limitations under
     * the License.
     */
    /**
     * Shameless copy of com.google.gwt.user.client.ui.Tree. Extension of FlexTable
     * adding a tree in one column. Uses a TreeItem model and row based rendering of
     * table cells. 
     * 
     * Changes: 
     * 

       * 
    • Removed focus functionality from Tree code. It was causing problems with IE. 
       * Not sure how applicable it is with FlexTable as the base class. It may have
       * problems playing well with GWT because of package scope work arounds. Seems
       * to work ok without the code, minus drawing an outline.

    •  * 
    • Made TreeItem a Widget to be added to a table cell. Removed ContentPanel
       * handling from the Tree code. 
       * 
    • Disabled some Widget add/remove code. This may cause strange bugs. Again, 
       * package scope issues. This needs a work around.

    •  * 
    • Streamlined findItemByChain() and modified elementClicked() to search the
       * table. This should probably be rewritten to leverage FlexTable. 
       * 

     * 
     * Notes:
     * 

       * 
    • If anyone has a firm understanding of "focus" in GWT I could use the help
       * cleaning this up.

    •  * 

     * 
     * @author Matt Boyd (modifications to GWT's classes)
     */
    class TreeTable extends FlexTable {
      private Element headElem;
      private TreeItem curSelection;
    //  private final Element focusable;
    //  private FocusListenerCollection focusListeners;
      private String imageBase = GWT.getModuleBaseURL();
      private KeyboardListenerCollection keyboardListeners;
      private TreeTableListenerCollection listeners;
      private MouseListenerCollection mouseListeners = null;
      private final TreeItem root;
      
      private TreeTableRenderer renderer;
      /**
       * Keeps track of the last event type seen. We do this to determine if we
       * have a duplicate key down.
       */
      private int lastEventType;
      /**
       * Needed local instance. GWT's is hidden in package scope. 
       */
    //  private FocusImpl impl = (FocusImpl) GWT.create(FocusImpl.class);
      public class Renderer {
        public void renderRow(TreeTable tree, TreeItem item, int row) {
        }
      }
      /**
       * Constructs an empty tree.
       */
      public TreeTable() {
        Element tableElem = getElement();
        headElem = DOM.createElement("thead");
        Element tr = DOM.createTR();
        DOM.appendChild(headElem, tr);
        DOM.insertChild(tableElem, headElem, 0);
        DOM.setStyleAttribute(getElement(), "position", "relative");
    //    focusable = impl.createFocusable();
    //    DOM.setStyleAttribute(focusable, "fontSize", "0");
    //    DOM.setStyleAttribute(focusable, "position", "absolute");
    //    DOM.setIntStyleAttribute(focusable, "zIndex", -1);
    //    DOM.appendChild(getElement(), focusable);
        sinkEvents(Event.MOUSEEVENTS | Event.ONCLICK | Event.KEYEVENTS);
    //    DOM.sinkEvents(focusable, Event.FOCUSEVENTS | Event.KEYEVENTS | DOM.getEventsSunk(focusable));
        // The 'root' item is invisible and serves only as a container
        // for all top-level items.
        root = new TreeItem() {
          public void addItem(TreeItem item) {
            // If this element already belongs to a tree or tree item,
            // remove it.
            if ((item.getParentItem() != null) || (item.getTreeTable() != null)) {
              item.remove();
            }
            item.setTreeTable(this.getTreeTable());
            // Explicitly set top-level items' parents to null.
            item.setParentItem(null);
            getChildren().add(item);
            // Use no margin on top-most items.
            DOM.setIntStyleAttribute(item.getElement(), "marginLeft", 0);
          }
          public void removeItem(TreeItem item) {
            if (!getChildren().contains(item)) {
              return;
            }
            // Update Item state.
            item.setTreeTable(null);
            item.setParentItem(null);
            getChildren().remove(item);
          }
        };
        root.setTreeTable(this);
        setStyleName("gwt-TreeTable");
      }
      /**
       * Adds the widget as a root tree item.
       * 
       * @see com.google.gwt.user.client.ui.HasWidgets#add(com.google.gwt.user.client.ui.Widget)
       * @param widget
       *            widget to add.
       */
      public void add(Widget widget) {
        addItem(widget);
      }
      
      /**
       * Adds a new tree item containing the specified widget.
       * 
       * @param widget
       *            the widget to be added
       */
      public TreeItem addItem(Widget widget) {
        TreeItem item = new TreeItem(widget);
        addItem(item);
        return item;
      }
      /**
       * Adds a simple tree item containing the specified text.
       * 
       * @param itemText
       *            the text of the item to be added
       * @return the item that was added
       */
      public TreeItem addItem(String itemText) {
        TreeItem ret = new TreeItem(itemText);
        addItem(ret);
        return ret;
      }
      
      public TreeItem addItem(Object userObj) {
        TreeItem ret = new TreeItem(userObj);
        addItem(ret);
        return ret;
      }
      /**
       * Adds an item to the root level of this tree.
       * 
       * @param item
       *            the item to be added
       */
      public void addItem(TreeItem item) {
        root.addItem(item);
        // Adds the item to the proper row
        insertItem(item, getRowCount());
        updateRowCache();
        updateVisibility(item);
      }
      /**
       * Updates table rows to include children.
       * 
       * @param item
       */
      void insertItem(TreeItem item, int r) {
        // inserts this item into the tree
        insertRow(r);
        setWidget(r, getTreeColumn(), item);
        item.setRow(r);
        render(item);
        Vector chlds = item.getChildren();
        for (int i = 0, s = chlds.size(); i < s; i++) {
          TreeItem chld = (TreeItem) chlds.get(i);
          insertItem(chld, r + 1);
        }
        
        TreeItem p = item.getParentItem();
        if (p != null) {
          if (!p.isOpen()) {
            setVisible(false, item.getRow());
            setChildrenVisible(item, false);
          }
        }
      }
      /**
       * Removes an item from the root level of this tree.
       * 
       * @param item
       *            the item to be removed
       */
      public void removeItem(TreeItem item) {
        root.removeItem(item);
        removeItemFromTable(item);
      }
      void removeItemFromTable(TreeItem item) {
        int r = item.getRow();
        int rs = item.getDescendentCount();
        for (int i = 0; i < rs; i++) {
          removeRow(r);
        }
        updateRowCache();
      }
      /**
       * Removes all items from the root level of this tree.
       */
      public void removeItems() {
        while (getItemCount() > 0) {
          removeItem(getItem(0));
        }
      }
      /**
       * Updates the cached row index for each tree item. TODO - Optomize with
       * start item.
       */
      void updateRowCache() {
        updateRowCache(root, -1);
      }
      int updateRowCache(TreeItem item, int r) {
        item.setRow(r);
        Vector chlds = item.getChildren();
        for (int i = 0, s = chlds.size(); i < s; i++) {
          TreeItem chld = (TreeItem) chlds.get(i);
          r++;
          r = updateRowCache(chld, r);
        }
        return r;
      }
      protected int getTreeColumn() {
        return 0;
      }
      public void addKeyboardListener(KeyboardListener listener) {
        if (keyboardListeners == null) {
          keyboardListeners = new KeyboardListenerCollection();
        }
        keyboardListeners.add(listener);
      }
      public void addMouseListener(MouseListener listener) {
        if (mouseListeners == null) {
          mouseListeners = new MouseListenerCollection();
        }
        mouseListeners.add(listener);
      }
      public void addTreeTableListener(TreeTableListener listener) {
        if (listeners == null) {
          listeners = new TreeTableListenerCollection();
        }
        listeners.add(listener);
      }
      /**
       * Clears all tree items from the current tree.
       */
      public void clear() {
        int size = root.getChildCount();
        for (int i = size - 1; i >= 0; i--) {
          root.getChild(i).remove();
        }
      }
      /**
       * Ensures that the currently-selected item is visible, opening its parents
       * and scrolling the tree as necessary.
       */
      public void ensureSelectedItemVisible() {
        if (curSelection == null) {
          return;
        }
        TreeItem parent = curSelection.getParentItem();
        while (parent != null) {
          parent.setState(true);
          parent = parent.getParentItem();
        }
      }
      /**
       * Gets this tree's default image package.
       * 
       * @return the tree's image package
       * @see #setImageBase
       */
      public String getImageBase() {
        return imageBase;
      }
      /**
       * Gets the top-level tree item at the specified index.
       * 
       * @param index
       *            the index to be retrieved
       * @return the item at that index
       */
      public TreeItem getItem(int index) {
        return root.getChild(index);
      }
      /**
       * Gets the number of items contained at the root of this tree.
       * 
       * @return this tree's item count
       */
      public int getItemCount() {
        return root.getChildCount();
      }
      /**
       * Gets the currently selected item.
       * 
       * @return the selected item
       */
      public TreeItem getSelectedItem() {
        return curSelection;
      }
      public void onBrowserEvent(Event event) {
        int eventType = DOM.eventGetType(event);
        switch (eventType) {
        case Event.ONCLICK: {
          Element e = DOM.eventGetTarget(event);
          if (shouldTreeDelegateFocusToElement(e)) {
            // The click event should have given focus to this element
            // already.
            // Avoid moving focus back up to the tree (so that focusable
            // widgets
            // attached to TreeItems can receive keyboard events).
          } else {
    //        setFocus(true);
          }
          break;
        }
        case Event.ONMOUSEDOWN: {
          if (mouseListeners != null) {
            mouseListeners.fireMouseEvent(this, event);
          }
          elementClicked(root, DOM.eventGetTarget(event));
          break;
        }
        case Event.ONMOUSEUP: {
          if (mouseListeners != null) {
            mouseListeners.fireMouseEvent(this, event);
          }
          break;
        }
        case Event.ONMOUSEMOVE: {
          if (mouseListeners != null) {
            mouseListeners.fireMouseEvent(this, event);
          }
          break;
        }
        case Event.ONMOUSEOVER: {
          if (mouseListeners != null) {
            mouseListeners.fireMouseEvent(this, event);
          }
          break;
        }
        case Event.ONMOUSEOUT: {
          if (mouseListeners != null) {
            mouseListeners.fireMouseEvent(this, event);
          }
          break;
        }
    //    case Event.ONFOCUS:
    //      // If we already have focus, ignore the focus event.
    //      if (focusListeners != null) {
    //        focusListeners.fireFocusEvent(this, event);
    //      }
    //      break;
    //
    //    case Event.ONBLUR: {
    //      if (focusListeners != null) {
    //        focusListeners.fireFocusEvent(this, event);
    //      }
    //
    //      break;
    //    }
        case Event.ONKEYDOWN:
          // If nothing's selected, select the first item.
          if (curSelection == null) {
            if (root.getChildCount() > 0) {
              onSelection(root.getChild(0), true);
            }
            super.onBrowserEvent(event);
            return;
          }
          if (lastEventType == Event.ONKEYDOWN) {
            // Two key downs in a row signal a duplicate event. Multiple key
            // downs can be triggered in the current configuration
            // independent
            // of the browser.
            return;
          }
          // Handle keyboard events
          switch (DOM.eventGetKeyCode(event)) {
          case KeyboardListener.KEY_UP: {
            moveSelectionUp(curSelection);
            DOM.eventPreventDefault(event);
            break;
          }
          case KeyboardListener.KEY_DOWN: {
            moveSelectionDown(curSelection, true);
            DOM.eventPreventDefault(event);
            break;
          }
          case KeyboardListener.KEY_LEFT: {
            if (curSelection.getState()) {
              curSelection.setState(false);
            }
            DOM.eventPreventDefault(event);
            break;
          }
          case KeyboardListener.KEY_RIGHT: {
            if (!curSelection.getState()) {
              curSelection.setState(true);
            }
            DOM.eventPreventDefault(event);
            break;
          }
          }
          // Intentional fallthrough.
        case Event.ONKEYUP:
          if (eventType == Event.ONKEYUP) {
            // If we got here because of a key tab, then we need to make
            // sure the
            // current tree item is selected.
            if (DOM.eventGetKeyCode(event) == KeyboardListener.KEY_TAB) {
              Vector chain = new Vector();
              collectElementChain(chain, getElement(), DOM.eventGetTarget(event));
              TreeItem item = findItemByChain(chain, 0, root);
              if (item != getSelectedItem()) {
                setSelectedItem(item, true);
              }
            }
          }
          // Intentional fallthrough.
        case Event.ONKEYPRESS: {
          if (keyboardListeners != null) {
            keyboardListeners.fireKeyboardEvent(this, event);
          }
          break;
        }
        }
        // We must call SynthesizedWidget's implementation for all other events.
        super.onBrowserEvent(event);
        lastEventType = eventType;
      }
      
      public void removeKeyboardListener(KeyboardListener listener) {
        if (keyboardListeners != null) {
          keyboardListeners.remove(listener);
        }
      }
      public void removeTreeTableListener(TreeTableListener listener) {
        if (listeners != null) {
          listeners.remove(listener);
        }
      }
      /**
       * Sets the base URL under which this tree will find its default images.
       * These images must be named "tree_white.gif", "tree_open.gif", and
       * "tree_closed.gif".
       */
      public void setImageBase(String baseUrl) {
        imageBase = baseUrl;
        root.updateStateRecursive();
      }
      /**
       * Selects a specified item.
       * 
       * @param item
       *            the item to be selected, or null to deselect
       *            all items
       */
      public void setSelectedItem(TreeItem item) {
        setSelectedItem(item, true);
      }
      /**
       * Selects a specified item.
       * 
       * @param item
       *            the item to be selected, or null to deselect
       *            all items
       * @param fireEvents
       *            true to allow selection events to be fired
       */
      public void setSelectedItem(TreeItem item, boolean fireEvents) {
        if (item == null) {
          if (curSelection == null) {
            return;
          }
          curSelection.setSelected(false);
          curSelection = null;
          return;
        }
        onSelection(item, fireEvents);
      }
      /**
       * Iterator of tree items.
       */
      public Iterator treeItemIterator() {
        List accum = new ArrayList();
        root.addTreeItems(accum);
        return accum.iterator();
      }
      protected void onLoad() {
        root.updateStateRecursive();
        
        renderTable();
        updateVisibility();
      }
      void fireStateChanged(TreeItem item) {
        if (listeners != null) {
          listeners.fireItemStateChanged(item);
        }
      }
      /**
       * Collects parents going up the element tree, terminated at the tree root.
       */
      private void collectElementChain(Vector chain, Element hRoot, Element hElem) {
        if ((hElem == null) || DOM.compare(hElem, hRoot)) {
          return;
        }
        collectElementChain(chain, hRoot, DOM.getParent(hElem));
        chain.add(hElem);
      }
      private boolean elementClicked(TreeItem root, Element hElem) {
        Vector chain = new Vector();
        collectElementChain(chain, getElement(), hElem);
        TreeItem item = findItemByChain(chain, 0, root);
        if (item != null) {
          if (DOM.compare(item.getImageElement(), hElem)) {
            item.setState(!item.getState(), true);
            return true;
          } else if (DOM.isOrHasChild(item.getElement(), hElem)) {
            onSelection(item, true);
            return true;
          }
        }
        return false;
      }
      private TreeItem findDeepestOpenChild(TreeItem item) {
        if (!item.getState()) {
          return item;
        }
        return findDeepestOpenChild(item.getChild(item.getChildCount() - 1));
      }
      private TreeItem findItemByChain(Vector chain, int idx, TreeItem root) {
        if (idx == chain.size()) {
          return root;
        }
        for (int i = 0, s = chain.size(); i < s; i++) {
          Element elem = (Element) chain.get(i);
          String n = getNodeName(elem);
          if ("div".equalsIgnoreCase(n)) {
            return findItemByElement(root, elem);
          }
        }
        return null;
      }
      private TreeItem findItemByElement(TreeItem item, Element elem) {
        if (DOM.compare(item.getElement(), elem)) {
          return item;
        }
        for (int i = 0, n = item.getChildCount(); i < n; ++i) {
          TreeItem child = item.getChild(i);
          child = findItemByElement(child, elem);
          if (child != null) {
            return child;
          }
        }
        return null;
      }
      private native String getNodeName(Element elem) /*-{
        return elem.nodeName;
      }-*/;
      /**
       * Moves to the next item, going into children as if dig is enabled.
       */
      private void moveSelectionDown(TreeItem sel, boolean dig) {
        if (sel == root) {
          return;
        }
        TreeItem parent = sel.getParentItem();
        if (parent == null) {
          parent = root;
        }
        int idx = parent.getChildIndex(sel);
        if (!dig || !sel.getState()) {
          if (idx < parent.getChildCount() - 1) {
            onSelection(parent.getChild(idx + 1), true);
          } else {
            moveSelectionDown(parent, false);
          }
        } else if (sel.getChildCount() > 0) {
          onSelection(sel.getChild(0), true);
        }
      }
      /**
       * Moves the selected item up one.
       */
      private void moveSelectionUp(TreeItem sel) {
        TreeItem parent = sel.getParentItem();
        if (parent == null) {
          parent = root;
        }
        int idx = parent.getChildIndex(sel);
        if (idx > 0) {
          TreeItem sibling = parent.getChild(idx - 1);
          onSelection(findDeepestOpenChild(sibling), true);
        } else {
          onSelection(parent, true);
        }
      }
      private void onSelection(TreeItem item, boolean fireEvents) {
        // 'root' isn't a real item, so don't let it be selected
        // (some cases in the keyboard handler will try to do this)
        if (item == root) {
          return;
        }
        if (curSelection != null) {
          curSelection.setSelected(false);
        }
        curSelection = item;
        if (curSelection != null) {
    //      moveFocus(curSelection);
          // Select the item and fire the selection event.
          curSelection.setSelected(true);
          if (fireEvents && (listeners != null)) {
            listeners.fireItemSelected(curSelection);
          }
        }
      }
      private native boolean shouldTreeDelegateFocusToElement(Element elem) /*-{
        var focus = 
          ((elem.nodeName == "SELECT") || 
          (elem.nodeName == "INPUT")  || 
          (elem.nodeName == "CHECKBOX")
        );
        return focus;
      }-*/;
      public void updateVisibility() {
        for (int i = 0, s = root.getChildCount(); i < s; i++) {
          TreeItem item = root.getChild(i);
          updateVisibility(item);
        }
      }
      protected void updateVisibility(TreeItem item) {
        if (item.isOpen()) {
          showChildren(item);
        } else {
          hideChildren(item);
        }
      }
      void setVisible(boolean visible, int row) {
        UIObject.setVisible(getRowFormatter().getElement(row), visible);
      }
      protected void setVisible(boolean visible, int row, int count) {
        for (int r = row, s = row + count; r < s; r++) {
          setVisible(visible, r);
        }
      }
      public void showChildren(TreeItem item) {
        for (int i = 0, s = item.getChildCount(); i < s; i++) {
          TreeItem child = item.getChild(i);
          setVisible(true, child.getRow());
          if (child.isOpen()) {
            showChildren(child);
          }
        }
      }
      public void hideChildren(TreeItem item) {
        setChildrenVisible(item, false);
      }
      
      public void setChildrenVisible(TreeItem item, boolean visible) {
        if (item.getChildCount() == 0) {
          return;
        }
        int row = item.getRow() + 1;
        int lastChildRow = getLastChildRow(item);
        int count = lastChildRow - row + 1;
        setVisible(visible, row, count);
      }
      protected TreeItem getNextSibling(TreeItem item) {
        TreeItem p = item.getParentItem();
        if (p == null) {
          int idx = root.getChildIndex(item) + 1;
          if (idx < root.getChildCount()) {
            // Gets the next sibling
            return root.getChild(idx);
          }
        } else {
          int idx = p.getChildIndex(item) + 1;
          if (idx < p.getChildCount()) {
            // Gets the next sibling
            return p.getChild(idx);
          }
        }
        return null;
      }
      protected TreeItem getNextNonChild(TreeItem item) {
        TreeItem next = getNextSibling(item);
        if (next != null) {
          return next;
        }
        TreeItem p = item.getParentItem();
        if (p != null) {
          return getNextNonChild(p);
        } else {
          return null;
        }
      }
      public int getLastChildRow(TreeItem item) {
        // Checks the row of the next sibling
        TreeItem next = getNextNonChild(item);
        if (next != null) {
          return next.getRow() - 1;
        }
        return getRowCount() - 1;
      }
      
      public void renderTable() {
        render(root);
      }
      
      /**
       * Renders TreeItems recursively. 
       * @param item
       */
      public void render(TreeItem item) {
        getRenderer().renderTreeItem(this, item, item.getRow());
        if (item.getParentItem() != null) {
          updateVisibility(item.getParentItem());
        }
        
        for (int i = 0, s = item.getChildCount(); i < s; i++) {
          TreeItem child = item.getChild(i);
          render(child);
        }
      }
      public TreeTableRenderer getRenderer() {
        if (renderer == null) {
          renderer = new DefaultRenderer();
        }
        return renderer;
      }
      public void setRenderer(TreeTableRenderer renderer) {
        this.renderer = renderer;
      }
      
    //  /**
    //   * Move the tree focus to the specified selected item.
    //   * 
    //   * @param selection
    //   */
    //  private void moveFocus(TreeItem selection) {
    //    HasFocus focusableWidget = selection.getFocusableWidget();
    //    if (focusableWidget != null) {
    //      focusableWidget.setFocus(true);
    //      DOM.scrollIntoView(((Widget) focusableWidget).getElement());
    //    } else {
    //      // Get the location and size of the given item's content element
    //      // relative
    //      // to the tree.
    //      Element selectedElem = selection.getContentElem();
    //      int containerLeft = getAbsoluteLeft();
    //      int containerTop = getAbsoluteTop();
    //
    //      int left = DOM.getAbsoluteLeft(selectedElem) - containerLeft;
    //      int top = DOM.getAbsoluteTop(selectedElem) - containerTop;
    //      int width = DOM.getIntAttribute(selectedElem, "offsetWidth");
    //      int height = DOM.getIntAttribute(selectedElem, "offsetHeight");
    //
    //      // Set the focusable element's position and size to exactly underlap
    //      // the
    //      // item's content element.
    //      DOM.setIntStyleAttribute(focusable, "left", left);
    //      DOM.setIntStyleAttribute(focusable, "top", top);
    //      DOM.setIntStyleAttribute(focusable, "width", width);
    //      DOM.setIntStyleAttribute(focusable, "height", height);
    //
    //      // Scroll it into view.
    //      DOM.scrollIntoView(focusable);
    //
    //      // Ensure Focus is set, as focus may have been previously delegated
    //      // by
    //      // tree.
    //      impl.focus(focusable);
    //    }
    //  }
      
    //  public int getTabIndex() {
    //    return impl.getTabIndex(focusable);
    //  }
    //  public void addFocusListener(FocusListener listener) {
    //    if (focusListeners == null) {
    //      focusListeners = new FocusListenerCollection();
    //    }
    //    focusListeners.add(listener);
    //  }
    //  public void removeFocusListener(FocusListener listener) {
    //    if (focusListeners != null) {
    //      focusListeners.remove(listener);
    //    }
    //  }
    //  public void setAccessKey(char key) {
    //    impl.setAccessKey(focusable, key);
    //  }
    //  public void setFocus(boolean focus) {
    //    if (focus) {
    //      impl.focus(focusable);
    //    } else {
    //      impl.blur(focusable);
    //    }
    //  }
    //  public void setTabIndex(int index) {
    //    impl.setTabIndex(focusable, index);
    //  }
      /**
       * Default renderer for TreeTable. Renders the user object into
       * the TreeItem. Widget user objects are preserved. Arrays are mapped
       * into the row with first object rendered into the TreeItem. All
       * other objects are rendered to the TreeItem with toString().
       */
      class DefaultRenderer implements TreeTableRenderer {
        public void renderTreeItem(TreeTable table, TreeItem item, int row) {
          Object obj = item.getUserObject();
          if (obj instanceof Widget) {
            item.setWidget((Widget) obj);
          } else if (obj instanceof Object[]) {
            Object [] objs = (Object []) obj;
            if (objs.length > 0) {
              Object o = objs[0];
              if (o instanceof Widget) {
                item.setWidget((Widget) o);
              } else if (o != null) {
                item.setHTML(o.toString());
              } else {
                item.setText(null);
              }
              for (int i = 1, s = objs.length; i < s; i++) {
                o = objs[i];
                if (o instanceof Widget) {
                  setWidget(row, i, (Widget) o);
                } else if (o != null) {
                  setHTML(row, i, o.toString());
                } else {
                  setHTML(row, i, null);
                }
              }
            }
          } else if (obj != null) {
            item.setHTML(obj.toString());
          }
        }
      }
      
      public void setWidget(int row, int column, Widget widget) {
        if (column != getTreeColumn()) {
          super.setWidget(row, column, widget);
        } else {
          if (widget instanceof TreeItem) {
            super.setWidget(row, column, widget);
          } else {
            throw new RuntimeException("Cannot add non-TreeItem to tree column");
          }
        }
      }
      
      public void setText(int row, int column, String text) {
        if (column != getTreeColumn()) {
          super.setText(row, column, text);
        } else {
          throw new RuntimeException("Cannot add non-TreeItem to tree column");
        }
      }
      
      public void setHTML(int row, int column, String text) {
        if (column != getTreeColumn()) {
          super.setHTML(row, column, text);
        } else {
          throw new RuntimeException("Cannot add non-TreeItem to tree column");
        }
      }
    }
               
             
        
      
    GWT-TreeTable.zip( 13 k)