
com.extjs.gxt.ui.client.widget.treegrid.TreeGrid Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gxt Show documentation
Show all versions of gxt Show documentation
Rich Internet Application Framework for GWT
/*
* 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