Java/GWT/Tree Table
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:*
* </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. * * @returntrue
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 theWidget
associated with this tree item. * * @return the widget */ public Widget getWidget() { return widget; } /** * Determines whether this item is currently selected. * * @returntrue
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, ornull
to deselect * all items */ public void setSelectedItem(TreeItem item) { setSelectedItem(item, true); } /** * Selects a specified item. * * @param item * the item to be selected, ornull
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>