
com.extjs.gxt.ui.client.widget.treepanel.TreePanel 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.treepanel;
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.aria.FocusFrame;
import com.extjs.gxt.ui.client.core.DomHelper;
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.BaseModel;
import com.extjs.gxt.ui.client.data.ModelData;
import com.extjs.gxt.ui.client.data.ModelIconProvider;
import com.extjs.gxt.ui.client.data.ModelProcessor;
import com.extjs.gxt.ui.client.data.ModelStringProvider;
import com.extjs.gxt.ui.client.data.TreeLoader;
import com.extjs.gxt.ui.client.event.BaseEvent;
import com.extjs.gxt.ui.client.event.CheckChangedEvent;
import com.extjs.gxt.ui.client.event.CheckChangedListener;
import com.extjs.gxt.ui.client.event.CheckProvider;
import com.extjs.gxt.ui.client.event.ComponentEvent;
import com.extjs.gxt.ui.client.event.Events;
import com.extjs.gxt.ui.client.event.Listener;
import com.extjs.gxt.ui.client.event.TreePanelEvent;
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.util.DelayedTask;
import com.extjs.gxt.ui.client.util.KeyNav;
import com.extjs.gxt.ui.client.util.Rectangle;
import com.extjs.gxt.ui.client.widget.BoxComponent;
import com.extjs.gxt.ui.client.widget.treepanel.TreePanelView.TreeViewRenderMode;
import com.google.gwt.dom.client.Document;
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 widget bound directly to a @link {@link TreeStore}.
* TreePanel
contains no child widgets, rather, the tree is
* rendered based on the models contained in the tree store. Once bound, the
* tree will remain in sync with the bound tree store.
*
*
* The text of each node can be specified in a couple of different ways. First,
* a display property can be set using {@link #setDisplayProperty(String)}. The
* is useful when the item's text is contained within the model's data. Second,
* a model string provider can be specified using
* {@link #setLabelProvider(ModelStringProvider)}.
*
*
* 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.
*
*
* - Events:
*
* - BeforeExpand : TreePanelEvent(treePanel, item)
* Fires before a node is expanded. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.
*
* - treePanel : the source tree
* - item : this
*
*
*
* - BeforeCollapse : TreePanelEvent(treePanel, item)
* Fires before a node is collapsed. Listeners can cancel the action by
* calling {@link BaseEvent#setCancelled(boolean)}.
*
* - treePanel : the source tree
* - item : this
*
*
*
* - Expand : TreePanelEvent(treePanel, item)
* Fires after a node has been expanded.
*
* - treePanel : the source tree
* - item : this
*
*
*
* - Collapse : TreePanelEvent(treePanel, item)
* Fires after a node is collapsed.
*
* - treePanel : the source tree
* - item : this
*
*
*
* - BeforeCheckChange : TreePanelEvent(treePanel, item)
* Fires before a node's check state is changed. Listeners can cancel the
* action by calling {@link BaseEvent#setCancelled(boolean)}.
*
* - treePanel : the source tree
* - item : this
* - checked : the checked state.
*
*
*
* - CheckChange : TreePanelEvent(treePanel, item)
* Fires after a item's check state changes.
*
* - treePanel : the source tree
* - item : this
* - checked : the checked state.
*
*
*
* - CheckChanged : CheckChangeEvent(provider, checkedeSelection)
* Fires after the tree's overall checked state changes.
*
* - provider : the source tree
* - checkedSelection : the tree checked item
*
*
*
*
*
* @param the model type
*/
public class TreePanel extends BoxComponent implements CheckProvider {
/**
* Check cascade enum.
*/
public enum CheckCascade {
/**
* Checks cascade to all child nodes.
*/
CHILDREN,
/**
* Checks to not cascade.
*/
NONE,
/**
* Checks cascade to all parent nodes.
*/
PARENTS;
}
/**
* Check nodes enum.
*/
public enum CheckNodes {
/**
* Check boxes for both leafs and parent nodes.
*/
BOTH,
/**
* Check boxes for only leaf nodes.
*/
LEAF,
/**
* Check boxes for only parent nodes.
*/
PARENT;
}
/**
* Joint enum.
*/
public enum Joint {
COLLAPSED(1), EXPANDED(2), NONE(0);
private int value;
private Joint(int value) {
this.value = value;
}
public int value() {
return value;
}
}
/**
* Maintains the internal state of nodes contained in the tree. Should not
* need to be referenced in typical usage.
*/
public class TreeNode {
protected Element element, container, joint, check, text, icon, elContainer;
protected String id;
protected M m;
private boolean childrenRendered;
private boolean expand, checked, expandDeep;
private boolean expanded;
private boolean leaf = true;
private boolean loaded;
private boolean loading;
TreeNode(String id, M m) {
this.id = id;
this.m = m;
}
public void clearElements() {
joint = null;
check = null;
icon = null;
text = null;
}
public Element getElement() {
if (element == null) {
element = (Element) Document.get().getElementById(id);
}
return element;
}
public int getItemCount() {
return store.getChildCount(m);
}
public M getModel() {
return m;
}
public TreeNode getParent() {
M p = store.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) {
TreePanel.this.setExpanded(m, expand);
}
public void setLeaf(boolean leaf) {
this.leaf = leaf;
TreePanel.this.refresh(m);
}
}
protected Map cache;
protected String displayProperty;
protected boolean filtering;
protected String itemSelector = ".x-tree3-node";
protected TreeLoader loader;
protected Map nodes = new FastMap();
protected TreePanelSelectionModel sm;
protected TreeStore store;
protected TreePanelView view = new TreePanelView();
private boolean autoExpand;
private boolean autoLoad;
private boolean autoSelect;
private boolean caching = true;
private boolean checkable;
private CheckNodes checkNodes = CheckNodes.BOTH;
private CheckCascade checkStyle = CheckCascade.PARENTS;
private boolean expandOnFilter = true;
private ModelIconProvider iconProvider;
private ModelStringProvider labelProvider;
private ModelProcessor modelProcessor;
private StoreListener storeListener = new StoreListener() {
@Override
public void storeAdd(StoreEvent se) {
onAdd((TreeStoreEvent) se);
}
@Override
public void storeClear(StoreEvent se) {
onClear((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 trackMouseOver = true;
private DelayedTask updateTask, cleanTask;
private Boolean useKeyProvider = null;
/**
* Creates a new tree panel.
*
* @param store the tree store
*/
public TreePanel(TreeStore store) {
this.store = store;
this.loader = store.getLoader();
store.addStoreListener(storeListener);
baseStyle = "x-tree3";
setSelectionModel(new TreePanelSelectionModel());
view.bind(this, store);
}
public void addCheckListener(CheckChangedListener listener) {
addListener(Events.CheckChanged, listener);
}
/**
* Collapses all nodes.
*/
public void collapseAll() {
for (M child : store.getRootItems()) {
setExpanded(child, false, true);
}
}
/**
* Expands all nodes.
*/
public void expandAll() {
for (M child : store.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 item = fly(target).findParentElement(itemSelector, 10);
if (item != null) {
String id = item.getId();
TreeNode node = nodes.get(id);
return node;
}
return null;
}
public List getCheckedSelection() {
List checked = new ArrayList();
for (TreeNode n : nodes.values()) {
if (n.checked) {
checked.add(n.m);
}
}
return checked;
}
/**
* Returns the child nodes value which determines what node types have a check
* box. Only applies when check boxes have been enabled (
* {@link #setCheckable(boolean)}.
*
* @return the child nodes value
*/
public CheckNodes getCheckNodes() {
return checkNodes;
}
/**
* The check cascade style value which determines if check box changes cascade
* to parent and children.
*
* @return the check cascade style
*/
public CheckCascade getCheckStyle() {
return checkStyle;
}
/**
* Returns the display property.
*
* @return the display property
*/
public String getDisplayProperty() {
return displayProperty;
}
/**
* Returns the model icon provider.
*
* @return the icon provider
*/
public ModelIconProvider getIconProvider() {
return iconProvider;
}
/**
* Returns the model processor.
*
* @return the model processor
*/
public ModelProcessor getModelProcessor() {
return modelProcessor;
}
/**
* Returns the tree's selection model.
*
* @return the selection model
*/
public TreePanelSelectionModel getSelectionModel() {
return sm;
}
/**
* Returns the tree's store.
*
* @return the store
*/
public TreeStore getStore() {
return store;
}
/**
* Returns the tree style.
*
* @return the tree style
*/
public TreeStyle getStyle() {
return style;
}
/**
* Returns the tree's view.
*
* @return the view
*/
public TreePanelView getView() {
return view;
}
/**
* 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 if select on load is enabled.
*
* @return the auto select state
*/
public boolean isAutoSelect() {
return autoSelect;
}
/**
* 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 check boxes are enabled.
*
* @return the check box state
*/
public boolean isCheckable() {
return checkable;
}
public boolean isChecked(M model) {
TreeNode node = findNode(model);
if (node != null && isCheckable(node)) {
return node.checked;
}
return false;
}
/**
* 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.expanded;
}
/**
* 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();
}
/**
* Returns true if nodes are highlighted on mouse over.
*
* @return true if enabled
*/
public boolean isTrackMouseOver() {
return trackMouseOver;
}
@Override
@SuppressWarnings({"unchecked", "rawtypes"})
public void onComponentEvent(ComponentEvent ce) {
super.onComponentEvent(ce);
TreePanelEvent tpe = (TreePanelEvent) ce;
int type = ce.getEventTypeInt();
switch (type) {
case Event.ONCLICK:
onClick(tpe);
break;
case Event.ONDBLCLICK:
onDoubleClick(tpe);
break;
case Event.ONSCROLL:
onScroll(tpe);
break;
case Event.ONFOCUS:
onFocus(ce);
break;
}
view.onEvent(tpe);
}
@Override
public void recalculate() {
super.recalculate();
update();
}
public void removeCheckListener(CheckChangedListener listener) {
removeListener(Events.CheckChanged, listener);
}
/**
* Scrolls the tree to ensure the given model is visible.
*
* @param model the model to scroll into view
*/
public void scrollIntoView(M model) {
TreeNode node = findNode(model);
if (node != null) {
Element c = getView().getElementContainer(node);
if (c != null) {
c.scrollIntoView();
}
}
}
/**
* 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;
}
/**
* True to select the first model after the store's data changes (defaults to
* false).
*
* @param autoSelect true to auto select
*/
public void setAutoSelect(boolean autoSelect) {
this.autoSelect = autoSelect;
}
/**
* 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;
}
/**
* Sets whether check boxes are used in the tree.
*
* @param checkable true for check boxes
*/
public void setCheckable(boolean checkable) {
this.checkable = checkable;
}
/**
* Sets the check state of the item. The checked state will only be set for
* nodes that have been rendered, {@link #setAutoLoad(boolean)} can be used to
* render all children.
*
* @param item the item
* @param checked true for checked
*/
public void setChecked(M item, boolean checked) {
if (!checkable) return;
TreeNode node = findNode(item);
if (node != null) {
if (node.checked == checked) {
onCheckCascade(item, checked);
return;
}
boolean leaf = node.isLeaf();
if ((!leaf && checkNodes == CheckNodes.LEAF) || (leaf && checkNodes == CheckNodes.PARENT)) {
return;
}
TreePanelEvent evt = new TreePanelEvent(this, item);
evt.setChecked(checked);
if (fireEvent(Events.BeforeCheckChange, evt)) {
node.checked = checked;
view.onCheckChange(node, checkable, checked);
fireEvent(Events.CheckChange, evt);
CheckChangedEvent cce = new CheckChangedEvent(this, (M) null);
fireEvent(Events.CheckChanged, cce);
onCheckCascade(item, checked);
}
}
}
public void setCheckedSelection(List selection) {
for (M m : store.getAllItems()) {
setChecked(m, selection != null && selection.contains(m));
}
}
/**
* Sets which tree items will display a check box (defaults to BOTH).
*
* Valid values are:
*
* - BOTH - both nodes and leafs
* - PARENT - only nodes with children
* - LEAF - only leafs
*
*
* @param checkNodes the child nodes value
*/
public void setCheckNodes(CheckNodes checkNodes) {
this.checkNodes = checkNodes;
if (rendered) {
for (M m : store.getAllItems()) {
refresh(m);
}
}
}
/**
* Sets the cascading behavior for check tree (defaults to PARENTS). When
* using CHILDREN, it is important to note that the cascade will only be
* applied to rendered nodes. {@link #setAutoLoad(boolean)} can be used to
* fully render the tree on render.
*
* Valid values are:
*
* - NONE - no cascading
* - PARENTS - cascade to parents
* - CHILDREN - cascade to children
*
*
* @param checkStyle the child style
*/
public void setCheckStyle(CheckCascade checkStyle) {
this.checkStyle = checkStyle;
}
/**
* Sets the display property name used to the item's text. As an alternative,
* a ModelStringProvider
can be specified using
* {@link #setLabelProvider(ModelStringProvider)}.
*
* @param displayProperty the property name
*/
public void setDisplayProperty(String displayProperty) {
this.displayProperty = displayProperty;
}
/**
* 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
*/
public void setExpanded(M model, boolean expand, boolean deep) {
if (expand) {
// make parents visible
List list = new ArrayList();
M p = model;
while ((p = store.getParent(p)) != null) {
TreeNode n = findNode(p);
if (n != null && !n.isExpanded()) {
list.add(p);
}
}
for (int i = list.size() - 1; i >= 0; i--) {
M item = list.get(i);
setExpanded(item, expand, false);
}
}
TreeNode node = findNode(model);
if (node != null) {
if (!rendered) {
node.expand = expand;
return;
}
if (expand) {
onExpand(model, node, deep);
} else {
onCollapse(model, node, deep);
}
}
}
/**
* 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 tree's model string provider for the text description of each
* node. If a a display property has been specified, it will be passed to the
* string provider. If a property has not been specified, null
* will be passed.
*
* @param labelProvider the label provider
*/
public void setLabelProvider(ModelStringProvider labelProvider) {
this.labelProvider = labelProvider;
}
/**
* 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);
}
}
/**
* Sets the treepanels's model processor.
*
* @see ModelProcessor
* @param modelProcessor
*/
public void setModelProcessor(ModelProcessor modelProcessor) {
this.modelProcessor = modelProcessor;
}
/**
* Sets the tree's selection model.
*
* @param sm the selection model
*/
public void setSelectionModel(TreePanelSelectionModel sm) {
if (this.sm != null) {
this.sm.bindTree(null);
}
this.sm = sm;
if (sm != null) {
sm.bindTree(this);
}
}
/**
* Sets the tree style.
*
* @param style the tree style
*/
public void setStyle(TreeStyle style) {
this.style = style;
}
/**
* True to highlight nodes when the mouse is over (defaults to true).
*
* @param trackMouseOver true to highlight nodes on mouse over
*/
public void setTrackMouseOver(boolean trackMouseOver) {
this.trackMouseOver = trackMouseOver;
}
/**
* Sets the tree's view. Only needs to be called when customizing the tree's
* presentation.
*
* @param view the view
*/
public void setView(TreePanelView view) {
this.view = view;
view.bind(this, store);
}
/**
* 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);
return node.isLeaf() ? Joint.NONE : node.expanded ? Joint.EXPANDED : Joint.COLLAPSED;
}
protected AbstractImagePrototype calculateIconStyle(M model) {
AbstractImagePrototype style = null;
if (iconProvider != null) {
AbstractImagePrototype iconStyle = iconProvider.getIcon(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;
}
protected void clean() {
if (cleanTask == null) {
cleanTask = new DelayedTask(new Listener() {
public void handleEvent(BaseEvent be) {
doClean();
}
});
}
cleanTask.delay(view.getCleanDelay());
}
protected void clear() {
fly(getContainer(null)).removeChildren();
nodes.clear();
if (cache != null) {
cache.clear();
}
}
@Override
protected ComponentEvent createComponentEvent(Event event) {
TreePanelEvent tpe = new TreePanelEvent(this, event);
return tpe;
}
protected void doClean() {
int count = getVisibleRowCount();
if (count > 0) {
List rows = getChildModel(store.getRootItems(), true);
int[] vr = getVisibleRows(rows, count);
vr[0] -= view.getCacheSize();
vr[1] += view.getCacheSize();
int i = 0;
// if first is less than 0, all rows have been rendered
// so lets clean the end...
if (vr[0] <= 0) {
i = vr[1] + 1;
}
for (int len = rows.size(); i < len; i++) {
// if current row is outside of first and last and
// has content, update the innerHTML to nothing
if (i < vr[0] || i > vr[1]) {
cleanNode(findNode(rows.get(i)));
}
}
}
}
protected void doUpdate() {
int count = getVisibleRowCount();
if (count > 0) {
List rootItems = store.getRootItems();
List visible = getChildModel(rootItems, true);
int[] vr = getVisibleRows(visible, count);
for (int i = vr[0]; i <= vr[1]; i++) {
if (!isRowRendered(i, visible)) {
M parent = store.getParent(visible.get(i));
String html = renderChild(parent, visible.get(i), store.getDepth(parent), TreeViewRenderMode.BODY);
findNode(visible.get(i)).getElement().getFirstChildElement().setInnerHTML(html);
}
}
clean();
}
}
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() + "_" + (store.getKeyProvider() != null ? store.getKeyProvider().getKey(m) : XDOM.getUniqueId());
}
protected List getChildModel(List l, boolean onlyVisible) {
List list = new ArrayList();
for (M m : l) {
list.add(m);
if (!onlyVisible || findNode(m).isExpanded()) {
findChildren(m, list, onlyVisible);
}
}
return list;
}
protected Element getContainer(M model) {
if (model == null) {
return (Element) getElement();
}
TreeNode node = findNode(model);
if (node != null) {
return view.getContainer(node);
}
return null;
}
protected String getText(M model) {
if (labelProvider != null) {
return labelProvider.getStringValue(model, displayProperty);
} else if (displayProperty != null) {
return (String) model.get(displayProperty);
}
return "";
}
protected int getVisibleRowCount() {
int rh = view.getCalculatedRowHeight();
int visibleHeight = el().getHeight(true);
return (int) ((visibleHeight < 1) ? 0 : Math.ceil(visibleHeight / rh));
}
protected boolean hasChildren(M model) {
TreeNode node = findNode(model);
if (loader != null && !node.loaded) {
return loader.hasChildren(node.m);
}
if (!node.leaf || store.hasChildren(node.m)) {
return true;
}
return false;
}
protected boolean isCheckable(TreeNode node) {
boolean leaf = node.isLeaf();
boolean check = checkable;
switch (checkNodes) {
case LEAF:
if (!leaf) {
check = false;
}
break;
case PARENT:
if (leaf) {
check = false;
}
}
return check;
}
@Override
protected void notifyShow() {
super.notifyShow();
update();
}
protected void onAdd(TreeStoreEvent se) {
for (M child : se.getChildren()) {
register(child);
}
if (rendered) {
M parent = se.getParent();
TreeNode pn = findNode(parent);
if (parent == null || (pn != null && pn.childrenRendered)) {
StringBuilder sb = new StringBuilder();
for (M child : se.getChildren()) {
sb.append(renderChild(parent, child, store.getDepth(parent), TreeViewRenderMode.MAIN));
}
int index = se.getIndex();
if (index == 0) {
DomHelper.insertFirst(getContainer(parent), sb.toString());
} else if (index == store.getChildCount(parent) - se.getChildren().size()) {
DomHelper.insertHtml("beforeEnd", getContainer(parent), sb.toString());
} else {
DomHelper.insertBefore(fly(getContainer(parent)).getChild(index).dom, sb.toString());
}
}
refresh(parent);
update();
}
}
@Override
protected void onAttach() {
super.onAttach();
update();
}
protected void onCheckCascade(M model, boolean checked) {
switch (getCheckStyle()) {
case PARENTS:
if (checked) {
M p = store.getParent(model);
while (p != null) {
setChecked(p, true);
p = store.getParent(p);
}
} else {
for (M child : store.getChildren(model)) {
setChecked(child, false);
}
}
break;
case CHILDREN:
for (M child : store.getChildren(model)) {
setChecked(child, checked);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void onCheckClick(TreePanelEvent tpe, TreeNode node) {
tpe.stopEvent();
setChecked((M) tpe.getItem(), !node.checked);
}
protected void onClear(TreeStoreEvent se) {
clear();
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void onClick(TreePanelEvent tpe) {
TreeNode node = tpe.getNode();
if (node != null) {
Element jointEl = view.getJointElement(node);
if (jointEl != null && tpe.within(jointEl)) {
toggle((M) tpe.getItem());
} else if (GXT.isHighContrastMode) {
Rectangle r = El.fly(jointEl).getBounds();
if (r.contains(tpe.getClientX(), tpe.getClientY())) {
toggle((M) tpe.getItem());
}
}
Element checkEl = view.getCheckElement(node);
if (checkable && checkEl != null && tpe.within(checkEl)) {
onCheckClick(tpe, node);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void onCollapse(M model, TreeNode node, boolean deep) {
TreePanelEvent tpe = new TreePanelEvent(this);
tpe.setItem(model);
tpe.setNode(node);
if (node.expanded && fireEvent(Events.BeforeCollapse, tpe)) {
node.expanded = false;
// collapse
view.collapse(node);
if (isStateful() && store.getKeyProvider() != null) {
Map state = getState();
List expanded = (List) state.get("expanded");
String id = store.getKeyProvider().getKey(model);
if (expanded != null && expanded.contains(id)) {
expanded.remove(id);
saveState();
}
}
List l = new ArrayList();
l.add(node.m);
update();
cleanCollapsed(node.m);
fireEvent(Events.Collapse, tpe);
}
if (deep) {
setExpandChildren(model, false);
}
}
protected void onDataChanged(TreeStoreEvent se) {
if (!isRendered()) {
return;
}
M p = se.getParent();
if (p == null) {
clear();
renderChildren(null);
if (autoSelect) {
M m = store.getChild(0);
if (m != null) {
List sel = new ArrayList();
sel.add(m);
getSelectionModel().setSelection(sel);
}
}
statefulExpand(store.getRootItems());
} else {
TreeNode n = findNode(p);
n.loaded = true;
n.loading = false;
if (n.childrenRendered) {
getContainer(p).setInnerHTML("");
}
renderChildren(p);
if (n.expand && !n.isLeaf()) {
n.expand = false;
boolean c = caching;
caching = true;
boolean deep = n.expandDeep;
n.expandDeep = false;
setExpanded(p, true, deep);
caching = c;
} else {
refresh(p);
}
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void onDoubleClick(TreePanelEvent tpe) {
TreeNode node = tpe.getNode();
if (node != null) {
setExpanded(node.m, !node.expanded);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
protected void onExpand(M model, TreeNode node, boolean deep) {
TreePanelEvent tpe = new TreePanelEvent(this);
tpe.setItem(model);
tpe.setNode(node);
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) {
store.removeAll(model);
node.expand = true;
node.expandDeep = deep;
node.loading = true;
view.onLoading(node);
loader.loadChildren(model);
return;
}
if (!node.expanded && fireEvent(Events.BeforeExpand, tpe)) {
node.expanded = true;
if (!node.childrenRendered) {
renderChildren(model);
node.childrenRendered = true;
}
// expand
view.expand(node);
if (isStateful() && store.getKeyProvider() != null) {
Map state = getState();
List expanded = (List) state.get("expanded");
if (expanded == null) {
expanded = new ArrayList();
state.put("expanded", expanded);
}
String id = store.getKeyProvider().getKey(model);
if (!expanded.contains(id)) {
expanded.add(id);
saveState();
}
}
update();
fireEvent(Events.Expand, tpe);
}
if (deep) {
setExpandChildren(model, true);
} else {
statefulExpand(store.getChildren(model));
}
}
}
protected void onFilter(TreeStoreEvent se) {
if (isRendered()) {
filtering = store.isFiltered();
clear();
renderChildren(null);
if (expandOnFilter && store.isFiltered()) {
expandAll();
}
update();
}
}
protected void onFocus(ComponentEvent ce) {
FocusFrame.get().frame(this);
}
protected void onRemove(TreeStoreEvent se) {
TreeNode node = findNode(se.getChild());
if (node != null) {
if (node.getElement() != null) {
El.fly(node.getElement()).removeFromParent();
}
unregister(se.getChild());
for (M child : se.getChildren()) {
unregister(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);
String s = view.getTemplate(null, null, null, null, false, false, null, 0, TreeViewRenderMode.CONTAINER);
setElement(XDOM.create(s), target, index);
el().show();
el().setStyleAttribute("overflow", "auto");
if ((GXT.isIE6 || GXT.isIE7) && GXT.isStrict) {
el().makePositionable();
}
el().setTabIndex(0);
el().setElementAttribute("hideFocus", "true");
if (store.getRootItems().size() == 0 && loader != null) {
loader.load();
} else {
renderChildren(null);
if (autoSelect) {
getSelectionModel().select(0, false);
}
statefulExpand(store.getRootItems());
}
if (GXT.isFocusManagerEnabled()) {
Accessibility.setRole(getElement(), Accessibility.ROLE_TREE);
new KeyNav(this) {
@Override
public void onDown(ComponentEvent ce) {
if (sm.getSelectedItems().size() == 0 && store.getRootItems().size() > 0) {
sm.select(store.getRootItems().get(0), false);
}
}
};
} else {
// JAWS does not work when disabling text selection
disableTextSelection(true);
}
sinkEvents(Event.ONFOCUS | Event.ONSCROLL | Event.ONCLICK | Event.ONDBLCLICK | Event.MOUSEEVENTS | Event.KEYEVENTS);
}
protected void onResize(int width, int height) {
super.onResize(width, height);
update();
}
protected void onScroll(TreePanelEvent tpe) {
update();
}
protected void onUpdate(TreeStoreEvent se) {
refresh(se.getModel());
}
protected M prepareData(M model) {
if (modelProcessor != null) {
boolean silent = false;
if (model instanceof BaseModel) {
silent = ((BaseModel) model).isSilent();
((BaseModel) model).setSilent(true);
}
M m = modelProcessor.prepareData(model);
if (model instanceof BaseModel) {
((BaseModel) model).setSilent(silent);
}
return m;
}
return model;
}
protected void refresh(M model) {
if (rendered) {
TreeNode node = findNode(model);
if (node != null && node.getElement() != null) {
view.onIconStyleChange(node, calculateIconStyle(model));
view.onJointChange(node, calcualteJoint(model));
view.onTextChange(node, getText(model));
boolean checkable = isCheckable(node);
setChecked(node.m, node.checked && checkable);
}
}
}
protected String register(M m) {
if (useKeyProvider == null) {
if (store.getKeyProvider() == null) {
useKeyProvider = false;
} else {
useKeyProvider = true;
}
}
if (!useKeyProvider) {
if (cache == null) {
cache = new HashMap();
}
String id = cache.get(m);
if (id == null) {
id = generateModelId(m);
cache.put(m, id);
nodes.put(id, new TreeNode(id, m));
}
return id;
}
String id = generateModelId(m);
if (!nodes.containsKey(id)) {
nodes.put(id, new TreeNode(id, m));
}
return id;
}
protected String renderChild(M parent, M child, int depth, TreeViewRenderMode renderMode) {
child = prepareData(child);
String id = register(child);
TreeNode node = findNode(child);
return view.getTemplate(child, id, getText(child), calculateIconStyle(child), isCheckable(node), node.checked,
calcualteJoint(child), depth, renderMode);
}
protected void renderChildren(M parent) {
StringBuilder markup = new StringBuilder();
int depth = store.getDepth(parent);
List children = parent == null ? store.getRootItems() : store.getChildren(parent);
if (children.size() == 0) {
return;
}
for (M child : children) {
register(child);
}
for (int i = 0; i < children.size(); i++) {
markup.append(renderChild(parent, children.get(i), depth, TreeViewRenderMode.MAIN));
}
Element container = getContainer(parent);
container.setInnerHTML(markup.toString());
for (int i = 0; i < children.size(); i++) {
M child = children.get(i);
TreeNode node = findNode(child);
if (autoExpand) {
setExpanded(child, true);
} else if (node.expand && !node.isLeaf()) {
node.expand = false;
setExpanded(child, true);
} else if (loader != null) {
if (autoLoad) {
if (store.isFiltered()) {
renderChildren(child);
} else {
if (loader.hasChildren(child)) {
loader.loadChildren(child);
}
}
}
} else if (autoLoad) {
renderChildren(child);
}
}
TreeNode n = findNode(parent);
if (n != null) {
onCheckCascade(n.m, n.checked);
n.childrenRendered = true;
}
update();
}
protected void unregister(M m) {
if (m != null && useKeyProvider != null) {
TreeNode n = null;
if (useKeyProvider) {
n = nodes.remove(generateModelId(m));
} else {
n = nodes.remove(cache.remove(m));
}
if (n != null) {
n.clearElements();
}
}
}
protected void update() {
if (updateTask == null) {
updateTask = new DelayedTask(new Listener() {
public void handleEvent(BaseEvent be) {
doUpdate();
}
});
}
updateTask.delay(view.getScrollDelay());
}
private void cleanCollapsed(M parent) {
List list = store.getChildren(parent, true);
for (M m : list) {
TreeNode node = findNode(m);
if (node != null && node.element != null) {
cleanNode(node);
}
}
}
private void cleanNode(TreeNode node) {
if (node != null && node.element != null) {
node.clearElements();
fly((Element) node.getElement().getFirstChildElement()).removeChildren();
}
}
private void findChildren(M parent, List list, boolean onlyVisible) {
for (M child : store.getChildren(parent)) {
list.add(child);
if (!onlyVisible || findNode(child).isExpanded()) {
findChildren(child, list, onlyVisible);
}
}
}
private int[] getVisibleRows(List visible, int count) {
int sc = el().getScrollTop();
int start = (int) (sc == 0 ? 0 : Math.floor(sc / view.getCalculatedRowHeight()) - 1);
int first = Math.max(start, 0);
int last = Math.min(start + count + 2, visible.size() - 1);
return new int[] {first, last};
}
private boolean isRowRendered(int i, List visible) {
Element e = findNode(visible.get(i)).getElement();
return e != null && e.getFirstChild().hasChildNodes();
}
private void setExpandChildren(M m, boolean expand) {
for (M child : store.getChildren(m)) {
setExpanded(child, expand, true);
}
}
@SuppressWarnings({"unchecked", "rawtypes"})
private void statefulExpand(List children) {
if (isStateful() && store.getKeyProvider() != null) {
List expanded = (List) getState().get("expanded");
if (expanded != null && expanded.size() > 0) {
for (M child : children) {
String id = store.getKeyProvider().getKey(child);
if (expanded.contains(id)) {
setExpanded(child, true);
}
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy