/*
* GWT-Ext Widget Library
* Copyright 2007 - 2008, GWT-Ext LLC., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 3 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package com.gwtext.client.widgets.tree;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.gwtext.client.core.Function;
import com.gwtext.client.data.Node;
import com.gwtext.client.data.NodeModel;
import com.gwtext.client.util.JavaScriptObjectHelper;
import com.gwtext.client.widgets.QuickTipsConfig;
import com.gwtext.client.widgets.tree.event.TreeNodeListener;
/**
* TreeNode class.
*
* @author Sanjiv Jivan
* @see com.gwtext.client.widgets.tree.TreePanel
*/
public class TreeNode extends Node {
protected NodeModel nodeModel = null;
/**
* Create a new TreeNode.
*/
public TreeNode() {
}
/**
* Create a new TreeNode.
*
* @param text the text / label of the TreeNode
*/
public TreeNode(final String text) {
setText(text);
}
/**
* Create a new TreeNode.
*
* @param text the text / label of the TreeNode
* @param iconCls the tree node icon CSS class
*/
public TreeNode(String text, String iconCls){
setText(text);
setIconCls(iconCls);
}
public TreeNode(NodeModel model){
setNodeModel(model);
}
TreeNode(JavaScriptObject jsObj) {
super(jsObj);
}
public static TreeNode treeNodeInstance(JavaScriptObject jsObj){
return new TreeNode(jsObj);
}
public JavaScriptObject getJsObj() {
if(jsObj == null) {
jsObj = super.getJsObj();
setNodeModel(nodeModel);
}
return jsObj;
}
protected native JavaScriptObject create(JavaScriptObject config)/*-{
return new $wnd.Ext.tree.TreeNode(config);
}-*/;
protected Node createNode(JavaScriptObject jsNode) {
return new TreeNode(jsNode);
}
/**
* Collapse this node.
*/
public native void collapse() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.collapse();
}-*/;
/**
* Collapse this node.
*
* @param deep true to collapse all children as well
* @param anim false to disable the default animation
*/
public native void collapse(boolean deep, boolean anim) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.collapse(deep, anim);
}-*/;
/**
* Collapse all child nodes.
*
* @param deep true if the child nodes should also collapse their child nodes
*/
public native void collapseChildNodes(boolean deep) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.collapseChildNodes(deep);
}-*/;
/**
* Destroy this node.
*/
public native void destroy() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.destroy();
}-*/;
/**
* Disables this node.
*/
public native void disable() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.disable();
}-*/;
/**
* True if this node is disabled.
*
* @return true if disabled
*/
public native boolean isDisabled() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
return node.disabled;
}-*/;
/**
* Enables this node.
*/
public native void enable() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.enable();
}-*/;
/**
* Ensures all parent nodes are expanded.
*/
public native void ensureVisible() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.ensureVisible();
}-*/;
public boolean isRendered() {
if(jsObj == null) return false;
TreeNodeUI treeNodeUI = getUI();
if(treeNodeUI == null) {
return false;
} else {
return treeNodeUI.getEl() != null;
}
}
/**
* Expand this node. The tree must be rendered before this method is called.
*/
public void expand(){
if(isRendered()) {
doExpand();
} else {
setExpanded(true);
}
}
private native void doExpand() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.expand();
}-*/;
//make sure tree is rendered before expand called
//todo add callback
/**
* Expand this node. The tree must be rendered before this method is called.
*
* @param deep true to expand all children as well
* @param anim false to cancel the default animation
*/
public void expand(boolean deep, boolean anim){
if(isRendered()){
doExpand(deep, anim);
} else {
setExpanded(true);
}
}
private native void doExpand(boolean deep, boolean anim) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.expand(deep, anim);
}-*/;
/**
* Expand this node. The tree must be rendered before this method is called.
*
* @param deep true to expand all children as well
* @param anim false to cancel the default animation
* @param callback a callback to be called when expanding this node completes (does not wait for deep expand to complete)
*/
public void expand(boolean deep, boolean anim, Function callback) {
if(isRendered()) {
doExpand(deep, anim, callback);
} else {
setExpanded(true);
}
}
private native void doExpand(boolean deep, boolean anim, Function callback) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.expand(deep, anim, function(self) {
callback.@com.gwtext.client.core.Function::execute()();
});
}-*/;
/**
* Expand all child nodes.
*
* @param deep true if the child nodes should also expand their child nodes
*/
public native void expandChildNodes(boolean deep) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.expandChildNodes(deep);
}-*/;
/**
* Returns the UI object for this node.
*
* @return the TreeNodeUI
*/
public native TreeNodeUI getUI() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
var ui = node.getUI();
return ui === undefined || ui == null ? null : @com.gwtext.client.widgets.tree.TreeNodeUI::instance(Lcom/google/gwt/core/client/JavaScriptObject;)(ui);
}-*/;
/**
* Returns true if this node is expanded.
*
* @return true if expanded
*/
public native boolean isExpanded() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
return node.isExpanded();
}-*/;
/**
* Returns true if this node is selected.
*
* @return true if selected
*/
public native boolean isSelected() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
return node.isSelected();
}-*/;
/**
* Triggers selection of this node.
*/
public void select() {
if(getOwnerTree() != null) {
selectRendered();
}
}
private native void selectRendered() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.select();
}-*/;
/**
* Sets the text for this node.
*
* @param text the node text
*/
private native void setTextCreated(String text) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.setText(text);
}-*/;
/**
* Return the nodes text.
*
* @return the nodes text
*/
public native String getText() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
return node.text;
}-*/;
/**
* Toggles expanded/collapsed state of the node.
*/
public native void toggle() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.toggle();
}-*/;
/**
* Triggers deselection of this node.
*/
public native void unselect() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.unselect();
}-*/;
/**
* Clones this TreeNode.
*
* @return the cloned node
*/
public native TreeNode cloneNode() /*-{
var nodeJS = this.@com.gwtext.client.core.JsObject::getJsObj()();
var copy = new $wnd.Ext.tree.TreeNode(
$wnd.Ext.apply({}, nodeJS.attributes)
);
copy.loader = undefined;
var copyJ = @com.gwtext.client.widgets.tree.TreeNode::treeNodeInstance(Lcom/google/gwt/core/client/JavaScriptObject;)(copy);
return copyJ;
}-*/;
/**
* Clones this TreeNode applying the specified configuration to the cloned node.
*
* @param config the cloned node config
* @return the cloned node
*/
public native TreeNode cloneNode(TreeNode config) /*-{
var nodeJS = this.@com.gwtext.client.core.JsObject::getJsObj()();
var configJS = config.@com.gwtext.client.core.JsObject::getJsObj()();
var copy = new $wnd.Ext.tree.TreeNode(
$wnd.Ext.apply(nodeJS.attributes, configJS)
);
copy.loader = undefined;
var copyJ = @com.gwtext.client.widgets.tree.TreeNode::treeNodeInstance(Lcom/google/gwt/core/client/JavaScriptObject;)(copy);
return copyJ;
}-*/;
/**
* Adds a TreeNode listener.
*
* @param listener the listener
*/
public native void addListener(TreeNodeListener listener)/*-{
this.@com.gwtext.client.data.Node::addListener(Lcom/gwtext/client/data/event/NodeListener;)(listener);
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
var nodeJ = this;
node.addListener('beforechildrenrendered',
function(source) {
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::doBeforeChildrenRendered(Lcom/gwtext/client/data/Node;)(nodeJ);
}
);
node.addListener('beforeclick',
function(source, event) {
var e = @com.gwtext.client.core.EventObject::instance(Lcom/google/gwt/core/client/JavaScriptObject;)(event);
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::doBeforeClick(Lcom/gwtext/client/data/Node;Lcom/gwtext/client/core/EventObject;)(nodeJ, e);
}
);
node.addListener('beforecollapse',
function(source, deep, anim) {
if(deep == null || deep === undefined) deep = false;
if(anim == null || anim === undefined) anim = false;
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::doBeforeCollapse(Lcom/gwtext/client/data/Node;ZZ)(nodeJ, deep, anim);
}
);
node.addListener('beforeexpand',
function(source, deep, anim) {
if(deep == null || deep === undefined) deep = false;
if(anim == null || anim === undefined) anim = false;
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::doBeforeExpand(Lcom/gwtext/client/data/Node;ZZ)(nodeJ, deep, anim);
}
);
node.addListener('checkchange',
function(source, checked) {
listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onCheckChanged(Lcom/gwtext/client/data/Node;Z)(nodeJ, checked);
}
);
node.addListener('click',
function(source, event) {
var e = @com.gwtext.client.core.EventObject::instance(Lcom/google/gwt/core/client/JavaScriptObject;)(event);
listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onClick(Lcom/gwtext/client/data/Node;Lcom/gwtext/client/core/EventObject;)(nodeJ, e);
}
);
node.addListener('collapse',
function(source) {
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onCollapse(Lcom/gwtext/client/data/Node;)(nodeJ);
}
);
node.addListener('contextmenu',
function(source, event) {
var e = @com.gwtext.client.core.EventObject::instance(Lcom/google/gwt/core/client/JavaScriptObject;)(event);
listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onContextMenu(Lcom/gwtext/client/data/Node;Lcom/gwtext/client/core/EventObject;)(nodeJ, e);
}
);
node.addListener('dblclick',
function(source, event) {
var e = @com.gwtext.client.core.EventObject::instance(Lcom/google/gwt/core/client/JavaScriptObject;)(event);
listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onDblClick(Lcom/gwtext/client/data/Node;Lcom/gwtext/client/core/EventObject;)(nodeJ, e);
}
);
node.addListener('disabledchange',
function(source, disabled) {
listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onDisabledChange(Lcom/gwtext/client/data/Node;Z)(nodeJ, disabled);
}
);
node.addListener('expand',
function(source) {
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onExpand(Lcom/gwtext/client/data/Node;)(nodeJ);
}
);
node.addListener('textchange',
function(source, text, oldText) {
if(oldText === undefined) oldText = null;
return listener.@com.gwtext.client.widgets.tree.event.TreeNodeListener::onTextChange(Lcom/gwtext/client/data/Node;Ljava/lang/String;Ljava/lang/String;)(nodeJ, text, oldText);
}
);
}-*/;
//config
/**
* False to make this node undraggable if DD is on (defaults to true).
*
* @param allowDrag false to disable drag
*/
public void setAllowDrag(boolean allowDrag) {
JavaScriptObjectHelper.setAttribute(configJS, "allowDrag", allowDrag);
}
/**
* False if this node cannot be drop on.
*
* @param allowDrop flase to disable drop
*/
public void setAllowDrop(boolean allowDrop) {
JavaScriptObjectHelper.setAttribute(configJS, "allowDrop", allowDrop);
}
/**
* True to render a checked checkbox for this node, false to render an unchecked checkbox
* (defaults to undefined with no checkbox rendered).
*
* @param checked true for checked checkbox
*/
public void setChecked(boolean checked) {
JavaScriptObjectHelper.setAttribute(configJS, "checked", checked);
}
/**
* A CSS class to be added to the node.
*
* @param cls a CSS class
*/
public void setCls(String cls) {
JavaScriptObjectHelper.setAttribute(configJS, "cls", cls);
}
/**
* True to start the node disabled
*
* @param disabled true to start disabled
*/
public void setDisabled(boolean disabled) {
JavaScriptObjectHelper.setAttribute(configJS, "disabled", disabled);
}
/**
* True to start the node expanded
*
* @param expanded true to start expanded
*/
public void setExpanded(boolean expanded) {
JavaScriptObjectHelper.setAttribute(configJS, "expanded", expanded);
}
/**
* Returns true if this node is expandable.
*
* @return true if expandable
*/
public boolean isExpandable() {
return JavaScriptObjectHelper.getAttributeAsBoolean(configJS, "expandable");
}
/**
* True to mark the node as expandable. If set to true, the node will always show a plus/minus icon, even when empty
*
* @param expandable true to start expandable
*/
public void setExpandable(boolean expandable) {
JavaScriptObjectHelper.setAttribute(configJS, "expandable", expandable);
}
/**
* URL of the link used for the node (defaults to #).
*
* @param href the node href URL
*/
public void setHref(String href) {
JavaScriptObjectHelper.setAttribute(configJS, "href", href);
}
/**
* Target frame for the link
*
* @param hrefTarget href frame target
*/
public void setHrefTarget(String hrefTarget) {
JavaScriptObjectHelper.setAttribute(configJS, "hrefTarget", hrefTarget);
}
/**
* The path to an icon for the node. The preferred way to do this is to use the cls
* or iconCls attributes and add the icon via a CSS background image.
*
* @param icon the node icon
*/
public void setIcon(String icon) {
if(!isRendered()) {
JavaScriptObjectHelper.setAttribute(configJS, "icon", icon);
} else {
Element el = getUI().getIconEl();
if(el != null) {
DOM.setElementAttribute(el, "src", icon);
}
}
}
/**
* A css class to be added to the nodes icon element for applying css background images.
*
* @param iconCls the icon CSS class
*/
public void setIconCls(String iconCls) {
if (!isRendered()) {
JavaScriptObjectHelper.setAttribute(configJS, "iconCls", iconCls);
} else {
Element el = getUI().getIconEl();
if (el != null) {
DOM.setElementAttribute(el, "className", "x-tree-node-icon " + iconCls);
DOM.setElementAttribute(el, "class", "x-tree-node-icon " + iconCls);
}
}
}
public String getIconCls() {
return JavaScriptObjectHelper.getAttribute(configJS, "iconCls");
}
/**
* Set a Tree node tooltip.
*
* @param qtip the tooltip
*/
public void setTooltip(String qtip) {
JavaScriptObjectHelper.setAttribute(configJS, "qtip", qtip);
}
/**
* Set a Tree node tooltip.
*
* @param qtipConfig the tooltip configuration
*/
public void setTooltip(QuickTipsConfig qtipConfig) {
JavaScriptObjectHelper.setAttribute(configJS, "qtipCfg", qtipConfig.getJsObj());
}
/**
* True for single click expand on this node.
*
* @param singleClickExpand true for single click expand
*/
public void setSingleClickExpand(boolean singleClickExpand) {
JavaScriptObjectHelper.setAttribute(configJS, "singleClickExpand", singleClickExpand);
}
/**
* The text for this node.
*
* @param text the nodes text / label
*/
public void setText(String text) {
if(!isCreated()) {
JavaScriptObjectHelper.setAttribute(configJS, "text", text);
} else {
setTextCreated(text);
}
}
//http://extjs.com/forum/showthread.php?t=4460&highlight=uiProvider
//TODO
//uiProvider
/**
* Sets a new TreeNodeUI
*
* @param uiProvider the new UI Provider
*/
public void setUiProvider(JavaScriptObject uiProvider) {
JavaScriptObjectHelper.setAttribute(configJS, "uiProvider", uiProvider);
}
public void setTreeAttribute(String name, Object value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
public void setTreeAttribute(String name, int value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
public void setTreeAttribute(String name, float value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
public void setTreeAttribute(String name, Date value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
public void setTreeAttribute(String name, boolean value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
public void setTreeAttribute(String name, double value) {
JavaScriptObjectHelper.setAttribute(configJS, name, value);
}
/**
* add child information to this node.
* @param child node based on JSon data:
*
* [{
* text: "TreeNode1",
* leaf: true
* }, {
* text: "TreeNode2",
* leaf: true
* }, {
* text: "TreeNode3",
* leaf: true
* }]
*
*/
public native void addChild(JavaScriptObject child)/*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.addChild(child);
}-*/;
/**
* Set the NodeModel associated to this TreeNode
* @param nodeModel The NodeModel to be associated to the TreeNode
*/
public void setNodeModel(NodeModel nodeModel){
if(nodeModel == null) return;
this.nodeModel = nodeModel;
if(!isCreated()) {
this.nodeModel = nodeModel;
} else {
setNodeModelCreated(nodeModel);
}
HashMap props = nodeModel.getProperties();
Iterator iter = props.keySet().iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
Object value = nodeModel.getPropertyAsObject(key);
setTreeNodeModelAttribute(key, value);
}
}
public void setTreeNodeModelAttribute(String key, Object value) {
if(NodeModel.attributesAllowed.contains(key)){
if(value instanceof Boolean){
JavaScriptObjectHelper.setAttribute(configJS, key, ((Boolean)value).booleanValue());
}else if(value instanceof String){
if(key.equals("text"))
setText((String)value);
else
JavaScriptObjectHelper.setAttribute(configJS, key, (String) value);
}else if(value instanceof Integer){
JavaScriptObjectHelper.setAttribute(configJS, key, ((Integer)value).intValue());
}else if(value instanceof Float){
JavaScriptObjectHelper.setAttribute(configJS, key, ((Float)value).floatValue());
}else if(value instanceof Double){
JavaScriptObjectHelper.setAttribute(configJS, key, ((Double)value).doubleValue());
}else if(value instanceof Date){
JavaScriptObjectHelper.setAttribute(configJS, key, (Date) value);
}else{
JavaScriptObjectHelper.setAttribute(configJS, key, value);
}
}
else{
setAttribute(key, value);
}
}
/**
* Return the user defined object
*
* @return the user defined object
*/
public NodeModel getNodeModel() {
if(!isCreated()) {
return nodeModel;
} else {
return (NodeModel)getNodeModelCreated();
}
}
/**
* Associate a NodeModel Object with the node.
*
* @param o the user data object
*/
private native void setNodeModelCreated(Object o) /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
node.attributes._nodeModel = o;
}-*/;
/**
* Return the NodeModel object associated with the node.
*
* @return the user defined object , null if not defined
*/
private native Object getNodeModelCreated() /*-{
var node = this.@com.gwtext.client.core.JsObject::getJsObj()();
//need to convert javascript undefined to null before passing to java layer
if(node.attributes._nodeModel === undefined) {
return null;
} else {
return node.attributes._nodeModel;
}
}-*/;
}