/*******************************************************************************
* Copyright (c) 2000, 2006 IBM Corporation and others.
* 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:
* IBM Corporation - initial API and implementation
* Darrell Meyer <darrell@mygwt.net> - derived implementation
* Tom Schindl <tom.schindl@bestsolution.at> - bugfix in issues: 40
*******************************************************************************/
package net.mygwt.ui.client.viewer;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import net.mygwt.ui.client.Events;
import net.mygwt.ui.client.event.BaseEvent;
import net.mygwt.ui.client.event.Listener;
import net.mygwt.ui.client.widget.Component;
import net.mygwt.ui.client.widget.tree.Tree;
import net.mygwt.ui.client.widget.tree.TreeItem;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.ui.Widget;
/**
* An interface to content providers for tree-structure-oriented viewers.
*
* <p>
* This code is based on JFace API from the Eclipse Project.
* </p>
*
* @see TreeViewer
*/
public class TreeViewer extends Viewer implements ICheckable {
private Tree tree;
private boolean async, checkable;
private ListenerList checkChangeListener;
/**
* Creates a new tree viewer instance.
*
* @param tree the underlying tree widget
*/
public TreeViewer(Tree tree) {
this.tree = tree;
hookWidget(tree);
tree.addListener(Events.BeforeExpand, new Listener() {
public void handleEvent(BaseEvent be) {
TreeItem item = (TreeItem) be.widget;
Object loaded = item.getData("loaded");
if (loaded == null) {
be.doit = false;
loadChildren(item, true);
}
}
});
}
/**
* Adds the given child element to this viewer as a child of the given parent
* element.
*
* @param parent the parent element
* @param child the child element
*/
public void add(Object parent, Object child) {
TreeItem p = (TreeItem) findItem(parent);
internalAdd(p, child, p.getItemCount());
}
public void addCheckStateListener(ICheckStateListener listener) {
if (checkChangeListener == null) {
checkChangeListener = new ListenerList();
}
checkChangeListener.add(listener);
}
public Widget findItem(Object elem) {
TreeItem[] items = tree.getAllItems();
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
if (item.getData() == elem) {
return item;
}
}
return null;
}
public boolean getChecked(Object element) {
TreeItem item = (TreeItem) findItem(element);
if (item != null) {
return item.isChecked();
}
return false;
}
/**
* Returns the viewer's tree widget.
*
* @return the tree
*/
public Tree getTree() {
return tree;
}
public Widget getWidget() {
return tree;
}
/**
* Inserts the given element as a new child element of the given parent
* element at the given position.
*
* @param parent the parent element
* @param child the child element
* @param position the insert position
*/
public void insert(Object parent, Object child, int position) {
TreeItem p = (TreeItem) findItem(parent);
internalAdd(p, child, position);
}
/**
* Refreshes this viewer starting with the given element.
*
* @param elem the element
*/
public void refresh(Object elem) {
TreeItem item = (TreeItem) findItem(elem);
if (item != null) {
if (item.getData("loaded") != null) {
int count = item.getItemCount();
for (int i = 0; i < count; i++) {
item.remove(item.getItem(0));
}
item.setData("loaded", null);
loadChildren(item, item.isExpanded());
}
}
}
/**
* Removes the given element from the viewer.
*
* @param element the element to be removed
*/
public void remove(Object element) {
TreeItem item = (TreeItem) findItem(element);
if (item != null) {
TreeItem parent = item.getParentItem();
parent.remove(item);
removeElement(item.getData());
item.setData(null);
}
}
public void removeCheckStateListener(ICheckStateListener listener) {
if (checkChangeListener != null) {
checkChangeListener.remove(listener);
}
}
/**
* Selects the elements.
*
* @param elem the element to be selected
*/
public void select(Object elem) {
TreeItem item = (TreeItem) findItem(elem);
tree.getSelectionModel().select(item);
}
public boolean setChecked(Object element, boolean state) {
TreeItem item = (TreeItem) findItem(element);
if (item != null) {
item.setChecked(state);
return true;
}
return false;
}
public void setContentProvider(IContentProvider contentProvider) {
super.setContentProvider(contentProvider);
if (contentProvider instanceof IAsyncTreeContentProvider) {
async = true;
}
if (contentProvider instanceof ICheckable) {
checkable = true;
}
}
/**
* Sets the expanded state for the element.
*
* @param element the element
* @param expanded the expand state
*/
public void setExpanded(Object element, boolean expanded) {
TreeItem item = (TreeItem) findItem(element);
if (item != null) {
item.setExpanded(expanded);
}
}
public void setSelection(ISelection selection, boolean reveal) {
List selected = selection.toList();
tree.getSelectionModel().deselectAll();
TreeItem[] items = tree.getAllItems();
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
Object elem = item.getData();
if (selected.contains(elem)) {
tree.getSelectionModel().select(item);
}
}
}
public void update() {
TreeItem[] items = tree.getAllItems();
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
updateInternal(item);
}
}
public void update(Object elem) {
TreeItem item = (TreeItem) findItem(elem);
if (item != null) {
updateInternal(item);
}
}
protected void add(Object elem) {
}
protected List getSelectedFromWidget() {
ArrayList elems = new ArrayList();
TreeItem[] items = tree.getSelection();
for (int i = 0; i < items.length; i++) {
TreeItem item = items[i];
elems.add(item.getData());
}
return elems;
}
protected void hookWidget(Component widget) {
super.hookWidget(widget);
widget.addListener(Events.CheckChange, new Listener() {
public void handleEvent(BaseEvent be) {
fireCheckStateChanged(be);
}
});
}
protected void insert(Object elem, int index) {
// do nothing
}
protected void onInputReceived(Object input) {
renderTree();
}
private Object[] filterChildren(Object parent, Object[] elems) {
List temp = new ArrayList();
for (int i = 0; i < elems.length; i++) {
if (isOrDecendantSelected(elems[i])) {
temp.add(elems[i]);
}
}
return temp.toArray();
}
private void fireCheckStateChanged(BaseEvent be) {
if (checkChangeListener != null) {
TreeItem item = (TreeItem) be.item;
CheckStateChangedEvent evt = new CheckStateChangedEvent(this, item.getData(),
item.isChecked());
Iterator it = checkChangeListener.iterator();
while (it.hasNext()) {
((ICheckStateListener) it.next()).checkStateChanged(evt);
}
}
}
private void internalAdd(TreeItem parent, Object elem, int position) {
// if the parent is not expanded force reload
if (!parent.isExpanded()) {
parent.setData("loaded", null);
parent.setLeaf(false);
parent.getUI().updateJoint();
return;
}
if (!isFiltered(parent, elem)) {
renderItem(parent, elem, position);
if (getSorter() != null) {
sortChildren(parent);
}
}
}
/**
* Returns <code>true</code> if the element or any child elements is not
* filetered.
*
* @param elem the element
* @return the select state
*/
private boolean isOrDecendantSelected(Object elem) {
if (!isFiltered(null, elem)) {
return true;
}
ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
Object[] children = cp.getChildren(elem);
for (int i = 0; i < children.length; i++) {
boolean result = isOrDecendantSelected(children[i]);
if (result) {
return true;
}
}
return false;
}
private void loadChildren(final TreeItem item, boolean expand) {
if (!async) {
ITreeContentProvider cp = (ITreeContentProvider) getContentProvider();
Object[] children = cp.getChildren(item.getData());
children = filterChildren(item, children);
sortElements(children);
for (int i = 0; i < children.length; i++) {
renderItem(item, children[i], i);
}
item.setData("loaded", "true");
item.setExpanded(expand);
} else {
IAsyncTreeContentProvider acp = (IAsyncTreeContentProvider) getContentProvider();
item.getUI().onLoadingChange(true);
acp.getChildren(item.getData(), new IAsyncContentCallback() {
public void setElements(Object[] children) {
item.getUI().onLoadingChange(false);
children = filterChildren(item, children);
sortElements(children);
for (int i = 0; i < children.length; i++) {
renderItem(item, children[i], -1);
}
item.setData("loaded", "true");
if (item.hasChildren()) {
item.setExpanded(true);
} else {
item.setLeaf(true);
item.getUI().updateJoint();
}
}
});
}
}
private void renderItem(TreeItem parent, Object elem, int position) {
boolean hasChildren = false;
if (async) {
hasChildren = ((IAsyncTreeContentProvider) getContentProvider()).hasChildren(elem);
} else {
hasChildren = ((ITreeContentProvider) getContentProvider()).hasChildren(elem);
}
ILabelProvider lp = (ILabelProvider) getLabelProvider();
TreeItem item = new TreeItem();
item.setData(elem);
item.setText(lp.getText(elem));
item.setIconStyle(lp.getIconStyle(elem));
item.setLeaf(!hasChildren);
if (checkable) {
item.setChecked(((ICheckable)getContentProvider()).getChecked(elem));
}
if (position == -1) {
parent.add(item);
} else {
parent.insert(item, position);
}
}
private void renderTree() {
TreeItem root = tree.getRootItem();
root.setData(input);
int count = root.getItemCount();
for (int i = 0; i < count; i++) {
root.remove(root.getItem(0));
}
Object[] elems = elements;
elems = sortElements(elems);
for (int i = 0; i < elems.length; i++) {
Object elem = elems[i];
if (getFilters().length > 0) {
if (isOrDecendantSelected(elem)) {
renderItem(root, elem, -1);
}
} else {
renderItem(root, elem, -1);
}
}
}
private void sortChildren(TreeItem parent) {
Object[] elems = new Object[parent.getItemCount()];
Element p = parent.getContainer();
for (int i = 0; i < elems.length; i++) {
TreeItem item = parent.getItem(i);
DOM.removeChild(p, item.getElement());
elems[i] = item.getData();
}
sortElements(elems);
for (int i = 0; i < elems.length; i++) {
TreeItem item = (TreeItem) findItem(elems[i]);
Element elem = item.getElement();
DOM.appendChild(p, elem);
}
}
private void updateInternal(TreeItem item) {
ILabelProvider lp = (ILabelProvider) getLabelProvider();
Object elem = item.getData();
item.setText(lp.getText(elem));
item.setIconStyle(lp.getIconStyle(elem));
}
}