All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.extjs.gxt.ui.client.widget.treegrid.TreeGrid Maven / Gradle / Ivy

There is a newer version: 2.3.1-gwt22
Show newest version
/*
 * Sencha GXT 2.3.0 - Sencha for GWT
 * Copyright(c) 2007-2013, Sencha, Inc.
 * [email protected]
 * 
 * http://www.sencha.com/products/gxt/license/
 */
 package com.extjs.gxt.ui.client.widget.treegrid;

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

import com.extjs.gxt.ui.client.GXT;
import com.extjs.gxt.ui.client.Style.SortDir;
import com.extjs.gxt.ui.client.core.El;
import com.extjs.gxt.ui.client.core.FastMap;
import com.extjs.gxt.ui.client.core.XDOM;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelIconProvider;
import com.extjs.gxt.ui.client.data.TreeLoader;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.GridEvent;
import com.extjs.gxt.ui.client.event.TreeGridEvent;
import com.extjs.gxt.ui.client.store.ListStore;
import com.extjs.gxt.ui.client.store.Record;
import com.extjs.gxt.ui.client.store.Store;
import com.extjs.gxt.ui.client.store.StoreEvent;
import com.extjs.gxt.ui.client.store.StoreListener;
import com.extjs.gxt.ui.client.store.TreeStore;
import com.extjs.gxt.ui.client.store.TreeStoreEvent;
import com.extjs.gxt.ui.client.widget.grid.ColumnModel;
import com.extjs.gxt.ui.client.widget.grid.Grid;
import com.extjs.gxt.ui.client.widget.grid.GridView;
import com.extjs.gxt.ui.client.widget.treepanel.TreeStyle;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanel.Joint;
import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Accessibility;

/**
 * A hierarchical tree grid bound to a TreeStore.
 * 
 * 

* A TreeGridCellRenderer can be assigned to the * ColumnConfig in which the tree will be displayed. * *

* With state enabled, TreePanel will save and restore the expand state of the * nodes in the tree. A ModelKeyProvider must specified with the * TreeStore this tree is bound to. Save and restore works with * both local, and asynchronous loading of children. * * @param the model type */ @SuppressWarnings("deprecation") public class TreeGrid extends Grid { public class TreeNode { protected String id; protected Element joint, icon; protected M m; private boolean childrenRendered; private boolean expand; private boolean expandDeep; private boolean expanded; private boolean leaf = true; private boolean loaded; private boolean loading; public TreeNode(String id, M m) { this.id = id; this.m = m; } public void clearElements() { joint = null; icon = null; } public int getItemCount() { return treeStore.getChildCount(m); } public M getModel() { return m; } public TreeNode getParent() { M p = treeStore.getParent(m); return findNode(p); } public int indexOf(TreeNode child) { M c = child.getModel(); return store.indexOf(c); } public boolean isExpanded() { return expanded; } public boolean isLeaf() { return !hasChildren(m); } public void setExpanded(boolean expand) { TreeGrid.this.setExpanded(m, expand); } public void setLeaf(boolean leaf) { this.leaf = leaf; TreeGrid.this.refresh(m); } } protected Map cache; protected boolean filtering; protected TreeLoader loader; protected Map nodes = new FastMap(); protected TreeGridView treeGridView; protected TreeStore treeStore; private boolean autoLoad, autoExpand; private boolean caching = true; private boolean columnLines; private boolean expandOnFilter = true; private ModelIconProvider iconProvider; private ListStore listStore = new ListStore() { @Override public boolean equals(M model1, M model2) { return treeStore.equals(model1, model2); } @Override public Record getRecord(M model) { return treeStore.getRecord(model); } @Override public boolean hasRecord(M model) { return treeStore.hasRecord(model); } }; private StoreListener storeListener = new StoreListener() { @Override public void storeAdd(StoreEvent se) { onAdd((TreeStoreEvent) se); } @Override public void storeClear(StoreEvent se) { onDataChanged((TreeStoreEvent) se); } @Override public void storeDataChanged(StoreEvent se) { onDataChanged((TreeStoreEvent) se); } @Override public void storeFilter(StoreEvent se) { onFilter((TreeStoreEvent) se); } @Override public void storeRemove(StoreEvent se) { onRemove((TreeStoreEvent) se); } @Override public void storeUpdate(StoreEvent se) { onUpdate((TreeStoreEvent) se); } }; private TreeStyle style = new TreeStyle(); private Boolean useKeyProvider = null; @SuppressWarnings({"unchecked", "rawtypes"}) public TreeGrid(TreeStore store, ColumnModel cm) { this.store = listStore; this.cm = cm; focusable = true; baseStyle = "x-grid-panel"; this.treeStore = store; this.loader = treeStore.getLoader(); addStyleName("x-treegrid"); disabledStyle = null; treeStore.addStoreListener(storeListener); setView(new TreeGridView()); disableTextSelection(true); setSelectionModel(new TreeGridSelectionModel()); } /** * Collapses all nodes. */ public void collapseAll() { for (M child : treeStore.getRootItems()) { setExpanded(child, false, true); } } /** * Expands all nodes. */ public void expandAll() { for (M child : treeStore.getRootItems()) { setExpanded(child, true, true); } } /** * Returns the tree node for the given target. * * @param target the target element * @return the tree node or null if no match */ public TreeNode findNode(Element target) { Element row = (Element) getView().findRow(target); if (row != null) { El item = fly(row).selectNode(".x-tree3-node"); if (item != null) { String id = item.getId(); TreeNode node = nodes.get(id); return node; } } return null; } /** * Returns the model icon provider. * * @return the icon provider */ public ModelIconProvider getIconProvider() { return iconProvider; } /** * Returns the tree style. * * @return the tree style */ public TreeStyle getStyle() { return style; } /** * Returns the tree's tree store. * * @return the tree store */ public TreeStore getTreeStore() { return treeStore; } /** * Returns the tree's view. * * @return the view */ public TreeGridView getTreeView() { return treeGridView; } /** * Returns true if auto expand is enabled. * * @return the auto expand state */ public boolean isAutoExpand() { return autoExpand; } /** * Returns true if auto load is enabled. * * @return the auto load state */ public boolean isAutoLoad() { return autoLoad; } /** * Returns true when a loader is queried for it's children each time a node is * expanded. Only applies when using a loader with the tree store. * * @return true if caching */ public boolean isCaching() { return caching; } /** * Returns true if column lines are enabled. * * @return true if column lines are enabled */ public boolean isColumnLines() { return columnLines; } /** * Returns true if the model is expanded. * * @param model the model * @return true if expanded */ public boolean isExpanded(M model) { TreeNode node = findNode(model); return node.isExpanded(); } /** * Returns the if expand all and collapse all is enabled on filter changes. * * @return the expand all collapse all state */ public boolean isExpandOnFilter() { return expandOnFilter; } /** * Returns true if the model is a leaf node. The leaf state allows a tree item * to specify if it has children before the children have been realized. * * @param model the model * @return the leaf state */ public boolean isLeaf(M model) { TreeNode node = findNode(model); return node != null && node.isLeaf(); } @Override public void reconfigure(ListStore store, ColumnModel cm) { throw new UnsupportedOperationException("Please call the other reconfigure method"); } public void reconfigure(TreeStore store, ColumnModel cm) { if (isLoadMask() && rendered) { mask(GXT.MESSAGES.loadMask_msg()); } if (rendered) { this.store.removeAll(); if (cache != null) { cache.clear(); } nodes.clear(); treeGridView.initData(this.store, cm); treeGridView.treeStore = store; } if (treeStore != null) { treeStore.removeStoreListener(storeListener); } loader = null; treeStore = store; if (treeStore != null) { loader = treeStore.getLoader(); treeStore.addStoreListener(storeListener); } this.cm = cm; // rebind the sm setSelectionModel(sm); if (isViewReady()) { view.refresh(true); doInitialLoad(); } if (isLoadMask() && rendered) { unmask(); } fireEvent(Events.Reconfigure); } /** * If set to true, all non leaf nodes will be expanded automatically (defaults * to false). * * @param autoExpand the auto expand state to set. */ public void setAutoExpand(boolean autoExpand) { this.autoExpand = autoExpand; } /** * Sets whether all children should automatically be loaded recursively * (defaults to false). Useful when the tree must be fully populated when * initially rendered. * * @param autoLoad true to auto load */ public void setAutoLoad(boolean autoLoad) { this.autoLoad = autoLoad; } /** * Sets whether the children should be cached after first being retrieved from * the store (defaults to true). When false, a load request will * be made each time a node is expanded. * * @param caching the caching state */ public void setCaching(boolean caching) { this.caching = caching; } /** * True to enable column separation lines (defaults to false). * * @param columnLines true to enable column separation lines */ public void setColumnLines(boolean columnLines) { this.columnLines = columnLines; } /** * Sets the item's expand state. * * @param model the model * @param expand true to expand */ public void setExpanded(M model, boolean expand) { setExpanded(model, expand, false); } /** * Sets the item's expand state. * * @param model the model * @param expand true to expand * @param deep true to expand all children recursively */ @SuppressWarnings({"unchecked", "rawtypes"}) public void setExpanded(M model, boolean expand, boolean deep) { TreeNode node = findNode(model); if (node != null) { if (expand) { // make parents visible List list = new ArrayList(); M p = model; while ((p = treeStore.getParent(p)) != null) { if (!findNode(p).isExpanded()) { list.add(p); } } for (int i = list.size() - 1; i >= 0; i--) { M item = list.get(i); setExpanded(item, expand, false); } } TreeGridEvent tge = new TreeGridEvent(this); tge.setModel(model); if (expand) { if (!node.isLeaf()) { // if we are loading, ignore it if (node.loading) { return; } // if we have a loader and node is not loaded make // load request and exit method if (!node.expanded && loader != null && (!node.loaded || !caching) && !filtering) { treeStore.removeAll(model); node.expand = true; node.expandDeep = deep; node.loading = true; treeGridView.onLoading(node); loader.loadChildren(model); return; } if (!node.expanded && fireEvent(Events.BeforeExpand, tge)) { node.expanded = true; if (!node.childrenRendered) { renderChildren(model, false); node.childrenRendered = true; } // expand treeGridView.expand(node); if (isStateful() && treeStore.getKeyProvider() != null) { Map state = getState(); List expanded = (List) state.get("expanded"); if (expanded == null) { expanded = new ArrayList(); state.put("expanded", expanded); } String id = treeStore.getKeyProvider().getKey(model); if (!expanded.contains(id)) { expanded.add(id); saveState(); } } fireEvent(Events.Expand, tge); } if (deep) { setExpandChildren(model, true); } else { statefulExpand(treeStore.getChildren(model)); } } } else { if (node.expanded && fireEvent(Events.BeforeCollapse, tge)) { node.expanded = false; // collapse treeGridView.collapse(node); if (isStateful() && treeStore.getKeyProvider() != null) { Map state = getState(); List expanded = (List) state.get("expanded"); String id = treeStore.getKeyProvider().getKey(model); if (expanded != null && expanded.contains(id)) { expanded.remove(id); saveState(); } } fireEvent(Events.Collapse, tge); } if (deep) { setExpandChildren(model, false); } } } } /** * Sets whether the tree should expand all and collapse all when filters are * applied (defaults to true). * * @param expandOnFilter true to expand and collapse on filter changes */ public void setExpandOnFilter(boolean expandOnFilter) { this.expandOnFilter = expandOnFilter; } /** * Sets the tree's model icon provider which provides the icon style for each * model. * * @param iconProvider the icon provider */ public void setIconProvider(ModelIconProvider iconProvider) { this.iconProvider = iconProvider; } /** * Sets the item's leaf state. The leaf state allows control of the expand * icon before the children have been realized. * * @param model the model * @param leaf the leaf state */ public void setLeaf(M model, boolean leaf) { TreeNode t = findNode(model); if (t != null) { t.setLeaf(leaf); } } @Override public void setView(GridView view) { assert view instanceof TreeGridView : "The view for a TreeGrid has to be an instance of TreeGridView"; super.setView(view); treeGridView = (TreeGridView) view; } /** * Toggles the model's expand state. * * @param model the model */ public void toggle(M model) { TreeNode node = findNode(model); if (node != null) { setExpanded(model, !node.expanded); } } protected Joint calcualteJoint(M model) { if (model == null) { return Joint.NONE; } TreeNode node = findNode(model); Joint joint = Joint.NONE; if (node == null) { return joint; } if (!node.isLeaf()) { boolean children = true; if (node.isExpanded()) { joint = children ? Joint.EXPANDED : Joint.NONE; } else { joint = children ? Joint.COLLAPSED : Joint.NONE; } } return joint; } protected AbstractImagePrototype calculateIconStyle(M model) { AbstractImagePrototype style = null; if (iconProvider != null) { AbstractImagePrototype iconStyle = iconProvider.getIcon((M) model); if (iconStyle != null) { return iconStyle; } } TreeNode node = findNode(model); TreeStyle ts = getStyle(); if (!node.isLeaf()) { if (isExpanded(model)) { style = ts.getNodeOpenIcon(); } else { style = ts.getNodeCloseIcon(); } } else { style = ts.getLeafIcon(); } return style; } @SuppressWarnings({"unchecked", "rawtypes"}) @Override protected ComponentEvent createComponentEvent(Event event) { return new TreeGridEvent(this, event); } @Override protected void doApplyStoreState(Map state) { String sortField = (String) state.get("sortField"); if (treeStore.getLoader() == null && sortField != null) { String sortDir = (String) state.get("sortDir"); SortDir dir = SortDir.findDir(sortDir); treeStore.sort(sortField, dir); } } protected int findLastOpenChildIndex(M model) { TreeNode mark = findNode(model); M lc = model; while (mark != null && mark.expanded) { M m = treeStore.getLastChild(mark.m); if (m != null) { lc = m; mark = findNode(lc); } else { break; } } return store.indexOf(lc); } protected TreeNode findNode(M m) { if (m == null || useKeyProvider == null) return null; return nodes.get(useKeyProvider ? generateModelId(m) : cache.get(m)); } protected String generateModelId(M m) { return getId() + "_" + (treeStore.getKeyProvider() != null ? treeStore.getKeyProvider().getKey(m) : XDOM.getUniqueId()); } protected boolean hasChildren(M m) { TreeNode node = findNode(m); if (loader != null && !node.loaded) { return loader.hasChildren(node.getModel()); } if (!node.leaf || treeStore.hasChildren(node.getModel())) { return true; } return false; } protected void onAdd(TreeStoreEvent se) { if (viewReady) { M p = se.getParent(); if (p == null) { for (M child : se.getChildren()) { register(child); } if (se.getIndex() > 0) { M prev = treeStore.getChild(se.getIndex() - 1); int index = findLastOpenChildIndex(prev); store.insert(se.getChildren(), index + 1); } else { store.insert(se.getChildren(), se.getIndex()); } } else { TreeNode node = findNode(p); if (node != null) { for (M child : se.getChildren()) { register(child); } if (!node.expanded) { refresh(p); return; } int index = se.getIndex(); if (index == 0) { int pindex = store.indexOf(p); store.insert(se.getChildren(), pindex + 1); } else { index = store.indexOf(treeStore.getChild(p, index - 1)); TreeNode mark = findNode(store.getAt(index)); index = findLastOpenChildIndex(mark.m); store.insert(se.getChildren(), index + 1); } refresh(p); } } } } @Override protected void onAfterRenderView() { super.onAfterRenderView(); doInitialLoad(); } @Override protected void onClick(GridEvent e) { M m = e.getModel(); if (m != null) { TreeNode node = findNode(m); if (node != null) { Element jointEl = treeGridView.getJointElement(node); if (jointEl != null && e.within(jointEl)) { toggle(m); } else { super.onClick(e); } } } } protected void onDataChanged(TreeStoreEvent se) { if (!isRendered() || !viewReady) { return; } M p = se.getParent(); if (p == null) { store.removeAll(); if (cache != null) { cache.clear(); } nodes.clear(); renderChildren(null, autoLoad); statefulExpand(treeStore.getRootItems()); } else { TreeNode n = findNode(p); n.loaded = true; n.loading = false; renderChildren(p, autoLoad); if (n.expand && !n.isLeaf()) { n.expand = false; boolean deep = n.expandDeep; n.expandDeep = false; boolean c = caching; caching = true; setExpanded(p, true, deep); caching = c; } else { refresh(p); } } } @Override protected void onDoubleClick(GridEvent e) { super.onDoubleClick(e); toggle(e.getModel()); } protected void onFilter(TreeStoreEvent se) { filtering = treeStore.isFiltered(); onDataChanged(se); if (expandOnFilter && treeStore.isFiltered()) { expandAll(); } } protected void onRemove(TreeStoreEvent se) { if (viewReady) { unregister(se.getChild()); store.remove(se.getChild()); for (M child : se.getChildren()) { unregister(child); store.remove(child); } TreeNode p = findNode(se.getParent()); if (p != null && p.expanded && p.getItemCount() == 0) { setExpanded(p.m, false); } else if (p != null && p.getItemCount() == 0) { refresh(se.getParent()); } } } @Override protected void onRender(Element target, int index) { super.onRender(target, index); el().setTabIndex(0); el().setElementAttribute("hideFocus", "true"); if (columnLines) { addStyleName("x-grid-with-col-lines"); } Accessibility.setRole(getElement(), "treegrid"); sinkEvents(Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.KEYEVENTS); } protected void onUpdate(TreeStoreEvent se) { store.update(se.getModel()); store.fireEvent(Store.Update, se); } protected void refresh(M model) { TreeNode node = findNode(model); if (rendered && viewReady && node != null) { AbstractImagePrototype style = calculateIconStyle(model); treeGridView.onIconStyleChange(node, style); Joint j = calcualteJoint(model); treeGridView.onJointChange(node, j); } } protected String register(M m) { if (useKeyProvider == null) { if (treeStore.getKeyProvider() == null) { useKeyProvider = false; } else { useKeyProvider = true; } } String id; if (!useKeyProvider) { if (cache == null) { cache = new HashMap(); } id = cache.get(m); if (id == null) { id = generateModelId(m); cache.put(m, id); nodes.put(id, new TreeNode(id, m)); } return id; } id = generateModelId(m); if (!nodes.containsKey(id)) { nodes.put(id, new TreeNode(id, m)); } return id; } protected void renderChildren(M parent, boolean auto) { List children = parent == null ? treeStore.getRootItems() : treeStore.getChildren(parent); for (M child : children) { register(child); } if (parent == null) { store.add(children); } for (M child : children) { if (autoExpand) { final M c = child; DeferredCommand.addCommand(new Command() { public void execute() { setExpanded(c, true); } }); } else if (loader != null) { if (autoLoad) { if (store.isFiltered() || (!auto)) { renderChildren(child, auto); } else { loader.loadChildren(child); } } } } } protected void unregister(M m) { TreeNode node = null; if (m != null && useKeyProvider != null && (node = findNode(m)) != null) { node.clearElements(); nodes.remove(useKeyProvider ? generateModelId(m) : cache.remove(m)); TreeGridEvent e = new TreeGridEvent(this); e.setModel(m); e.setTreeNode(node); fireEvent(Events.Unregister, e); } } private void doInitialLoad() { if (treeStore.getRootItems().size() == 0 && loader != null) { loader.load(); } else { renderChildren(null, false); if (autoExpand) { expandAll(); } else { statefulExpand(treeStore.getRootItems()); } } } private void setExpandChildren(M m, boolean expand) { for (M child : treeStore.getChildren(m)) { setExpanded(child, expand, true); } } @SuppressWarnings({"unchecked", "rawtypes"}) private void statefulExpand(List children) { if (isStateful() && treeStore.getKeyProvider() != null) { List expanded = (List) getState().get("expanded"); if (expanded != null && expanded.size() > 0) { for (M child : children) { String id = treeStore.getKeyProvider().getKey(child); if (expanded.contains(id)) { setExpanded(child, true); } } } } } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy