Mega Code Archive

 
Categories / Java / GWT
 

A Tree Table

/*  * 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 <b>HTML</b>");     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:  * <li>Removed the DOM hierarchy of tree nodes. Each node is  * independent and therefore placable is a table cell.</li>  * <li>Changed subclass to Widget so the TreeItem could be added to a table.</li>   * <li>Changed parent Tree to TreeTable.</li>  * <li>Worked around package scope methods from the GWT ui package.</li>  * <li>Removed ContentPanel.</li>  * <li>Added row index cache.</li>  * </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:     // <div (handle)>     // <table (itemElem)>     // <tr>     // <td><img (imgElem)/></td>     // <td><span (contents)/></td>     // </tr>     // </table>     // </div>     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 <code>-1</code> 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 <code>true</code> 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 <code>Widget</code> associated with this tree item.    *     * @return the widget    */   public Widget getWidget() {     return widget;   }   /**    * Determines whether this item is currently selected.    *     * @return <code>true</code> 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    *            <code>true</code> to select the item, <code>false</code>    *            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    *            <code>true</code> 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:   * <ul>  * <li>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.</li>  * <li>Made TreeItem a Widget to be added to a table cell. Removed ContentPanel  * handling from the Tree code.   * <li>Disabled some Widget add/remove code. This may cause strange bugs. Again,   * package scope issues. This needs a work around.</li>  * <li>Streamlined findItemByChain() and modified elementClicked() to search the  * table. This should probably be rewritten to leverage FlexTable.   * </ul>  *   * Notes:  * <ul>  * <li>If anyone has a firm understanding of "focus" in GWT I could use the help  * cleaning this up.</li>  * </ul>  *   * @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 <code>null</code> to deselect    *            all items    */   public void setSelectedItem(TreeItem item) {     setSelectedItem(item, true);   }   /**    * Selects a specified item.    *     * @param item    *            the item to be selected, or <code>null</code> to deselect    *            all items    * @param fireEvents    *            <code>true</code> 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)