/*******************************************************************************
* Copyright (c) 2012-2017 Codenvy, S.A.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Codenvy, S.A. - initial API and implementation
*******************************************************************************/
package org.eclipse.che.ide.ui.smartTree;
import elemental.svg.SVGSVGElement;
import com.google.common.base.Strings;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.user.client.Event;
import org.eclipse.che.ide.api.data.tree.Node;
/**
* @author Vlad Zhukovskiy
*/
public class TreeView {
protected NodeDescriptor over;
protected Tree tree;
private int cacheSize = 20;
private int cleanDelay = 500;
private int scrollDelay = 1;
public static String blankImageUrl = "data:image/gif;base64,R0lGODlhAQABAID/AMDAwAAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==";
public void bind(Tree tree) {
this.tree = tree;
}
public void collapse(NodeDescriptor node) {
getDescendantsContainer(node).getStyle().setDisplay(Style.Display.NONE);
onJointChange(node, tree.getJoint(node.getNode()));
}
public void expand(NodeDescriptor node) {
getDescendantsContainer(node).getStyle().setDisplay(Style.Display.BLOCK);
onJointChange(node, tree.getJoint(node.getNode()));
}
/**
* Returns the cache size.
*
* @return the cache size
*/
public int getCacheSize() {
return cacheSize;
}
public int getCleanDelay() {
return cleanDelay;
}
public Element getDescendantsContainer(NodeDescriptor node) {
if (node.getDescendantsContainerElement() == null) {
Element element = getRootContainer(node).getChildNodes().getItem(1).cast();
node.setDescendantsContainerElement(element);
}
return node.getDescendantsContainerElement();
}
/**
* Gets the rendered element, if any, for the given tree node object. This method will look up the dom element if it
* has not yet been seen. The getRootContainer() method for the node will return the same value as this method does after
* it has been cached.
*
* @param node
* the tree node to find an element for
* @return the element that the node represents, or null if not yet rendered
*/
public Element getRootContainer(NodeDescriptor node) {
if (node.getRootContainer() == null) {
Element element = Document.get().getElementById(node.getDomId()).cast();
node.setRootContainerElement(element);
}
return node.getRootContainer();
}
/**
* Return div that contains joint element, check element, main icon element, text element
*
* @param node
* @return
*/
public Element getNodeContainer(NodeDescriptor node) {
if (node.getNodeContainerElement() == null) {
node.setNodeContainerElement(getRootContainer(node) != null ? getRootContainer(node).getFirstChildElement() : null);
}
return node.getNodeContainerElement().cast();
}
public Element getJointContainer(NodeDescriptor node) {
if (node.getJointContainerElement() == null) {
Element element = getNodeContainer(node).getChildNodes().getItem(0).cast();
node.setJointContainerElement(element);
}
return node.getJointContainerElement();
}
public Element getIconContainer(NodeDescriptor node) {
if (node.getIconContainerElement() == null) {
Element element = getNodeContainer(node).getChildNodes().getItem(1).cast();
node.setIconContainerElement(element);
}
return node.getIconContainerElement();
}
public Element getUserElementContainer(NodeDescriptor node) {
if (node.getUserElement() == null) {
Element element = getNodeContainer(node).getChildNodes().getItem(2).cast();
node.setUserElement(element);
}
return node.getUserElement();
}
public Element getPresentableTextContainer(NodeDescriptor node) {
if (node.getPresentableTextContainer() == null) {
Element element = getNodeContainer(node).getChildNodes().getItem(3).cast();
node.setPresentableTextContainer(element);
}
return node.getPresentableTextContainer();
}
public Element getInfoTextContainer(NodeDescriptor node) {
if (node.getPresentableTextContainer() == null) {
Element element = getNodeContainer(node).getChildNodes().getItem(4).cast();
node.setInfoTextContainer(element);
}
return node.getInfoTextContainer();
}
public int getScrollDelay() {
return scrollDelay;
}
public boolean isSelectableTarget(Node node, Element target) {
NodeDescriptor nodeDescriptor = tree.getNodeDescriptor(node);
return nodeDescriptor != null && !isJointElement(target);
}
public void onDropChange(NodeDescriptor nodeDescriptor, boolean drop) {
Element e = tree.getView().getNodeContainer(nodeDescriptor);
setClassName(e, tree.getTreeStyles().styles().dragOver(), drop);
}
public void onEvent(Event ce) {
int type = ce.getTypeInt();
switch (type) {
case Event.ONMOUSEOVER:
if (tree.isTrackMouseOver()) {
onMouseOver(ce);
}
break;
case Event.ONMOUSEOUT:
if (tree.isTrackMouseOver()) {
onMouseOut(ce);
}
break;
}
}
public void onDepthUpdated(NodeDescriptor node, int newDepth) {
Element nodeElement = getNodeContainer(node);
nodeElement.getStyle().setPaddingLeft(newDepth * getIndenting(node), Style.Unit.PX);
}
public void onElementChanged(NodeDescriptor node, Element element) {
Element el = getRootContainer(node).getFirstChildElement();
if (el == null) {
return;
}
el.removeFromParent();
getRootContainer(node).insertFirst(element.getFirstChild());
node.setNodeContainerElement(null);
node.setJointContainerElement(null);
onSelectChange(node.getNode(), tree.getSelectionModel().isSelected(node.getNode()));
}
public void onJointChange(NodeDescriptor node, Tree.Joint joint) {
Element currJointEl = getJointContainer(node);
if (currJointEl == null) {
return;
}
Element jointContainer = tree.getPresentationRenderer().getJointContainer(joint);
getNodeContainer(node).insertFirst(jointContainer);
currJointEl.removeFromParent();
node.setJointContainerElement(jointContainer);
}
public void onLoadChange(NodeDescriptor node, boolean loading) {
if (node == null) {
return;
}
Element rootContainer = getNodeContainer(node);
if (loading) {
rootContainer.addClassName(tree.getTreeStyles().styles().loading());
} else {
rootContainer.removeClassName(tree.getTreeStyles().styles().loading());
}
}
public void onOverChange(NodeDescriptor node, boolean over) {
setClassName(getNodeContainer(node), tree.getTreeStyles().styles().hover(), over);
}
public void onSelectChange(Node node, boolean select) {
if (select) {
Node p = tree.getNodeStorage().getParent(node);
if (p != null) {
tree.setExpanded(tree.getNodeStorage().getParent(node), true);
}
}
NodeDescriptor nodeDescriptor = tree.getNodeDescriptor(node);
if (nodeDescriptor != null) {
Element e = getNodeContainer(nodeDescriptor);
if (e != null) {
setClassName(e, tree.getTreeStyles().styles().selected(), select);
}
}
}
public void onTextChange(NodeDescriptor node, SafeHtml text) {
Element textEl = getPresentableTextContainer(node);
if (textEl != null) {
textEl.setInnerHTML(Strings.isNullOrEmpty(text.asString()) ? " " : text.asString());
}
}
public void onInfoTextChange(NodeDescriptor node, String text) {
Element textEl = getInfoTextContainer(node);
if (textEl != null) {
textEl.setInnerHTML(Strings.isNullOrEmpty(text) ? " " : text);
}
}
public void setCacheSize(int cacheSize) {
this.cacheSize = cacheSize;
}
public void setCleanDelay(int cleanDelay) {
this.cleanDelay = cleanDelay;
}
public void setScrollDelay(int scrollDelay) {
this.scrollDelay = scrollDelay;
}
protected int getCalculatedRowHeight() {
return 20;
}
protected int getIndenting(NodeDescriptor node) {
return 18;
}
protected void onMouseOut(NativeEvent ce) {
if (over != null) {
onOverChange(over, false);
over = null;
}
}
protected void onMouseOver(NativeEvent ne) {
NodeDescriptor nodeDescriptor = tree.getNodeDescriptor((Element)ne.getEventTarget().cast());
if (nodeDescriptor != null) {
if (over != nodeDescriptor) {
onMouseOut(ne);
over = nodeDescriptor;
onOverChange(over, true);
}
}
}
private void setClassName(Element element, String cls, boolean add) {
if (element == null) {
return;
}
if (add) {
element.addClassName(cls);
} else {
element.removeClassName(cls);
}
}
private boolean isJointElement(Element element) {
if (element instanceof SVGSVGElement) {
SVGSVGElement joint = (SVGSVGElement)element;
return joint.getClassList().contains(tree.getTreeStyles().styles().joint());
}
return false;
}
}