blob: 39ce156558186e43478dbfffcc257d6f7e3af97a [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2006, 2008 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
* Stefan Xenos, IBM - initial API and implementation
*******************************************************************************/
package org.eclipse.jface.internal.databinding.provisional.viewers;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import org.eclipse.core.databinding.observable.IStaleListener;
import org.eclipse.core.databinding.observable.Observables;
import org.eclipse.core.databinding.observable.StaleEvent;
import org.eclipse.core.databinding.observable.set.IObservableSet;
import org.eclipse.core.databinding.observable.set.ISetChangeListener;
import org.eclipse.core.databinding.observable.set.SetChangeEvent;
import org.eclipse.core.databinding.observable.set.SetDiff;
import org.eclipse.jface.databinding.viewers.ObservableListTreeContentProvider;
import org.eclipse.jface.databinding.viewers.ObservableSetTreeContentProvider;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.widgets.Control;
/**
*
* @since 1.0
* @deprecated Use {@link ObservableSetTreeContentProvider} or
* {@link ObservableListTreeContentProvider} instead.
*/
/* package */ class TreeNode implements ISetChangeListener, IStaleListener {
private UnorderedTreeContentProvider contentProvider;
private Object element;
// Stores the set of parents (null if there are less than 2)
private HashSet parents = null;
// Stores one representative parent. If there is more than one parent,
// the complete set of parents can be found in the parents set.
Object parent;
/**
* Set of child elements.
*/
private IObservableSet children;
private boolean hasPendingNode = false;
private boolean isStale;
private boolean listeningToChildren = false;
private boolean prefetchEnqueued = false;
/**
* @param element
* @param cp
*/
public TreeNode(Object element, UnorderedTreeContentProvider cp) {
this.element = element;
this.contentProvider = cp;
children = contentProvider.createChildSet(element);
if (children == null) {
children = Observables.emptyObservableSet();
listeningToChildren = true;
}
hasPendingNode = children.isStale();
}
/**
* @param parent
*/
public void addParent(Object parent) {
if (this.parent == null) {
this.parent = parent;
} else {
if (parent.equals(this.parent)) {
return;
}
if (parents == null) {
parents = new HashSet();
parents.add(this.parent);
}
parents.add(parent);
}
}
/**
* @param parent
*/
public void removeParent(Object parent) {
if (this.parents != null) {
parents.remove(parent);
}
if (parent == this.parent) {
if (parents == null || parents.isEmpty()) {
this.parent = null;
} else {
this.parent = parents.iterator().next();
}
}
if (this.parents != null && this.parents.size() <= 1) {
this.parents = null;
}
}
/**
* Returns the set of children for this node. If new children are discovered later, they
* will be added directly to the viewer.
*
* @return TODO
*/
public Set getChildren() {
if (!listeningToChildren) {
listeningToChildren = true;
children.addSetChangeListener(this);
hasPendingNode = children.isEmpty() && children.isStale();
children.addStaleListener(this);
updateStale();
}
// If the child set is stale and empty, show the "pending" node
if (hasPendingNode) {
Object pendingNode = contentProvider.getPendingNode();
return Collections.singleton(pendingNode);
}
return children;
}
/**
* @return TODO
*/
public IObservableSet getChildrenSet() {
return children;
}
private void updateStale() {
boolean willBeStale = children.isStale();
if (willBeStale != isStale) {
isStale = willBeStale;
contentProvider.changeStale(isStale? 1 : -1);
}
}
/**
* @return TODO
*/
public boolean isStale() {
return isStale;
}
/**
* Returns true if the viewer should show a plus sign for expanding this
* node.
*
* @return TODO
*/
public boolean shouldShowPlus() {
if (children == null) {
// if (!hasPendingNode) {
// hasPendingNode = true;
// contentProvider.add(element, Collections.singleton(contentProvider.getPendingNode()));
// }
return true;
}
if (!listeningToChildren && !prefetchEnqueued) {
prefetchEnqueued = true;
contentProvider.enqueuePrefetch(this);
}
return !listeningToChildren || hasPendingNode || !children.isEmpty();
}
/**
* Disposes this node and removes all remaining children.
*/
public void dispose() {
if (children != null) {
if (listeningToChildren) {
contentProvider.remove(element, children, true);
children.removeSetChangeListener(this);
children.removeStaleListener(this);
}
children.dispose();
children = null;
if (listeningToChildren && isStale) {
contentProvider.changeStale(-1);
}
}
}
/**
* @return TODO
*/
public boolean isDisposed() {
return children == null;
}
/**
* Returns one representative parent, or null if this node is unparented. Use
* getParents() to get the complete set of known parents.
*
* @return TODO
*/
public Object getParent() {
return parent;
}
/**
*
* @return the set of all known parents for this node
*/
public Set getParents() {
if (parents == null) {
if (parent == null) {
return Collections.EMPTY_SET;
}
return Collections.singleton(parent);
}
return parents;
}
/**
* Called when the child set changes. Should not be called directly by the viewer.
*/
public void handleSetChange(SetChangeEvent event) {
SetDiff diff = event.diff;
TreeViewer viewer = this.contentProvider.getViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control != null) {
if (control.isDisposed()) {
// If the widgetry was disposed without notifying the content provider, then
// dispose the content provider now and stop processing events.
contentProvider.dispose();
return;
}
}
}
boolean shouldHavePendingNode = children.isEmpty() && children.isStale();
Set additions = diff.getAdditions();
// Check if we should add the pending node
if (shouldHavePendingNode && !hasPendingNode) {
HashSet newAdditions = new HashSet();
newAdditions.addAll(additions);
newAdditions.add(contentProvider.getPendingNode());
additions = newAdditions;
hasPendingNode = true;
}
Set removals = diff.getRemovals();
// Check if we should remove the pending node
if (!shouldHavePendingNode && hasPendingNode) {
HashSet newRemovals = new HashSet();
newRemovals.addAll(removals);
newRemovals.add(contentProvider.getPendingNode());
removals = newRemovals;
hasPendingNode = false;
}
if (!additions.isEmpty()) {
contentProvider.add(element, additions);
}
if (!removals.isEmpty()) {
contentProvider.remove(element, removals, children.isEmpty() && !hasPendingNode);
}
updateStale();
}
public void handleStale(StaleEvent staleEvent) {
TreeViewer viewer = this.contentProvider.getViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control != null) {
if (control.isDisposed()) {
// If the widgetry was disposed without notifying the content provider, then
// dispose the content provider now and stop processing events.
contentProvider.dispose();
return;
}
}
}
boolean shouldHavePendingNode = children.isEmpty() && children.isStale();
// Check if we should add the pending node
if (shouldHavePendingNode && !hasPendingNode) {
hasPendingNode = shouldHavePendingNode;
contentProvider.add(element, Collections.singleton(contentProvider.getPendingNode()));
}
// Check if we should remove the pending node
if (!shouldHavePendingNode && hasPendingNode) {
hasPendingNode = shouldHavePendingNode;
contentProvider.remove(element, Collections.singleton(contentProvider.getPendingNode()), true);
}
updateStale();
}
/**
* @return TODO
*/
public Object getElement() {
return element;
}
/**
*
*/
public void prefetch() {
TreeViewer viewer = this.contentProvider.getViewer();
if (viewer != null) {
Control control = viewer.getControl();
if (control != null) {
if (control.isDisposed()) {
// If the widgetry has been disposed, then avoid sending anything
// to the viewer.
return;
}
}
}
Set children = getChildren();
if (!children.isEmpty()) {
contentProvider.add(element, children);
} else {
// We need to remove the + sign, and adding/removing elements won't do the trick
contentProvider.getViewer().refresh(element);
}
}
}