com.extjs.gxt.ui.client.widget.tree.Tree.java Source code

Java tutorial

Introduction

Here is the source code for com.extjs.gxt.ui.client.widget.tree.Tree.java

Source

/*
 * Ext GWT - Ext for GWT
 * Copyright(c) 2007-2009, Ext JS, LLC.
 * licensing@extjs.com
 * 
 * http://extjs.com/license
 */
package com.extjs.gxt.ui.client.widget.tree;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.PartFactory;
import com.extjs.gxt.ui.client.PartProvider;
import com.extjs.gxt.ui.client.Style.SelectionMode;
import com.extjs.gxt.ui.client.aria.FocusFrame;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.ContainerEvent;
import com.extjs.gxt.ui.client.event.TreeEvent;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.widget.Container;
import com.extjs.gxt.ui.client.widget.menu.Menu;
import com.extjs.gxt.ui.client.widget.selection.Selectable;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanel;
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.Accessibility;

/**
 * A standard hierarchical tree widget. The tree contains a hierarchy of
 * <code>TreeItems</code> that the user can open, close, and select.
 * 
 * <p/>
 * The root item cannot be displayed.
 * 
 * <dt><b>Events:</b></dt>
 * 
 * <dd><b>BeforeAdd</b> : TreeEvent(item, child, index)<br>
 * <div>Fires before a item is added or inserted. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>item : this</li>
 * <li>child : the item being added</li>
 * <li>index : the index at which the item will be added</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeRemove</b> : TreeEvent(item, child)<br>
 * <div>Fires before a item is removed. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>item : this</li>
 * <li>child : the item being removed</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeExpand</b> : TreeEvent(item)<br>
 * <div>Fires before a item is expanded. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>item : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeCollapse</b> : TreeEvent(item)<br>
 * <div>Fires before a item is collapsed. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>item : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Add</b> : TreeEvent(item, child, index)<br>
 * <div>Fires after a item has been added or inserted.</div>
 * <ul>
 * <li>item : this</li>
 * <li>child : the item that was added</li>
 * <li>index : the index at which the item will be added</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Remove</b> : TreeEvent(tree, item, child)<br>
 * <div>Fires after a item has been removed.</div>
 * <ul>
 * <li>tree : this</li>
 * <li>item : item</li>
 * <li>child : the item being removed</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>BeforeSelect</b> : TreeEvent(tree, item)<br>
 * <div>Fires before a item is selected. Listeners can cancel the action by
 * calling {@link BaseEvent#setCancelled(boolean)}.</div>
 * <ul>
 * <li>tree : this</li>
 * <li>item : the selected item</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>SelectionChange</b> : TreeEvent(tree, selected)<br>
 * <div>Fires after the tree selection changes.</div>
 * <ul>
 * <li>tree : this</li>
 * <li>selected : the selected items</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Expand</b> : TreeEvent(tree, item)<br>
 * <div>Fires after a item has been expanded.</div>
 * <ul>
 * <li>item : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>Collapse</b> : TreeEvent(tree, item)<br>
 * <div>Fires after a item is collapsed.</div>
 * <ul>
 * <li>item : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>CheckChange</b> : TreeEvent(tree, item)<br>
 * <div>Fires after a check state change.</div>
 * <ul>
 * <li>item : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>ContextMenu</b> : TreeEvent(tree)<br>
 * <div>Fires before the tree's context menu is shown.</div>
 * <ul>
 * <li>component : this</li>
 * </ul>
 * </dd>
 * 
 * <dd><b>KeyPress</b> : TreeEvent(tree, event)<br>
 * <div>Fires when a key is pressed.</div>
 * <ul>
 * <li>event : dom event</li>
 * </ul>
 * </dd>
 * </dl>
 * 
 * <dl>
 * <dt>Inherited Events:</dt>
 * <dd>BoxComponent Move</dd>
 * <dd>BoxComponent Resize</dd>
 * <dd>Component Enable</dd>
 * <dd>Component Disable</dd>
 * <dd>Component BeforeHide</dd>
 * <dd>Component Hide</dd>
 * <dd>Component BeforeShow</dd>
 * <dd>Component Show</dd>
 * <dd>Component Attach</dd>
 * <dd>Component Detach</dd>
 * <dd>Component BeforeRender</dd>
 * <dd>Component Render</dd>
 * <dd>Component BrowserEvent</dd>
 * <dd>Component BeforeStateRestore</dd>
 * <dd>Component StateRestore</dd>
 * <dd>Component BeforeStateSave</dd>
 * <dd>Component SaveState</dd>
 * </dl>
 * 
 * <dl>
 * <dt><b>CSS:</b></dt>
 * <dd>.my-tree (the tree itself)</dd>
 * <dd>.my-tree-item-text span (the tree item text)</dd>
 * </dl>
 * 
 * @deprecated see {@link TreePanel}
 */
public class Tree extends Container<TreeItem> implements Selectable<TreeItem> {

    /**
     * Check cascade enum.
     */
    public enum CheckCascade {
        CHILDREN, NONE, PARENTS;
    }

    /**
     * Check nodes enum.
     */
    public enum CheckNodes {
        BOTH, LEAF, PARENT;
    }

    /**
     * Joint enum.
     */
    public enum Joint {
        NONE(0), COLLAPSED(1), EXPANDED(2);

        private int value;

        private Joint(int value) {
            this.value = value;
        }

        public int value() {
            return value;
        }
    }

    public static final String DEFAULT_TREE_ITEM_ID = "tree.item.default";
    public static final String FAST_TREE_ITEM_ID = "tree.item.fast";
    static {
        PartFactory.registerProvider(new PartProvider() {
            public Object createPart(String id) {
                if (id.equals(DEFAULT_TREE_ITEM_ID)) {
                    return new DefaultTreeItemUI();
                } else if (id.equals(FAST_TREE_ITEM_ID)) {
                    return new FastTreeItemUI();
                }
                return null;
            }
        });
    }

    protected boolean isViewer;
    protected TreeItem root;
    protected TreeSelectionModel sm;
    private boolean animate = true;
    private boolean checkable;
    private CheckNodes checkNodes = CheckNodes.BOTH;
    private CheckCascade checkStyle = CheckCascade.PARENTS;
    private int indentWidth = 18;
    private Map<String, TreeItem> nodeHash;
    private TreeStyle style = new TreeStyle();
    private String treeItemPartId = DEFAULT_TREE_ITEM_ID;
    private String itemSelector = ".x-tree-item";

    /**
     * Returns the item selector.
     * 
     * @return the item selector
     */
    public String getItemSelector() {
        return itemSelector;
    }

    /**
     * Sets the CSS selector used to retrieve tree items after bulk rendering
     * (defaults to '.x-tree-item').
     * 
     * @param itemSelector the item selector
     */
    public void setItemSelector(String itemSelector) {
        this.itemSelector = itemSelector;
    }

    /**
     * Creates a new single select tree.
     */
    public Tree() {
        attachChildren = false;
        baseStyle = "my-tree";
        focusable = true;
        createRootItem();
        root.root = true;
        nodeHash = new HashMap<String, TreeItem>();
        setSelectionModel(new TreeSelectionModel());
    }

    /**
     * Collapses all item's.
     */
    public void collapseAll() {
        boolean anim = animate;
        if (anim)
            animate = false;
        root.setExpanded(false, true);
        if (anim)
            animate = true;
    }

    /**
     * Expands all item's.
     */
    public void expandAll() {
        boolean anim = animate;
        if (anim)
            animate = false;
        root.setExpanded(true, true);
        if (anim)
            animate = true;
    }

    /**
     * Expands a specified path. A path can be retrieved from a tree item with
     * {@link TreeItem#getPath()}.
     * 
     * @param path the path to expand
     * @return <code>true</code> if all paths expanded
     */
    public boolean expandPath(String path) {
        if (path == null)
            return false;
        String[] ids = path.split(",");
        if (ids.length == 0)
            return false;
        if (ids[0].equals(root.getId())) {
            root.setExpanded(true);

            TreeItem current = root;
            for (int i = 1; i < ids.length; i++) {
                String id = ids[i];
                boolean match = false;
                for (int j = 0; j < current.getItemCount(); j++) {
                    TreeItem child = current.getItem(j);
                    if (!match && child.getId().equals(id)) {
                        child.setExpanded(true);
                        current = child;
                        match = true;
                        break;
                    }
                }
                if (!match) {
                    return false;
                }
            }

        }
        return true;
    }

    /**
     * Returns the tree whose element or child elements match the passed target.
     * 
     * @param element the target element
     * @return the matching tree item or <code>null</code> if no match
     */
    public TreeItem findItem(Element element) {
        Element elem = fly(element).findParentElement(itemSelector, 15);
        if (elem != null) {
            String id = elem.getId();
            if (id != null && !id.equals("")) {
                TreeItem item = getItemById(id);
                return item;
            }
        }
        return null;
    }

    /**
     * Returns the total number of items contained in the tree excluding the root
     * item.
     * 
     * @return the total item count
     */
    public int getAllItemCount() {
        return nodeHash.size();
    }

    /**
     * Returns all tree item's contained by the tree.
     * 
     * @return all tree item's
     */
    public List<TreeItem> getAllItems() {
        List<TreeItem> temp = new ArrayList<TreeItem>();
        temp.add(root);
        temp.addAll(nodeHash.values());
        return temp;
    }

    /**
     * Returns true if animations are enabled.
     * 
     * @return the animate state
     */
    public boolean getAnimate() {
        return animate;
    }

    /**
     * Returns true if check boxs are enabled.
     * 
     * @return the checkbox state
     */
    public boolean getCheckable() {
        return checkable;
    }

    /**
     * Returns a list of id's for all checked items.
     * 
     * @return the list of checked id's
     */
    public List<TreeItem> getChecked() {
        List<TreeItem> list = new ArrayList<TreeItem>();
        Iterator<TreeItem> it = nodeHash.values().iterator();
        while (it.hasNext()) {
            TreeItem item = it.next();
            if (item.isChecked()) {
                list.add(item);
            }
        }
        return list;
    }

    /**
     * Returns the child nodes value.
     * 
     * @return the child nodes value
     */
    public CheckNodes getCheckNodes() {
        return checkNodes;
    }

    /**
     * The check style value.
     * 
     * @return the check style
     */
    public CheckCascade getCheckStyle() {
        return checkStyle;
    }

    @Override
    public Menu getContextMenu() {
        // made public
        return super.getContextMenu();
    }

    /**
     * Returns the indent width.
     * 
     * @return the indent width
     */
    public int getIndentWidth() {
        return indentWidth;
    }

    @Override
    public TreeItem getItem(int index) {
        return getRootItem().getItem(index);
    }

    /**
     * Returns the item by id.
     * 
     * @param id the id of the element to return
     * @return the item
     */
    public TreeItem getItemById(String id) {
        return nodeHash.get(id);
    }

    /**
     * Returns the item icon style.
     * 
     * @return the icon style
     * @deprecated see {@link TreeStyle#getLeafIconStyle()}
     */
    public String getItemIconStyle() {
        return style.getLeafIconStyle();
    }

    /**
     * Returns the node icon style.
     * 
     * @return the icon style
     * @deprecated see {@link TreeStyle#getNodeCloseIconStyle()}
     */
    public String getNodeIconStyle() {
        return style.getNodeCloseIconStyle();
    }

    /**
     * Returns the open node icon style.
     * 
     * @return the icon style
     * @deprecated see {@link TreeStyle#getNodeOpenIconStyle()}
     */
    public String getOpenNodeIconStyle() {
        return style.getNodeOpenIconStyle();
    }

    /**
     * Returns the tree's root item. The root item cannot be displayed.
     * 
     * @return the root item
     */
    public TreeItem getRootItem() {
        return root;
    }

    /**
     * Returns the selected item.
     * 
     * @return the item
     */
    public TreeItem getSelectedItem() {
        return (TreeItem) sm.getSelectedItem();
    }

    /**
     * Returns the selected items.
     * 
     * @return the selected items
     */
    public List<TreeItem> getSelectedItems() {
        return sm.getSelectedItems();
    }

    public SelectionMode getSelectionMode() {
        return sm.getSelectionMode();
    }

    /**
     * Returns the tree's selection model.
     * 
     * @return the selection model
     */
    public TreeSelectionModel getSelectionModel() {
        return sm;
    }

    /**
     * Returns the tree's style.
     * 
     * @return the tree style
     */
    public TreeStyle getStyle() {
        return style;
    }

    /**
     * Returns the tree item part id.
     * 
     * @return the part id
     */
    public String getTreeItemPartId() {
        return treeItemPartId;
    }

    @Override
    public void onComponentEvent(ComponentEvent ce) {
        super.onComponentEvent(ce);
        TreeEvent te = (TreeEvent) ce;
        if (te.getItem() != null) {
            te.getItem().onComponentEvent(te);
        }

        int type = ce.getEventTypeInt();
        switch (type) {
        case Event.ONFOCUS:
            onFocus(ce);
            break;
        }
    }

    protected void onFocus(ComponentEvent ce) {
        FocusFrame.get().frame(this);
    }

    public void onSelectChange(TreeItem item, boolean select) {
        item.getUI().onSelectedChange(select);
    }

    @Override
    public boolean removeAll() {
        getRootItem().removeAll();
        nodeHash.clear();
        return true;
    }

    /**
     * Sets whether expand /collapse should be animated (defaults to true).
     * 
     * @param animate the animate state
     */
    public void setAnimate(boolean animate) {
        this.animate = animate;
    }

    /**
     * Sets whether checkboxes are used in the tree.
     * 
     * @param checkable true for checkboxes
     */
    public void setCheckable(boolean checkable) {
        this.checkable = checkable;
    }

    /**
     * Sets which tree items will display a check box (defaults to BOTH).
     * <p>
     * Valid values are:
     * <ul>
     * <li>BOTH - both nodes and leafs</li>
     * <li>PARENT - only nodes with children</li>
     * <li>LEAF - only leafs</li>
     * </ul>
     * 
     * @param checkNodes the child nodes value
     */
    public void setCheckNodes(CheckNodes checkNodes) {
        this.checkNodes = checkNodes;
    }

    /**
     * Sets the cascading behavior for check tree (defaults to PARENTS).
     * <p>
     * Valid values are:
     * <ul>
     * <li>NONE - no cascading</li>
     * <li>PARENTS - cascade to parents</li>
     * <li>CHILDREN - cascade to children</li>
     * </ul>
     * 
     * @param checkStyle the child style
     */
    public void setCheckStyle(CheckCascade checkStyle) {
        this.checkStyle = checkStyle;
    }

    @Override
    public void setContextMenu(Menu menu) {
        super.setContextMenu(menu);
    }

    /**
     * Sets the number of pixels child items are indented. Default value is 18.
     * 
     * @param indentWidth the indent width
     */
    public void setIndentWidth(int indentWidth) {
        this.indentWidth = indentWidth;
    }

    /**
     * Sets the global icon style for leaf tree items. Individual tree items can
     * override this value by setting the the item's icon style.
     * 
     * @param itemImageStyle the image style
     * @deprecated see {@link TreeStyle#setLeafIconStyle(String)}
     */
    public void setItemIconStyle(String itemImageStyle) {
        style.setLeafIconStyle(itemImageStyle);
    }

    /**
     * The global icon style for tree items with children (defaults to
     * 'tree-folder'). Individual tree items can override this value by setting
     * the the item's icon style.
     * 
     * @param nodeIconStyle the node icon style
     * @deprecated see {@link TreeStyle#setNodeCloseIconStyle(String)}
     */
    public void setNodeIconStyle(String nodeIconStyle) {
        style.setNodeCloseIconStyle(nodeIconStyle);
    }

    /**
     * Sets the global icon style for expanded tree items (defaults to
     * 'tree-folder-open'). Individual tree items can override this value by
     * setting the the item's icon style.
     * 
     * @param openNodeIconStyle the open node icon style
     * @deprecated see {@link TreeStyle#setNodeOpenIconStyle(String)}
     */
    public void setOpenNodeIconStyle(String openNodeIconStyle) {
        style.setNodeOpenIconStyle(openNodeIconStyle);
    }

    public void setSelectedItem(TreeItem item) {
        sm.select(item, false);
    }

    public void setSelectedItems(List<TreeItem> items) {
        sm.select(items, false);
    }

    /**
     * Sets the table's selection mode.
     * 
     * @param mode the selection mode
     */
    public void setSelectionMode(SelectionMode mode) {
        setSelectionModel(new TreeSelectionModel(mode));
    }

    /**
     * Sets the tree's selection model.
     * 
     * @param sm the tree selection model
     */
    public void setSelectionModel(TreeSelectionModel sm) {
        assert sm != null;
        if (this.sm != null) {
            this.sm.bind(null);
        }
        this.sm = sm;
        sm.bind(this);
    }

    /**
     * Sets the part id used to obtain new tree item ui instances (defaults to
     * {@value #DEFAULT_TREE_ITEM_ID}.
     * 
     * @param treeItemPartId the tree item part id
     */
    public void setTreeItemPartId(String treeItemPartId) {
        this.treeItemPartId = treeItemPartId;
    }

    @Override
    protected ComponentEvent createComponentEvent(Event event) {
        return new TreeEvent(this, event == null ? null : findItem(DOM.eventGetTarget(event)));
    }

    @Override
    @SuppressWarnings("unchecked")
    protected ContainerEvent createContainerEvent(TreeItem item) {
        return new TreeEvent(this, item);
    }

    protected void createRootItem() {
        root = new RootTreeItem(this);
        root.tree = this;
    }

    @Override
    protected void onRender(Element target, int index) {
        setElement(DOM.createDiv(), target, index);
        super.onRender(target, index);

        root.render(getElement());

        if (!root.childrenRendered) {
            root.renderChildren();
        }

        addStyleName("x-ftree-no-lines x-ftree-arrows");

        disableTextSelection(true);

        if (GXT.isAriaEnabled()) {
            new KeyNav<ComponentEvent>(this) {
                @Override
                public void onDown(ComponentEvent ce) {
                    if (getSelectedItems().size() == 0 && getRootItem().getItemCount() > 0) {
                        setSelectedItem(getRootItem().getItem(0));
                    }
                }
            };
            setAnimate(false);
        }

        el().setTabIndex(0);
        el().setElementAttribute("hideFocus", "true");

        Accessibility.setRole(getElement(), Accessibility.ROLE_TREE);

        sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.KEYEVENTS | Event.MOUSEEVENTS | Event.FOCUSEVENTS);
    }

    void registerItem(TreeItem item) {
        nodeHash.put(item.getId(), item);
    }

    void unregisterItem(TreeItem item) {
        int count = item.getItemCount();
        for (int i = 0; i < count; i++) {
            unregisterItem(item.getItem(i));
        }
        nodeHash.remove(item.getId());
    }
}