diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/cview/CViewContentProvider.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/cview/CViewContentProvider.java
index 3c78cf1732c..9ecf1b8ffa3 100644
--- a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/cview/CViewContentProvider.java
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/cview/CViewContentProvider.java
@@ -22,35 +22,63 @@ import org.eclipse.cdt.core.model.ICElement;
import org.eclipse.cdt.core.model.ICProject;
import org.eclipse.cdt.core.model.IIncludeReference;
import org.eclipse.cdt.core.model.ILibraryReference;
+import org.eclipse.cdt.internal.ui.util.RemoteTreeContentManager;
+import org.eclipse.cdt.internal.ui.util.RemoteTreeViewer;
import org.eclipse.cdt.ui.CElementContentProvider;
import org.eclipse.core.resources.IContainer;
import org.eclipse.core.runtime.IPath;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.ui.IWorkbenchPartSite;
/**
* CViewContentProvider
*/
public class CViewContentProvider extends CElementContentProvider {
+ private RemoteTreeContentManager fManager;
+
/**
*
*/
- public CViewContentProvider() {
+ public CViewContentProvider(TreeViewer viewer, IWorkbenchPartSite site) {
super();
+ fManager = createContentManager(viewer, site);
}
/**
* @param provideMembers
* @param provideWorkingCopy
*/
- public CViewContentProvider(boolean provideMembers, boolean provideWorkingCopy) {
+ public CViewContentProvider(TreeViewer viewer, IWorkbenchPartSite site, boolean provideMembers, boolean provideWorkingCopy) {
super(provideMembers, provideWorkingCopy);
+ fManager = createContentManager(viewer, site);
+ }
+
+ protected RemoteTreeContentManager createContentManager(TreeViewer viewer, IWorkbenchPartSite site) {
+ if (site == null) {
+ return new RemoteTreeContentManager(this, (RemoteTreeViewer)viewer, null);
+ }
+ return new RemoteTreeContentManager(this, (RemoteTreeViewer)viewer, site);
}
-
/* (non-Javadoc)
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
public Object[] getChildren(Object element) {
- Object[] objs = super.getChildren(element);
+ Object[] objs = null;
+
+ // use the the deferred manager for some cases
+ if (element instanceof IBinary) {
+ // It takes sometimes to parse binaries deferred it
+ objs = fManager.getChildren(element);
+ } else if (element instanceof IArchive) {
+ // It takes sometimes to parse archives deferred it
+ objs = fManager.getChildren(element);
+ }
+
+ if (objs == null) {
+ objs = super.getChildren(element);
+ }
Object[] extras = null;
try {
if (element instanceof ICProject) {
@@ -170,7 +198,11 @@ public class CViewContentProvider extends CElementContentProvider {
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
public boolean hasChildren(Object element) {
- if (element instanceof IBinaryContainer) {
+ if (element instanceof IBinary) {
+ return fManager.mayHaveChildren(element);
+ } else if (element instanceof IArchive) {
+ return fManager.mayHaveChildren(element);
+ } else if (element instanceof IBinaryContainer) {
try {
IBinaryContainer cont = (IBinaryContainer)element;
IBinary[] bins = getBinaries(cont);
@@ -199,4 +231,23 @@ public class CViewContentProvider extends CElementContentProvider {
}
return super.hasChildren(element);
}
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#dispose()
+ */
+ public void dispose() {
+ if (fManager != null) {
+ fManager.cancel();
+ }
+ super.dispose();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, java.lang.Object, java.lang.Object)
+ */
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ fManager.cancel();
+ super.inputChanged(viewer, oldInput, newInput);
+ }
+
}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/PendingUpdateAdapter.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/PendingUpdateAdapter.java
new file mode 100644
index 00000000000..6a250234cc8
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/PendingUpdateAdapter.java
@@ -0,0 +1,84 @@
+/*******************************************************************************
+ * Copyright (c) 2003, 2005 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.cdt.internal.ui.util;
+
+import org.eclipse.core.runtime.IAdaptable;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.ui.model.IWorkbenchAdapter;
+
+/**
+ * The PendingUpdateAdapter is a convenience object that can be used
+ * by a BaseWorkbenchContentProvider that wants to show a pending update.
+ */
+public class PendingUpdateAdapter implements IWorkbenchAdapter, IAdaptable {
+
+ boolean removed = false;
+
+ /**
+ * Return whether or not this has been removed from the tree.
+ * @return boolean
+ */
+ public boolean isRemoved() {
+ return removed;
+ }
+
+ /**
+ * Set whether or not this has been removed from the tree.
+ * @param removedValue boolean
+ */
+ public void setRemoved(boolean removedValue) {
+ this.removed = removedValue;
+ }
+
+ /**
+ * Create a new instance of the receiver.
+ */
+ public PendingUpdateAdapter() {
+ //No initial behavior
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.IAdaptable#getAdapter(java.lang.Class)
+ */
+ public Object getAdapter(Class adapter) {
+ if (adapter == IWorkbenchAdapter.class)
+ return this;
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getChildren(java.lang.Object)
+ */
+ public Object[] getChildren(Object o) {
+ return new Object[0];
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getImageDescriptor(java.lang.Object)
+ */
+ public ImageDescriptor getImageDescriptor(Object object) {
+ return null;
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getLabel(java.lang.Object)
+ */
+ public String getLabel(Object o) {
+ return "Pending"; //$NON-NLS-1$
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.model.IWorkbenchAdapter#getParent(java.lang.Object)
+ */
+ public Object getParent(Object o) {
+ return null;
+ }
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeContentManager.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeContentManager.java
new file mode 100644
index 00000000000..9d35aa314a5
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeContentManager.java
@@ -0,0 +1,357 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.cdt.internal.ui.util;
+
+import java.util.ArrayList;
+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.ITreeContentProvider;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.ui.IWorkbenchPartSite;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.progress.IDeferredWorkbenchAdapter;
+import org.eclipse.ui.progress.IElementCollector;
+import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
+import org.eclipse.ui.progress.WorkbenchJob;
+
+/**
+ * A remote content manager that merges content into a tree rather then replacing
+ * its children with a "pending" node, and then the real children when they are available.
+ * This avoids collapsing the viewer when a refresh is performed. This implementation is
+ * currently tied to the RemoteTreeViewer
.
+ *
+ * @since 3.1
+ */
+public class RemoteTreeContentManager {
+ private RemoteTreeViewer fViewer;
+ private IWorkbenchSiteProgressService progressService;
+ private ITreeContentProvider fProvider;
+
+ /**
+ * Job to fetch children
+ */
+ private Job fFetchJob = new FetchJob();
+
+ /**
+ * Queue of parents to fetch children for, and
+ * associated element collectors and deferred adapters.
+ */
+ private List fElementQueue = new ArrayList();
+ private List fCollectors = new ArrayList();
+ private List fAdapaters = new ArrayList();
+
+ /**
+ * Fetching children is done in a single background job.
+ * This makes fetching single threaded/serial per view.
+ */
+ class FetchJob extends Job {
+
+ public FetchJob() {
+ super("FetchJob"); //$NON-NLS-1$
+ setSystem(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.core.runtime.jobs.Job#run(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ protected IStatus run(IProgressMonitor monitor) {
+ while (!fElementQueue.isEmpty() && !monitor.isCanceled()) {
+ Object element = null;
+ IElementCollector collector = null;
+ IDeferredWorkbenchAdapter adapter = null;
+ synchronized (fElementQueue) {
+ // could have been cancelled after entering the while loop
+ if (fElementQueue.isEmpty()) {
+ return Status.CANCEL_STATUS;
+ }
+ element = fElementQueue.remove(0);
+ collector = (IElementCollector) fCollectors.remove(0);
+ adapter = (IDeferredWorkbenchAdapter) fAdapaters.remove(0);
+ }
+ adapter.fetchDeferredChildren(element, collector, monitor);
+ }
+ if (monitor.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ return Status.OK_STATUS;
+ }
+
+ }
+
+ /**
+ * Element collector
+ */
+ public class Collector implements IElementCollector {
+ // number of children added to the tree
+ int offset = 0;
+ Object fParent;
+
+ public Collector(Object parent) {
+ fParent = parent;
+ }
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object, org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void add(Object element, IProgressMonitor monitor) {
+ add(new Object[] { element }, monitor);
+ }
+
+ /*
+ * (non-Javadoc)
+ * @see org.eclipse.jface.progress.IElementCollector#add(java.lang.Object[], org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public void add(Object[] elements, IProgressMonitor monitor) {
+ Object[] filtered = fViewer.filter(elements);
+ if (filtered.length > 0) {
+ replaceChildren(fParent, filtered, offset, monitor);
+ offset = offset + filtered.length;
+ }
+ }
+
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.jface.progress.IElementCollector#done()
+ */
+ public void done() {
+ prune(fParent, offset);
+ }
+ }
+
+ /**
+ * Contructs a new content manager.
+ *
+ * @param provider content provider
+ * @param viewer viewer
+ * @param site part site
+ */
+ public RemoteTreeContentManager(ITreeContentProvider provider, RemoteTreeViewer viewer, IWorkbenchPartSite site) {
+ fViewer = viewer;
+ fProvider = provider;
+ if (site != null) {
+ Object siteService = site.getAdapter(IWorkbenchSiteProgressService.class);
+ if (siteService != null) {
+ progressService = (IWorkbenchSiteProgressService) siteService;
+ }
+ }
+ }
+
+ /**
+ * Create the element collector for the receiver.
+ *@param parent
+ * The parent object being filled in,
+ * @param placeholder
+ * The adapter that will be used to indicate that results are
+ * pending, possibly null
+ * @return IElementCollector
+ */
+ protected IElementCollector createElementCollector(Object parent, PendingUpdateAdapter placeholder) {
+ return new Collector(parent);
+ }
+
+ /**
+ * Returns the child elements of the given element, or in the case of a
+ * deferred element, returns a placeholder. If a deferred element is used, a
+ * job is created to fetch the children in the background.
+ *
+ * @param parent
+ * The parent object.
+ * @return Object[] or null
if parent is not an instance of
+ * IDeferredWorkbenchAdapter.
+ */
+ public Object[] getChildren(final Object parent) {
+ IDeferredWorkbenchAdapter element = getAdapter(parent);
+ if (element == null)
+ return null;
+ Object[] currentChildren = fViewer.getCurrentChildren(parent);
+ PendingUpdateAdapter placeholder = null;
+ if (currentChildren == null || currentChildren.length == 0) {
+ placeholder = new PendingUpdateAdapter();
+ }
+ startFetchingDeferredChildren(parent, element, placeholder);
+ if (placeholder == null) {
+ return currentChildren;
+ }
+ return new Object[] { placeholder };
+ }
+
+ /**
+ * Create a UIJob to replace the children of the parent in the tree viewer.
+ *
+ * @param parent the parent for which children are to be replaced
+ * @param children the replacement children
+ * @param offset the offset at which to start replacing children
+ * @param monitor progress monitor
+ */
+ protected void replaceChildren(final Object parent, final Object[] children, final int offset, IProgressMonitor monitor) {
+ if (monitor.isCanceled()) {
+ return;
+ }
+ WorkbenchJob updateJob = new WorkbenchJob("IncrementalDeferredTreeContentManager") { //$NON-NLS-1$
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus runInUIThread(IProgressMonitor updateMonitor) {
+ //Cancel the job if the tree viewer got closed
+ if (fViewer.getControl().isDisposed())
+ return Status.CANCEL_STATUS;
+ fViewer.replace(parent, children, offset);
+ return Status.OK_STATUS;
+ }
+ };
+ updateJob.setSystem(true);
+ updateJob.setPriority(Job.INTERACTIVE);
+ updateJob.schedule();
+ }
+
+ /**
+ * Create a UIJob to prune the children of the parent in the tree viewer, starting
+ * at the given offset.
+ *
+ * @param parent the parent for which children should be pruned
+ * @param offset the offset at which children should be pruned. All children at and after
+ * this index will be removed from the tree.
+ * @param monitor
+ */
+ protected void prune(final Object parent, final int offset) {
+ WorkbenchJob updateJob = new WorkbenchJob("DeferredTree") { //$NON-NLS-1$
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus runInUIThread(IProgressMonitor updateMonitor) {
+ //Cancel the job if the tree viewer got closed
+ if (fViewer.getControl().isDisposed())
+ return Status.CANCEL_STATUS;
+ fViewer.prune(parent, offset);
+ return Status.OK_STATUS;
+ }
+ };
+ updateJob.setSystem(true);
+ updateJob.setPriority(Job.INTERACTIVE);
+ updateJob.schedule();
+ }
+
+ /**
+ * Run a job to clear the placeholder. This is used when the update
+ * for the tree is complete so that the user is aware that no more
+ * updates are pending.
+ *
+ * @param placeholder
+ */
+ protected void runClearPlaceholderJob(final PendingUpdateAdapter placeholder) {
+ if (placeholder == null || placeholder.isRemoved() || !PlatformUI.isWorkbenchRunning())
+ return;
+ //Clear the placeholder if it is still there
+ WorkbenchJob clearJob = new WorkbenchJob("DeferredTreeContentManager_ClearJob") { //$NON-NLS-1$
+ /*
+ * (non-Javadoc)
+ *
+ * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (!placeholder.isRemoved()) {
+ Control control = fViewer.getControl();
+ if (control.isDisposed())
+ return Status.CANCEL_STATUS;
+ fViewer.remove(placeholder);
+ placeholder.setRemoved(true);
+ }
+ return Status.OK_STATUS;
+ }
+ };
+ clearJob.setSystem(true);
+ clearJob.schedule();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.progress.DeferredTreeContentManager#getFetchJobName(java.lang.Object, org.eclipse.ui.progress.IDeferredWorkbenchAdapter)
+ */
+ protected String getFetchJobName(Object parent, IDeferredWorkbenchAdapter adapter) {
+ return "RemoteTreeContentManager"; //$NON-NLS-1$
+ }
+
+
+ /**
+ * Return the IDeferredWorkbenchAdapter for element or the element if it is
+ * an instance of IDeferredWorkbenchAdapter. If it does not exist return
+ * null.
+ *
+ * @param element
+ * @return IDeferredWorkbenchAdapter or null
+ */
+ protected IDeferredWorkbenchAdapter getAdapter(Object element) {
+ if (element instanceof IDeferredWorkbenchAdapter)
+ return (IDeferredWorkbenchAdapter) element;
+ if (!(element instanceof IAdaptable))
+ return null;
+ Object adapter = ((IAdaptable) element)
+ .getAdapter(IDeferredWorkbenchAdapter.class);
+ if (adapter == null)
+ return null;
+ return (IDeferredWorkbenchAdapter) adapter;
+ }
+
+ protected void startFetchingDeferredChildren(final Object parent, final IDeferredWorkbenchAdapter adapter, PendingUpdateAdapter placeholder) {
+ final IElementCollector collector = createElementCollector(parent, placeholder);
+ synchronized (fElementQueue) {
+ if (!fElementQueue.contains(parent)) {
+ fElementQueue.add(parent);
+ fCollectors.add(collector);
+ fAdapaters.add(adapter);
+ }
+ }
+ if (progressService == null)
+ fFetchJob.schedule();
+ else
+ progressService.schedule(fFetchJob);
+ }
+
+ /**
+ * Provides an optimized lookup for determining if an element has children.
+ * This is required because elements that are populated lazilly can't
+ * answer getChildren
just to determine the potential for
+ * children. Throw an AssertionFailedException if element is null.
+ *
+ * @param element The Object being tested. This should not be
+ * null
.
+ * @return boolean true
if there are potentially children.
+ * @throws RuntimeException if the element is null.
+ */
+ public boolean mayHaveChildren(Object element) {
+ //Assert.isNotNull(element, ProgressMessages.DeferredTreeContentManager_NotDeferred);
+ IDeferredWorkbenchAdapter adapter = getAdapter(element);
+ return adapter != null && adapter.isContainer();
+ }
+
+ /**
+ * Cancels any content this provider is currently fetching.
+ */
+ public void cancel() {
+ synchronized (fElementQueue) {
+ fFetchJob.cancel();
+ fElementQueue.clear();
+ fAdapaters.clear();
+ fCollectors.clear();
+ }
+ }
+
+}
diff --git a/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeViewer.java b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeViewer.java
new file mode 100644
index 00000000000..6ae497d2796
--- /dev/null
+++ b/core/org.eclipse.cdt.ui/src/org/eclipse/cdt/internal/ui/util/RemoteTreeViewer.java
@@ -0,0 +1,431 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2005 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.cdt.internal.ui.util;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.cdt.core.model.ITranslationUnit;
+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.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 ProblemTreeViewer {
+ private ExpansionJob fExpansionJob = null;
+ private SelectionJob fSelectionJob = null;
+
+
+ class ExpansionJob extends UIJob {
+
+ private Object element;
+ private List parents = new ArrayList(); // top down
+
+ /**
+ * Constucts a job to expand the given element.
+ *
+ * @param target the element to expand
+ */
+ public ExpansionJob() {
+ super("Expansion"); //$NON-NLS-1$
+ setPriority(Job.INTERACTIVE);
+ setSystem(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (getControl().isDisposed() || element == null) {
+ return Status.OK_STATUS;
+ }
+ synchronized (RemoteTreeViewer.this) {
+ boolean allParentsExpanded = true;
+ Iterator 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);
+ }
+
+ }
+
+ class SelectionJob extends UIJob {
+
+ private IStructuredSelection selection;
+ private Object first;
+ private List parents = new ArrayList(); // top down
+
+ /**
+ * Constucts a job to select the given element.
+ *
+ * @param target the element to select
+ */
+ public SelectionJob() {
+ super("Selection"); //$NON-NLS-1$
+ setPriority(Job.INTERACTIVE);
+ setSystem(true);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor)
+ */
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (getControl().isDisposed() || selection == null) {
+ return Status.OK_STATUS;
+ }
+ synchronized (RemoteTreeViewer.this) {
+ boolean allParentsExpanded = true;
+ Iterator 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() {
+ 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);
+ }
+ }
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.AbstractTreeViewer#add(java.lang.Object, java.lang.Object)
+ */
+ public synchronized void add(Object parentElement, Object childElement) {
+ super.add(parentElement, childElement);
+ runDeferredUpdates();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.AbstractTreeViewer#add(java.lang.Object, java.lang.Object[])
+ */
+ public synchronized void add(Object parentElement, Object[] childElements) {
+ super.add(parentElement, childElements);
+ runDeferredUpdates();
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.AbstractTreeViewer#remove(java.lang.Object)
+ */
+ public synchronized void remove(Object element) {
+ validateDeferredUpdates(element);
+ super.remove(element);
+ }
+
+ /* (non-Javadoc)
+ * @see org.eclipse.jface.viewers.AbstractTreeViewer#remove(java.lang.Object[])
+ */
+ public synchronized void remove(Object[] elements) {
+ for (int i = 0; i < elements.length; i++) {
+ validateDeferredUpdates(elements[i]);
+ }
+ 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 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);
+ if (!(parent instanceof ITranslationUnit))
+ addAllParents(list, parent);
+ }
+ }
+ }
+ }
+
+ 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() {
+ 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() {
+ 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();
+ }
+ });
+ }
+
+
+}