Java/GWT/Tree Table

Материал из Java эксперт
Перейти к: навигация, поиск

A Tree Table

   <source lang="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.jexp.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.
  • * </ul>
    * 
    * @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:
    
    //
    // // // // // //
    <img (imgElem)/><span (contents)/>
    //
       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.rupare(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.rupare(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.rupare(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");
       }
     }
    

    }


          </source>