blob: f21f71609cbf1c0cdccb9a66b9a3128d402e5b47 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2000, 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
*******************************************************************************/
package org.eclipse.remote.internal.ui;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.eclipse.core.runtime.IAdaptable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.TreeViewer;
import org.eclipse.swt.events.DisposeEvent;
import org.eclipse.swt.events.DisposeListener;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Item;
import org.eclipse.swt.widgets.Tree;
import org.eclipse.swt.widgets.TreeItem;
import org.eclipse.swt.widgets.Widget;
import org.eclipse.ui.model.IWorkbenchAdapter;
import org.eclipse.ui.progress.UIJob;
public class RemoteTreeViewer extends TreeViewer {
private ExpansionJob fExpansionJob = null;
private SelectionJob fSelectionJob = null;
private class ExpansionJob extends UIJob {
private Object element;
private final List<Object> parents = new ArrayList<Object>(); // top down
/**
* Constructs a job to expand the given element.
*
*/
public ExpansionJob() {
super("Expansion"); //$NON-NLS-1$
setPriority(Job.INTERACTIVE);
setSystem(true);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (getControl().isDisposed() || element == null) {
return Status.OK_STATUS;
}
synchronized (RemoteTreeViewer.this) {
boolean allParentsExpanded = true;
Iterator<Object> iterator = parents.iterator();
while (iterator.hasNext() && !monitor.isCanceled()) {
Object parent = iterator.next();
Widget item = findItem(parent);
if (item != null) {
expandToLevel(parent, 1);
} else {
allParentsExpanded = false;
break;
}
}
if (allParentsExpanded) {
Widget item = findItem(element);
if (item != null) {
if (isExpandable(element)) {
expandToLevel(element, 1);
}
element = null;
parents.clear();
return Status.OK_STATUS;
}
}
return Status.OK_STATUS;
}
}
public void validate(Object object) {
if (element != null) {
if (element.equals(object) || parents.contains(object)) {
cancel();
element = null;
}
}
}
public void setDeferredExpansion(Object toExpand) {
element = toExpand;
parents.clear();
addAllParents(parents, element);
}
}
private class SelectionJob extends UIJob {
private IStructuredSelection selection;
private Object first;
private final List<Object> parents = new ArrayList<Object>(); // top down
/**
* Constucts a job to select the given element.
*/
public SelectionJob() {
super("Selection"); //$NON-NLS-1$
setPriority(Job.INTERACTIVE);
setSystem(true);
}
@Override
public IStatus runInUIThread(IProgressMonitor monitor) {
if (getControl().isDisposed() || selection == null) {
return Status.OK_STATUS;
}
synchronized (RemoteTreeViewer.this) {
boolean allParentsExpanded = true;
Iterator<Object> iterator = parents.iterator();
while (iterator.hasNext() && !monitor.isCanceled()) {
Object parent = iterator.next();
Widget item = findItem(parent);
if (item != null) {
expandToLevel(parent, 1);
} else {
allParentsExpanded = false;
break;
}
}
if (allParentsExpanded) {
if (findItem(first) != null) {
setSelection(selection, true);
selection = null;
first = null;
parents.clear();
return Status.OK_STATUS;
}
}
return Status.OK_STATUS;
}
}
public void setDeferredSelection(IStructuredSelection sel) {
selection = sel;
first = selection.getFirstElement();
parents.clear();
addAllParents(parents, first);
}
public void validate(Object object) {
if (first != null) {
if (first.equals(object) || parents.contains(object)) {
cancel();
selection = null;
}
}
}
}
/**
* Constructs a remote tree viewer parented by the given composite.
*
* @param parent
* parent composite
*/
public RemoteTreeViewer(Composite parent) {
super(parent);
addDisposeListener();
fExpansionJob = new ExpansionJob();
fSelectionJob = new SelectionJob();
}
/**
* Constructs a remote tree viewer parented by the given composite
* with the given style.
*
* @param parent
* parent composite
* @param style
* style bits
*/
public RemoteTreeViewer(Composite parent, int style) {
super(parent, style);
addDisposeListener();
fExpansionJob = new ExpansionJob();
fSelectionJob = new SelectionJob();
}
/**
* Constructs a remote tree viewer with the given tree.
*
* @param tree
* tree widget
*/
public RemoteTreeViewer(Tree tree) {
super(tree);
addDisposeListener();
fExpansionJob = new ExpansionJob();
fSelectionJob = new SelectionJob();
}
private void addDisposeListener() {
getControl().addDisposeListener(new DisposeListener() {
@Override
public void widgetDisposed(DisposeEvent e) {
cancelJobs();
}
});
}
protected void runDeferredUpdates() {
if (fExpansionJob != null) {
fExpansionJob.schedule();
}
if (fSelectionJob != null) {
fSelectionJob.schedule();
}
}
/**
* The given element is being removed from the tree. Cancel
* any deferred updates for the element.
*
* @param element
*/
protected void validateDeferredUpdates(Object element) {
if (element != null) {
if (fExpansionJob != null) {
fExpansionJob.validate(element);
}
if (fSelectionJob != null) {
fSelectionJob.validate(element);
}
}
}
@Override
public synchronized void add(Object parentElement, Object childElement) {
super.add(parentElement, childElement);
runDeferredUpdates();
}
@Override
public synchronized void add(Object parentElement, Object... childElements) {
super.add(parentElement, childElements);
runDeferredUpdates();
}
@Override
public synchronized void remove(Object element) {
validateDeferredUpdates(element);
super.remove(element);
}
@Override
public synchronized void remove(Object... elements) {
for (Object element : elements) {
validateDeferredUpdates(element);
}
super.remove(elements);
}
/**
* Cancels any deferred updates currently scheduled/running.
*/
public void cancelJobs() {
cancel(fSelectionJob);
cancel(fExpansionJob);
}
public synchronized void deferExpansion(Object element) {
TreeItem treeItem = (TreeItem) findItem(element);
if (treeItem == null) {
fExpansionJob.setDeferredExpansion(element);
fExpansionJob.schedule();
} else {
if (!getExpanded(treeItem)) {
fExpansionJob.setDeferredExpansion(element);
fExpansionJob.schedule();
}
}
}
public synchronized void deferSelection(IStructuredSelection selection) {
if (fSelectionJob == null) {
fSelectionJob = new SelectionJob();
}
fSelectionJob.setDeferredSelection(selection);
fSelectionJob.schedule();
}
public IStructuredSelection getDeferredSelection() {
if (fSelectionJob != null) {
return fSelectionJob.selection;
}
return null;
}
private void cancel(Job job) {
if (job != null) {
job.cancel();
}
}
private void addAllParents(List<Object> list, Object element) {
if (element instanceof IAdaptable) {
IAdaptable adaptable = (IAdaptable) element;
IWorkbenchAdapter adapter = (IWorkbenchAdapter) adaptable.getAdapter(IWorkbenchAdapter.class);
if (adapter != null) {
Object parent = adapter.getParent(element);
if (parent != null) {
list.add(0, parent);
addAllParents(list, parent);
}
}
}
}
@Override
public Object[] filter(Object[] elements) {
return super.filter(elements);
}
public Object[] getCurrentChildren(Object parent) {
Widget widget = findItem(parent);
if (widget != null) {
Item[] items = getChildren(widget);
Object[] children = new Object[items.length];
for (int i = 0; i < children.length; i++) {
Object data = items[i].getData();
if (data == null) {
return null;
}
children[i] = data;
}
return children;
}
return null;
}
public synchronized void prune(final Object parent, final int offset) {
Widget widget = findItem(parent);
if (widget != null) {
final Item[] currentChildren = getChildren(widget);
if (offset < currentChildren.length) {
preservingSelection(new Runnable() {
@Override
public void run() {
for (int i = offset; i < currentChildren.length; i++) {
if (currentChildren[i].getData() != null) {
disassociate(currentChildren[i]);
}
currentChildren[i].dispose();
}
}
});
}
}
}
public synchronized void replace(final Object parent, final Object[] children, final int offset) {
preservingSelection(new Runnable() {
@Override
public void run() {
Widget widget = findItem(parent);
if (widget == null) {
add(parent, children);
} else {
Item[] currentChildren = getChildren(widget);
int pos = offset;
if (pos >= currentChildren.length) {
// append
add(parent, children);
} else {
// replace
for (int i = 0; i < children.length; i++) {
Object child = children[i];
if (pos < currentChildren.length) {
// replace
Item item = currentChildren[pos];
Object data = item.getData();
if (!child.equals(data)) {
// no need to cancel pending updates here, the child may have shifted up/down
internalRefresh(item, child, true, true);
} else {
// If it's the same child, the label/content may still have changed
doUpdateItem(item, child);
updatePlus(item, child);
}
} else {
// add
int numLeft = children.length - i;
if (numLeft > 1) {
Object[] others = new Object[numLeft];
System.arraycopy(children, i, others, 0, numLeft);
add(parent, others);
} else {
add(parent, child);
}
break;
}
pos++;
}
}
}
runDeferredUpdates();
}
});
}
}