diff options
author | Kevin Barnes | 2006-01-18 20:54:16 +0000 |
---|---|---|
committer | Kevin Barnes | 2006-01-18 20:54:16 +0000 |
commit | 8915ee74280e60250b975c4d990bab9f79e93ad0 (patch) | |
tree | 92fc6696715cec72ed898762231b65df542dda15 /org.eclipse.debug.ui | |
parent | b9c31085fdd20248d0d40a485bb91246e21a7455 (diff) | |
download | eclipse.platform.debug-8915ee74280e60250b975c4d990bab9f79e93ad0.tar.gz eclipse.platform.debug-8915ee74280e60250b975c4d990bab9f79e93ad0.tar.xz eclipse.platform.debug-8915ee74280e60250b975c4d990bab9f79e93ad0.zip |
Bug 123163 - AsynchronousTreeViewer should use SWT.VIRTUAL.
Diffstat (limited to 'org.eclipse.debug.ui')
10 files changed, 1122 insertions, 902 deletions
diff --git a/org.eclipse.debug.ui/.options b/org.eclipse.debug.ui/.options new file mode 100644 index 000000000..a2688151a --- /dev/null +++ b/org.eclipse.debug.ui/.options @@ -0,0 +1,2 @@ +org.eclipse.debug.ui/debug = false +org.eclipse.debug.ui/debug/viewer_cache_debug = false diff --git a/org.eclipse.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.debug.ui/META-INF/MANIFEST.MF index 3a6a3b5fd..550c37d6c 100644 --- a/org.eclipse.debug.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.ui/META-INF/MANIFEST.MF @@ -11,6 +11,8 @@ Export-Package: org.eclipse.debug.internal.ui;x-internal:=true, org.eclipse.debug.internal.ui.actions.breakpointGroups;x-internal:=true, org.eclipse.debug.internal.ui.actions.breakpoints, org.eclipse.debug.internal.ui.actions.context;x-internal:=true, + org.eclipse.debug.internal.ui.actions.expressions, + org.eclipse.debug.internal.ui.actions.variables, org.eclipse.debug.internal.ui.contexts;x-internal:=true, org.eclipse.debug.internal.ui.elements.adapters;x-internal:=true, org.eclipse.debug.internal.ui.importexport.breakpoints;x-internal:=true, @@ -49,3 +51,4 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.2.0,4.0.0)", org.eclipse.ui.editors;bundle-version="[3.2.0,4.0.0)", org.eclipse.core.runtime;bundle-version="[3.1.0,4.0.0)" Eclipse-LazyStart: true +Bundle-ClassPath: . diff --git a/org.eclipse.debug.ui/build.properties b/org.eclipse.debug.ui/build.properties index 10a4ee194..e17542238 100644 --- a/org.eclipse.debug.ui/build.properties +++ b/org.eclipse.debug.ui/build.properties @@ -13,7 +13,8 @@ bin.includes = icons/,\ plugin.properties,\ .,\ about.html,\ - META-INF/ + META-INF/,\ + .options source.. = ui/ src.includes = about.html,\ diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewer.java index 2207d546b..1e873e685 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewer.java @@ -22,8 +22,10 @@ import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.util.Assert; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.StructuredSelection; +import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.jface.viewers.ViewerSorter; import org.eclipse.swt.SWT; import org.eclipse.swt.events.MouseEvent; @@ -35,8 +37,11 @@ import org.eclipse.swt.graphics.Font; import org.eclipse.swt.graphics.FontData; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; +import org.eclipse.swt.widgets.Event; +import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Tree; import org.eclipse.swt.widgets.TreeItem; import org.eclipse.swt.widgets.Widget; @@ -60,912 +65,981 @@ import org.eclipse.ui.progress.WorkbenchJob; * * @since 3.2 */ -public class AsynchronousTreeViewer extends AsynchronousViewer { - - /** - * A map of widget to parent widgets used to avoid requirement for parent - * access in UI thread. Currently used by update objects to detect/cancel - * updates on updates of children. - */ - private Map fItemToParentItem = new HashMap(); - - /** - * The tree - */ - private Tree fTree; - - /** - * Collection of tree paths to be expanded. A path is removed from the - * collection when it is expanded. The entire list is cleared when the - * input to the viewer changes. - */ - private List fPendingExpansion = new ArrayList(); - - /** - * Creates an asynchronous tree viewer on a newly-created tree control under - * the given parent. The tree control is created using the SWT style bits - * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The - * viewer has no input, no content provider, a default label provider, no - * sorter, and no filters. - * - * @param parent the parent control - */ - public AsynchronousTreeViewer(Composite parent) { - this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL); - } - - /** - * Creates an asynchronous tree viewer on a newly-created tree control under - * the given parent. The tree control is created using the given SWT style - * bits. The viewer has no input. - * - * @param parent the parent control - * @param style the SWT style bits used to create the tree. - */ - public AsynchronousTreeViewer(Composite parent, int style) { - this(new Tree(parent, style)); - } - - /** - * Creates an asynchronous tree viewer on the given tree control. The viewer - * has no input, no content provider, a default label provider, no sorter, - * and no filters. - * - * @param tree the tree control - */ - public AsynchronousTreeViewer(Tree tree) { - super(); - fTree = tree; - hookControl(fTree); - setUseHashlookup(false); - tree.addTreeListener(new TreeListener() { - public void treeExpanded(TreeEvent e) { - ((TreeItem) e.item).setExpanded(true); - internalRefresh(e.item.getData(), e.item); - } +public class AsynchronousTreeViewer extends AsynchronousViewer implements Listener { - public void treeCollapsed(TreeEvent e) { - } - }); - tree.addMouseListener(new MouseListener() { + private static final Rectangle NOT_VISIBLE = new Rectangle(0, 0, 0, 0); - public void mouseUp(MouseEvent e) { - } + /** + * A map of widget to parent widgets used to avoid requirement for parent + * access in UI thread. Currently used by update objects to detect/cancel + * updates on updates of children. + */ + private Map fItemToParentItem = new HashMap(); - public void mouseDown(MouseEvent e) { - } + private AsynchronousTreeViewerContentManager fContentManager = new AsynchronousTreeViewerContentManager(); - public void mouseDoubleClick(MouseEvent e) { - TreeItem item = ((Tree) e.widget).getItem(new Point(e.x, e.y)); - if (item != null) { - if (item.getExpanded()) { - item.setExpanded(false); - } else { - item.setExpanded(true); - internalRefresh(item.getData(), item); - } - } - } - }); - } - - /** - * Returns the tree control for this viewer. - * - * @return the tree control for this viewer - */ - public Tree getTree() { - return fTree; - } - - /** - * Updates whether the given element has children. - * - * @param element element to update - * @param widget widget associated with the element in this viewer's tree - */ - protected void updateHasChildren(Object element, Widget widget) { - IAsynchronousTreeContentAdapter adapter = getTreeContentAdapter(element); - if (adapter != null) { - IContainerRequestMonitor update = new ContainerRequestMonitor(widget, this); - schedule(update); - adapter.isContainer(element, getPresentationContext(), update); - } - } - - /** - * Updates the children of the given element. - * - * @param parent element of which to update children - * @param widget widget associated with the element in this viewer's tree - */ - protected void updateChildren(Object parent, Widget widget) { - IAsynchronousTreeContentAdapter adapter = getTreeContentAdapter(parent); - if (adapter != null) { - IChildrenRequestMonitor update = new ChildrenRequestMonitor(widget, this); - schedule(update); - adapter.retrieveChildren(parent, getPresentationContext(), update); - } - } - - /** - * Returns the tree element adapter for the given element or - * <code>null</code> if none. - * - * @param element element to retrieve adapter for - * @return presentation adapter or <code>null</code> - */ - protected IAsynchronousTreeContentAdapter getTreeContentAdapter(Object element) { - IAsynchronousTreeContentAdapter adapter = null; - if (element instanceof IAsynchronousTreeContentAdapter) { - adapter = (IAsynchronousTreeContentAdapter) element; - } else if (element instanceof IAdaptable) { - IAdaptable adaptable = (IAdaptable) element; - adapter = (IAsynchronousTreeContentAdapter) adaptable.getAdapter(IAsynchronousTreeContentAdapter.class); - } - return adapter; - } - - /** - * Expands all elements in the given tree selection. - * - * @param selection - */ - public synchronized void expand(ISelection selection) { - if (selection instanceof TreeSelection) { + /** + * The tree + */ + private Tree fTree; + + /** + * Collection of tree paths to be expanded. A path is removed from the + * collection when it is expanded. The entire list is cleared when the input + * to the viewer changes. + */ + private List fPendingExpansion = new ArrayList(); + + /** + * Creates an asynchronous tree viewer on a newly-created tree control under + * the given parent. The tree control is created using the SWT style bits + * <code>MULTI, H_SCROLL, V_SCROLL,</code> and <code>BORDER</code>. The + * viewer has no input, no content provider, a default label provider, no + * sorter, and no filters. + * + * @param parent + * the parent control + */ + public AsynchronousTreeViewer(Composite parent) { + this(parent, SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL); + } + + /** + * Creates an asynchronous tree viewer on a newly-created tree control under + * the given parent. The tree control is created using the given SWT style + * bits. The viewer has no input. + * + * @param parent + * the parent control + * @param style + * the SWT style bits used to create the tree. + */ + public AsynchronousTreeViewer(Composite parent, int style) { + this(new Tree(parent, style)); + } + + /** + * Creates an asynchronous tree viewer on the given tree control. The viewer + * has no input, no content provider, a default label provider, no sorter, + * and no filters. + * + * @param tree + * the tree control + */ + public AsynchronousTreeViewer(Tree tree) { + super(); + Assert.isTrue((tree.getStyle() & SWT.VIRTUAL) != 0); + + fTree = tree; + hookControl(fTree); + setUseHashlookup(false); + + tree.addTreeListener(new TreeListener() { + public void treeExpanded(TreeEvent e) { + ((TreeItem) e.item).setExpanded(true); + internalRefresh(e.item.getData(), e.item); + } + + public void treeCollapsed(TreeEvent e) { + } + }); + + tree.addMouseListener(new MouseListener() { + public void mouseUp(MouseEvent e) { + } + + public void mouseDown(MouseEvent e) { + } + + public void mouseDoubleClick(MouseEvent e) { + TreeItem item = ((Tree) e.widget).getItem(new Point(e.x, e.y)); + if (item != null) { + if (item.getExpanded()) { + item.setExpanded(false); + } else { + item.setExpanded(true); + internalRefresh(item.getData(), item); + } + } + } + }); + tree.addListener(SWT.SetData, this); + } + + /** + * Returns the tree control for this viewer. + * + * @return the tree control for this viewer + */ + public Tree getTree() { + return fTree; + } + + /** + * Updates whether the given element has children. + * + * @param element + * element to update + * @param widget + * widget associated with the element in this viewer's tree + */ + protected void updateHasChildren(Object element, Widget widget) { + IAsynchronousTreeContentAdapter adapter = getTreeContentAdapter(element); + if (adapter != null) { + IContainerRequestMonitor update = new ContainerRequestMonitor(widget, this); + schedule(update); + adapter.isContainer(element, getPresentationContext(), update); + } + } + + /** + * Updates the children of the given element. + * + * @param parent + * element of which to update children + * @param widget + * widget associated with the element in this viewer's tree + */ + protected void updateChildren(Object parent, Widget widget) { + IAsynchronousTreeContentAdapter adapter = getTreeContentAdapter(parent); + if (adapter != null) { + IChildrenRequestMonitor update = new ChildrenRequestMonitor(widget, this); + schedule(update); + adapter.retrieveChildren(parent, getPresentationContext(), update); + } + } + + /** + * Returns the tree element adapter for the given element or + * <code>null</code> if none. + * + * @param element + * element to retrieve adapter for + * @return presentation adapter or <code>null</code> + */ + protected IAsynchronousTreeContentAdapter getTreeContentAdapter(Object element) { + IAsynchronousTreeContentAdapter adapter = null; + if (element instanceof IAsynchronousTreeContentAdapter) { + adapter = (IAsynchronousTreeContentAdapter) element; + } else if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + adapter = (IAsynchronousTreeContentAdapter) adaptable.getAdapter(IAsynchronousTreeContentAdapter.class); + } + return adapter; + } + + /** + * Expands all elements in the given tree selection. + * + * @param selection + */ + public synchronized void expand(ISelection selection) { + if (selection instanceof TreeSelection) { TreePath[] paths = ((TreeSelection) selection).getPaths(); for (int i = 0; i < paths.length; i++) { fPendingExpansion.add(paths[i]); } - if (getControl().getDisplay().getThread() == Thread.currentThread()) { - attemptExpansion(); - } else { - WorkbenchJob job = new WorkbenchJob("attemptExpansion") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - attemptExpansion(); - return Status.OK_STATUS; - } + if (getControl().getDisplay().getThread() == Thread.currentThread()) { + attemptExpansion(); + } else { + WorkbenchJob job = new WorkbenchJob("attemptExpansion") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + attemptExpansion(); + return Status.OK_STATUS; + } - }; - job.setSystem(true); - job.schedule(); - } - } - } + }; + job.setSystem(true); + job.schedule(); + } + } + } - /** - * Attempts to expand all pending expansions. - */ - synchronized void attemptExpansion() { - if (fPendingExpansion != null) { + /** + * Attempts to expand all pending expansions. + */ + synchronized void attemptExpansion() { + if (fPendingExpansion != null) { for (Iterator i = fPendingExpansion.iterator(); i.hasNext();) { TreePath path = (TreePath) i.next(); if (attemptExpansion(path)) { i.remove(); } } - } - } - - /** - * Attempts to expand the given tree path and returns whether the expansion - * was completed. - * - * @param path path to exapand - * @return whether the expansion was completed - */ - synchronized boolean attemptExpansion(TreePath path) { - int segmentCount = path.getSegmentCount(); - for (int j = segmentCount - 1; j >= 0; j--) { - Object element = path.getSegment(j); - Widget[] treeItems = getWidgets(element); - if (treeItems != null) { - for (int k = 0; k < treeItems.length; k++) { - if (treeItems[k] instanceof TreeItem) { - TreeItem treeItem = (TreeItem) treeItems[k]; - TreePath treePath = getTreePath(treeItem); - if (path.startsWith(treePath)) { - if (!treeItem.getExpanded() && treeItem.getItemCount() > 0) { - update(element); - updateChildren(element, treeItem); - expand(treeItem); - if (path.getSegmentCount() == treePath.getSegmentCount()) { - return true; - } - return false; - } - } - } - } - } - } - return false; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#getControl() - */ - public Control getControl() { - return fTree; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.StructuredViewer#unmapAllElements() - */ - protected synchronized void unmapAllElements() { - super.unmapAllElements(); - fItemToParentItem.clear(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, - * java.lang.Object) - */ - synchronized protected void inputChanged(Object input, Object oldInput) { + } + } + + /** + * Attempts to expand the given tree path and returns whether the expansion + * was completed. + * + * @param path path to expand + * @return whether the expansion was completed + */ + synchronized boolean attemptExpansion(TreePath path) { + int segmentCount = path.getSegmentCount(); + for (int j = segmentCount - 1; j >= 0; j--) { + Object element = path.getSegment(j); + Widget[] treeItems = getWidgets(element); + if (treeItems != null) { + for (int k = 0; k < treeItems.length; k++) { + if (treeItems[k] instanceof TreeItem) { + TreeItem treeItem = (TreeItem) treeItems[k]; + TreePath treePath = getTreePath(treeItem); + if (path.startsWith(treePath)) { + if (!treeItem.isDisposed() && !treeItem.getExpanded() && treeItem.getItemCount() > 0) { + update(element); + updateChildren(element, treeItem); + expand(treeItem); + if (path.getSegmentCount() == treePath.getSegmentCount()) { + return true; + } + return false; + } + } + } + } + } + } + return false; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#getControl() + */ + public Control getControl() { + return fTree; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.StructuredViewer#unmapAllElements() + */ + protected synchronized void unmapAllElements() { + super.unmapAllElements(); + fItemToParentItem.clear(); + fContentManager.clearAll(); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, + * java.lang.Object) + */ + synchronized protected void inputChanged(Object input, Object oldInput) { fPendingExpansion.clear(); - super.inputChanged(input, oldInput); - map(input, fTree); - refresh(); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#map(java.lang.Object, - * org.eclipse.swt.widgets.Widget) - */ - protected void map(Object element, Widget item) { - super.map(element, item); - if (item instanceof TreeItem) { - TreeItem treeItem = (TreeItem) item; - TreeItem parentItem = treeItem.getParentItem(); - if (parentItem != null) { - fItemToParentItem.put(treeItem, parentItem); - } - } - } - - /** - * Returns all paths to the given element or <code>null</code> if none. - * - * @param element - * @return paths to the given element or <code>null</code> - */ - public synchronized TreePath[] getTreePaths(Object element) { - Widget[] widgets = getWidgets(element); - if (widgets == null) { - return null; - } - TreePath[] paths = new TreePath[widgets.length]; - for (int i = 0; i < widgets.length; i++) { - List path = new ArrayList(); - path.add(element); - Widget widget = widgets[i]; - TreeItem parent = null; - if (widget instanceof TreeItem) { - TreeItem treeItem = (TreeItem) widget; - parent = getParentItem(treeItem); - } - while (parent != null) { - Object data = getElement(parent); - if (data == null) { - // if the parent is unmapped while attempting selection - return null; - } - path.add(0, data); - parent = getParentItem(parent); - } - if (!path.get(0).equals(getInput())) { - path.add(0, getInput()); - } - paths[i] = new TreePath(path.toArray()); - if (widget instanceof TreeItem) { - paths[i].setTreeItem((TreeItem) widget); - } - } - return paths; - } - - /** - * Constructs and returns a tree path for the given item. Must be called - * from the UI thread. - * - * @param item item to constuct a path for - * @return tree path for the item - */ - protected synchronized TreePath getTreePath(TreeItem item) { - TreeItem parent = item; - List path = new ArrayList(); - while (parent != null) { - path.add(0, parent.getData()); - parent = parent.getParentItem(); - } - path.add(0, fTree.getData()); - return new TreePath(path.toArray()); - } - - /** - * Called by <code>ContainerRequestMonitor</code> after it is determined - * if the widget contains children. - * - * @param widget - * @param containsChildren - */ - synchronized void setIsContainer(Widget widget, boolean containsChildren) { - TreeItem[] prevChildren = null; - if (widget instanceof Tree) { - Tree tree = (Tree) widget; - prevChildren = tree.getItems(); - } else { - prevChildren = ((TreeItem) widget).getItems(); - } - if (containsChildren) { - if (prevChildren.length == 0) { - if (widget instanceof Tree) { - // update root elements in the tree - updateChildren(widget.getData(), widget); - } else { - // insert new dummy node to add + - newTreeItem(widget, 0); - if (((TreeItem) widget).getExpanded()) { - updateChildren(widget.getData(), widget); - } - } - } else { - if (widget instanceof Tree || ((TreeItem) widget).getExpanded()) { - // if expanded, update the children - updateChildren(widget.getData(), widget); - } - } - } else if (prevChildren.length > 0) { - // dispose previous children - for (int i = 0; i < prevChildren.length; i++) { - TreeItem prevChild = prevChildren[i]; - unmap(prevChild.getData(), prevChild); - prevChild.dispose(); - } - } - attemptExpansion(); - } - - /** - * Adds a child at end of parent. - * - * @param parent - * @param child - */ - synchronized void add(Widget parent, Object child) { - // refresh vs. add if not expanded - boolean expanded = true; - if (parent instanceof TreeItem) { - expanded = ((TreeItem)parent).getExpanded(); - } - if (expanded) { - Object[] objects = filter(new Object[]{child}); - if (objects.length == 1) { - child = objects[0]; - TreeItem item = newTreeItem(parent); - map(child, item); - internalRefresh(child, item); - } - } else { - internalRefresh(parent.getData(), parent); - } - attemptExpansion(); - attemptSelection(false); - } - - /** - * Called by <code>ChildrenRequestMonitor</code> after children have been - * retrieved. - * - * @param widget - * @param newChildren - */ - synchronized void setChildren(final Widget widget, final List newChildren) { - preservingSelection(new Runnable() { + super.inputChanged(input, oldInput); + map(input, fTree); + refresh(); + } - public void run() { - // apply filters - Object[] children = filter(newChildren.toArray()); + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#map(java.lang.Object, + * org.eclipse.swt.widgets.Widget) + */ + protected void map(Object element, Widget item) { + super.map(element, item); + if (item instanceof TreeItem) { + TreeItem treeItem = (TreeItem) item; + TreeItem parentItem = treeItem.getParentItem(); + if (parentItem != null) { + fItemToParentItem.put(treeItem, parentItem); + } + } + } - // sort filtered children - ViewerSorter viewerSorter = getSorter(); - if (viewerSorter != null) { - viewerSorter.sort(AsynchronousTreeViewer.this, children); - } + /** + * Returns all paths to the given element or <code>null</code> if none. + * + * @param element + * @return paths to the given element or <code>null</code> + */ + public synchronized TreePath[] getTreePaths(Object element) { + Widget[] widgets = getWidgets(element); + if (widgets == null) { + return null; + } + TreePath[] paths = new TreePath[widgets.length]; + for (int i = 0; i < widgets.length; i++) { + List path = new ArrayList(); + path.add(element); + Widget widget = widgets[i]; + TreeItem parent = null; + if (widget instanceof TreeItem) { + TreeItem treeItem = (TreeItem) widget; + parent = getParentItem(treeItem); + } + while (parent != null) { + Object data = getElement(parent); + if (data == null) { + // if the parent is unmapped while attempting selection + return null; + } + path.add(0, data); + parent = getParentItem(parent); + } + if (!path.get(0).equals(getInput())) { + path.add(0, getInput()); + } + paths[i] = new TreePath(path.toArray()); + if (widget instanceof TreeItem) { + paths[i].setTreeItem((TreeItem) widget); + } + } + return paths; + } - // update tree - TreeItem[] prevItems = null; - if (widget instanceof Tree) { - Tree tree = (Tree) widget; - prevItems = tree.getItems(); - } else { - prevItems = ((TreeItem) widget).getItems(); - } + /** + * Constructs and returns a tree path for the given item. Must be called + * from the UI thread. + * + * @param item + * item to constuct a path for + * @return tree path for the item + */ + protected synchronized TreePath getTreePath(TreeItem item) { + TreeItem parent = item; + List path = new ArrayList(); + while (parent != null && !parent.isDisposed()) { + Object parentElement = parent.getData(); + if (parentElement == null) { + return new TreePath(new Object[0]); + } + path.add(0, parentElement); + parent = parent.getParentItem(); + } + path.add(0, fTree.getData()); + return new TreePath(path.toArray()); + } + + /** + * Called by <code>ContainerRequestMonitor</code> after it is determined + * if the widget contains children. + * + * @param widget + * @param containsChildren + */ + synchronized void setIsContainer(Widget widget, boolean containsChildren) { + TreeItem[] prevChildren = getItems(widget); + + if (containsChildren) { + if (prevChildren.length == 0) { + setItemCount(widget, 1); + if (widget instanceof Tree) { + updateChildren(widget.getData(), widget); + } else { + TreeItem treeItem = (TreeItem) widget; + if (treeItem.getExpanded()) { + updateChildren(widget.getData(), widget); + } + } + } else { + if (widget instanceof Tree || ((TreeItem) widget).getExpanded()) { + // if expanded, update the children + updateChildren(widget.getData(), widget); + } + } + } else { + for (int i = 0; i < prevChildren.length; i++) { + TreeItem item = prevChildren[i]; + Object element = getElement(item); + unmap(element, item); + } + setItemCount(widget, 0); + } + + attemptExpansion(); + attemptSelection(true); + } + + /** + * Adds a child at end of parent. + * + * @param parent + * @param child + */ + synchronized void add(Widget parentWidget, Object child) { + Object[] children = filter(new Object[] { child }); + if (children.length == 0) { + return; // added element was filtered out. + } + + //TODO sort??? + + fContentManager.addChildElement(parentWidget, child); + int childCount = fContentManager.getChildCount(parentWidget); + setItemCount(parentWidget, childCount); - int index = 0; - for (; index < children.length; index++) { - Object kid = children[index]; - TreeItem item = null; - if (index < prevItems.length) { - item = prevItems[index]; - Object oldData = item.getData(); - if (kid.equals(oldData)) { - if (kid != oldData) { - // if equal but not identical, remap the element - remap(kid, item); - } - } else { - unmap(oldData, item); - map(kid, item); - } + attemptExpansion(); + attemptSelection(false); + } + + /** + * Called by <code>ChildrenRequestMonitor</code> after children have been + * retrieved. + * + * @param widget + * @param newChildren + */ + synchronized void setChildren(final Widget widget, final List newChildren) { + // apply filters + final Object[] children = filter(newChildren.toArray()); + + // sort filtered children + ViewerSorter viewerSorter = getSorter(); + if (viewerSorter != null) { + viewerSorter.sort(AsynchronousTreeViewer.this, children); + } + + preservingSelection(new Runnable() { + public void run() { + //unmap all old children + TreeItem[] currentChildItems = getItems(widget); + for (int i = 0; i < currentChildItems.length; i++) { + TreeItem item = currentChildItems[i]; + if (!item.isDisposed()) { + Object oldElement = item.getData(); + if (oldElement != null) { + unmap(oldElement, item); + } + } + } + + //map new children + for (int i = 0; i < currentChildItems.length; i++) { + TreeItem item = currentChildItems[i]; + if (i >= children.length) { + item.dispose(); + } else if (isVisible(item)) { + map(children[i], item); + internalRefresh(children[i], item); } else { - item = newTreeItem(widget, index); - map(kid, item); + clear(item); } - internalRefresh(kid, item); - } - // remove left over old items - while (index < prevItems.length) { - TreeItem oldItem = prevItems[index]; - unmap(oldItem.getData(), oldItem); - oldItem.dispose(); - index++; } + + // update parent to children map + fContentManager.setChildElements(widget, children); + + //set item count + Widget theWidget = widget; + setItemCount(widget, children.length); } - }); - attemptExpansion(); - attemptSelection(true); - } - - /** - * Expands the given tree item and all of its parents. Does *not* update - * elements or retrieve children. - * - * @param child item to expand - */ - private void expand(TreeItem child) { - if (!child.getExpanded()) { - child.setExpanded(true); - - TreeItem parent = child.getParentItem(); - if (parent != null) { - expand(parent); - } - } - } - - /** - * Creates a new tree item as a child of the given widget at the specified - * index. - * - * @param parent parent widget - a Tree or TreeItem - * @param index index at which to create new child - * @return tree item - */ - protected TreeItem newTreeItem(Widget parent, int index) { - if (parent instanceof Tree) { - return new TreeItem((Tree) parent, SWT.NONE, index); - } - return new TreeItem((TreeItem) parent, SWT.NONE, index); - } - - /** - * Creates a new tree item as a child of the given widget as last. - * - * @param parent parent widget - a Tree or TreeItem - * @return tree item - */ - protected TreeItem newTreeItem(Widget parent) { - if (parent instanceof Tree) { - return new TreeItem((Tree) parent, SWT.NONE); - } - return new TreeItem((TreeItem) parent, SWT.NONE); - } - - /** - * Unmaps the given item, and unmaps and disposes of all children of that - * item. Does not dispose of the given item. - * - * @param kid - * @param oldItem - */ - protected synchronized void unmap(Object kid, Widget widget) { - if (kid == null) { - // when unmapping a dummy item - return; - } - - TreeItem[] selection = fTree.getSelection(); - if (selection.length == 1 && isChild(widget, selection[0])) { - setSelection(null); - } - - super.unmap(kid, widget); - fItemToParentItem.remove(widget); - if (widget instanceof TreeItem) { - TreeItem item = (TreeItem) widget; - TreeItem[] children = item.getItems(); - for (int i = 0; i < children.length; i++) { - TreeItem child = children[i]; - unmap(child.getData(), child); - child.dispose(); - } - } - } + attemptExpansion(); + attemptSelection(true); + } - private boolean isChild(Widget parent, TreeItem child) { - if (child==null) { - return false; - } else if (parent == child) { - return true; - } - return isChild(parent, child.getParentItem()); - } - - /** - * Returns the parent item for an item or <code>null</code> if none. - * - * @param item item for which parent is requested - * @return parent item or <code>null</code> - */ - protected synchronized TreeItem getParentItem(TreeItem item) { - return (TreeItem) fItemToParentItem.get(item); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object) - */ - protected Widget doFindInputItem(Object element) { - if (element.equals(getInput())) { - return fTree; - } - return null; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object) - */ - protected Widget doFindItem(Object element) { - Widget[] widgets = getWidgets(element); - if (widgets != null && widgets.length > 0) { - return widgets[0]; - } - return null; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.viewers.AsynchronousViewer#newSelectionFromWidget() - */ - protected ISelection newSelectionFromWidget() { - Control control = getControl(); - if (control == null || control.isDisposed()) { - return StructuredSelection.EMPTY; - } - List list = getSelectionFromWidget(); - return new TreeSelection((TreePath[]) list.toArray()); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget() - */ - protected synchronized List getSelectionFromWidget() { - TreeItem[] selection = fTree.getSelection(); - TreePath[] paths = new TreePath[selection.length]; - for (int i = 0; i < selection.length; i++) { - paths[i] = getTreePath(selection[i]); - } - return Arrays.asList(paths); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#internalRefresh(java.lang.Object, - * org.eclipse.swt.widgets.Widget) - */ - protected synchronized void internalRefresh(Object element, Widget item) { - super.internalRefresh(element, item); - updateHasChildren(element, item); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object) - */ - public void reveal(Object element) { - Widget[] widgets = getWidgets(element); - if (widgets != null && widgets.length > 0) { - // TODO: only reveals the first occurrence - should we reveal all? - TreeItem item = (TreeItem) widgets[0]; - Tree tree = (Tree) getControl(); - tree.showItem(item); - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#doAttemptSelectionToWidget(org.eclipse.jface.viewers.ISelection, - * boolean) - */ - protected synchronized ISelection doAttemptSelectionToWidget(ISelection selection, boolean reveal) { - List remaining = new ArrayList(); - if (!selection.isEmpty()) { - List toSelect = new ArrayList(); - List theElements = new ArrayList(); - TreeSelection treeSelection = (TreeSelection) selection; - TreePath[] paths = treeSelection.getPaths(); - for (int i = 0; i < paths.length; i++) { - TreePath path = paths[i]; - if (path == null) { - continue; - } - TreePath[] treePaths = getTreePaths(path.getLastSegment()); - boolean selected = false; - if (treePaths != null) { - for (int j = 0; j < treePaths.length; j++) { - TreePath existingPath = treePaths[j]; - if (existingPath.equals(path)) { - TreeItem treeItem = existingPath.getTreeItem(); - toSelect.add(treeItem); - theElements.add(path.getLastSegment()); - selected = true; - break; - } - } - } - if (!selected) { - remaining.add(path); - } - } - if (!toSelect.isEmpty()) { - final TreeItem[] items = (TreeItem[]) toSelect.toArray(new TreeItem[toSelect.size()]); - // TODO: hack to ensure selection contains 'selected' element - // instead of 'equivalent' element. Handles synch problems - // between - // set selection & refresh - for (int i = 0; i < items.length; i++) { - TreeItem item = items[i]; - Object element = theElements.get(i); - if (item.getData() != element) { - remap(element, item); - } - } - fTree.setSelection(items); - if (reveal) { - fTree.showItem(items[0]); - } - } - } else { - fTree.setSelection(new TreeItem[0]); - } - return new TreeSelection((TreePath[]) remaining.toArray(new TreePath[remaining.size()])); - } - - /** - * Collapses all items in the tree. - */ - public void collapseAll() { - TreeItem[] items = fTree.getItems(); - for (int i = 0; i < items.length; i++) { - TreeItem item = items[i]; - if (item.getExpanded()) - collapse(item); - } - } - - /** - * Collaspes the given item and all of its children items. - * - * @param item item to collapose recursively - */ - protected void collapse(TreeItem item) { - TreeItem[] items = item.getItems(); - for (int i = 0; i < items.length; i++) { - TreeItem child = items[i]; - if (child.getExpanded()) { - collapse(child); - } - } - item.setExpanded(false); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#setColor(org.eclipse.swt.widgets.Widget, - * org.eclipse.swt.graphics.RGB, org.eclipse.swt.graphics.RGB) - */ - void setColors(Widget widget, RGB[] foregrounds, RGB[] backgrounds) { - if (widget instanceof TreeItem) { - TreeItem item = (TreeItem) widget; - Color[] fgs = getColor(foregrounds); - Color[] bgs = getColor(backgrounds); - for (int i = 0; i < bgs.length; i++) { - item.setForeground(i, fgs[i]); - item.setBackground(i, bgs[i]); - } - } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#setFont(org.eclipse.swt.widgets.Widget, - * org.eclipse.swt.graphics.FontData) - */ - void setFonts(Widget widget, FontData[] fontData) { - if (widget instanceof TreeItem) { - TreeItem item = (TreeItem) widget; - Font[] fonts = getFonts(fontData); - for (int i = 0; i < fonts.length; i++) { - item.setFont(i, fonts[i]); + protected void doUpdateItem(Widget item, Object element, boolean fullMap) { + super.doUpdateItem(item, element, fullMap); + updateHasChildren(element, item); + } + + private void setItemCount(Widget widget, int itemCount) { + if (widget == fTree) { + fTree.setItemCount(itemCount); + } else { + ((TreeItem) widget).setItemCount(itemCount); + } + } + + private TreeItem[] getItems(Widget widget) { + if (widget instanceof TreeItem) { + return ((TreeItem) widget).getItems(); + } else { + return fTree.getItems(); + } + } + + public synchronized void handleEvent(final Event event) { + preservingSelection(new Runnable() { + public void run() { + TreeItem item = (TreeItem) event.item; + Widget parentItem = item.getParentItem(); + int index = 0; + if (parentItem != null) { + index = ((TreeItem) parentItem).indexOf(item); + } else { + parentItem = fTree; + index = fTree.indexOf(item); + } + + Object element = fContentManager.getChildElement(parentItem, index); + if (element != null) { + map(element, item); + internalRefresh(element, true); + } + } + }); + } + + /** + * Expands the given tree item and all of its parents. Does *not* update + * elements or retrieve children. + * + * @param child + * item to expand + */ + private void expand(TreeItem child) { + if (!child.getExpanded()) { + child.setExpanded(true); + + TreeItem parent = child.getParentItem(); + if (parent != null) { + expand(parent); + } + } + } + + /** + * Unmaps the given item, and unmaps and disposes of all children of that + * item. Does not dispose of the given item. + * + * @param kid + * @param oldItem + */ + protected synchronized void unmap(Object element, Widget widget) { + TreeItem[] selection = fTree.getSelection(); + if (selection.length == 1 && isChild(widget, selection[0])) { + setSelection(null); + } + + TreeItem[] items = getItems(widget); + for (int i = 0; i < items.length; i++) { + TreeItem childItem = items[i]; + Object childElement = getElement(childItem); + if (childElement != null) { + unmap(childElement, childItem); } } - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#getParent(org.eclipse.swt.widgets.Widget) - */ - protected Widget getParent(Widget widget) { - return (Widget) fItemToParentItem.get(widget); - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#acceptsSelection(org.eclipse.jface.viewers.ISelection) - */ - protected boolean acceptsSelection(ISelection selection) { - return selection instanceof TreeSelection; - } - - /* - * (non-Javadoc) - * - * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#getEmptySelection() - */ - protected ISelection getEmptySelection() { - return new TreeSelection(new TreePath[0]); - } - - /** - * Adds the item specified by the given tree path to this tree. - * Can be called in a non-UI thread. - * - * @param treePath - */ - public synchronized void add(TreePath treePath) { - int parentIndex = treePath.getSegmentCount() - 2; - if (parentIndex >= 0) { - // find the paths to the parent, if it's present - Object parent = treePath.getSegment(parentIndex); - TreePath[] paths = getTreePaths(parent); - if (paths != null) { - // find the right path - for (int i = 0; i < paths.length; i++) { - TreePath path = paths[i]; - if (treePath.startsWith(path)) { - Widget widget = path.getTreeItem(); - if (widget == null) { - widget = getTree(); - } - AddRequestMonitor addRequest = new AddRequestMonitor(widget, treePath, this); - schedule(addRequest); - addRequest.done(); - return; - } - } - } - // refresh the leaf parent, if any - for (int i = parentIndex -1; i >= 0; i++) { - parent = treePath.getSegment(i); - paths = getTreePaths(parent); - if (paths != null) { - for (int j = 0; j < paths.length; j++) { - TreePath path = paths[j]; - if (treePath.startsWith(path)) { - Widget widget = path.getTreeItem(); - if (widget == null) { - widget = getTree(); - } - internalRefresh(parent, widget); - return; - } - } - } - } + + super.unmap(element, widget); + + fItemToParentItem.remove(widget); + fContentManager.remove(widget); + } + + private void clear(Widget widget) { + if (widget instanceof TreeItem) { + TreeItem item = (TreeItem) widget; + item.clearAll(true); + } else { + fTree.clearAll(true); + } + } + + private boolean isVisible(TreeItem item) { + Rectangle itemBounds = item.getBounds(); + return !NOT_VISIBLE.equals(itemBounds); + } + + private boolean isChild(Widget parent, TreeItem child) { + if (child == null) { + return false; + } else if (parent == child) { + return true; + } + return isChild(parent, child.getParentItem()); + } + + /** + * Returns the parent item for an item or <code>null</code> if none. + * + * @param item + * item for which parent is requested + * @return parent item or <code>null</code> + */ + protected synchronized TreeItem getParentItem(TreeItem item) { + return (TreeItem) fItemToParentItem.get(item); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object) + */ + protected Widget doFindInputItem(Object element) { + if (element.equals(getInput())) { + return fTree; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object) + */ + protected Widget doFindItem(Object element) { + Widget[] widgets = getWidgets(element); + if (widgets != null && widgets.length > 0) { + return widgets[0]; + } + return null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.internal.ui.viewers.AsynchronousViewer#newSelectionFromWidget() + */ + protected ISelection newSelectionFromWidget() { + Control control = getControl(); + if (control == null || control.isDisposed()) { + return StructuredSelection.EMPTY; + } + List list = getSelectionFromWidget(); + return new TreeSelection((TreePath[]) list.toArray()); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget() + */ + protected synchronized List getSelectionFromWidget() { + TreeItem[] selection = fTree.getSelection(); + TreePath[] paths = new TreePath[selection.length]; + for (int i = 0; i < selection.length; i++) { + paths[i] = getTreePath(selection[i]); + } + return Arrays.asList(paths); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#internalRefresh(java.lang.Object, + * org.eclipse.swt.widgets.Widget) + */ + protected synchronized void internalRefresh(Object element, Widget item) { + super.internalRefresh(element, item); + updateHasChildren(element, item); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object) + */ + public void reveal(Object element) { + Widget[] widgets = getWidgets(element); + if (widgets != null && widgets.length > 0) { + // TODO: only reveals the first occurrence - should we reveal all? + TreeItem item = (TreeItem) widgets[0]; + Tree tree = (Tree) getControl(); + tree.showItem(item); + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#doAttemptSelectionToWidget(org.eclipse.jface.viewers.ISelection, + * boolean) + */ + protected synchronized ISelection doAttemptSelectionToWidget(ISelection selection, boolean reveal) { + List remaining = new ArrayList(); + if (!selection.isEmpty()) { + List toSelect = new ArrayList(); + List theElements = new ArrayList(); + TreeSelection treeSelection = (TreeSelection) selection; + TreePath[] paths = treeSelection.getPaths(); + for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + if (path == null) { + continue; + } + TreePath[] treePaths = getTreePaths(path.getLastSegment()); + boolean selected = false; + if (treePaths != null) { + for (int j = 0; j < treePaths.length; j++) { + TreePath existingPath = treePaths[j]; + if (existingPath.equals(path)) { + TreeItem treeItem = existingPath.getTreeItem(); + if (!treeItem.isDisposed()) { + toSelect.add(treeItem); + theElements.add(path.getLastSegment()); + selected = true; + break; + } + } + } + } + if (!selected) { + remaining.add(path); + } + } + if (!toSelect.isEmpty()) { + final TreeItem[] items = (TreeItem[]) toSelect.toArray(new TreeItem[toSelect.size()]); + // TODO: hack to ensure selection contains 'selected' element + // instead of 'equivalent' element. Handles synch problems + // between + // set selection & refresh + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + Object element = theElements.get(i); + if (!item.isDisposed() && item.getData() != element) { + remap(element, item); + } + } + fTree.setSelection(items); + if (reveal) { + fTree.showItem(items[0]); + } + } + } else { + fTree.setSelection(new TreeItem[0]); + } + return new TreeSelection((TreePath[]) remaining.toArray(new TreePath[remaining.size()])); + } + + /** + * Collapses all items in the tree. + */ + public void collapseAll() { + TreeItem[] items = fTree.getItems(); + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + if (item.getExpanded()) + collapse(item); + } + } + + /** + * Collaspes the given item and all of its children items. + * + * @param item + * item to collapose recursively + */ + protected void collapse(TreeItem item) { + TreeItem[] items = item.getItems(); + for (int i = 0; i < items.length; i++) { + TreeItem child = items[i]; + if (child.getExpanded()) { + collapse(child); + } + } + item.setExpanded(false); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#setColor(org.eclipse.swt.widgets.Widget, + * org.eclipse.swt.graphics.RGB, org.eclipse.swt.graphics.RGB) + */ + void setColors(Widget widget, RGB[] foregrounds, RGB[] backgrounds) { + if (widget instanceof TreeItem) { + TreeItem item = (TreeItem) widget; + Color[] fgs = getColor(foregrounds); + Color[] bgs = getColor(backgrounds); + for (int i = 0; i < bgs.length; i++) { + item.setForeground(i, fgs[i]); + item.setBackground(i, bgs[i]); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#setFont(org.eclipse.swt.widgets.Widget, + * org.eclipse.swt.graphics.FontData) + */ + void setFonts(Widget widget, FontData[] fontData) { + if (widget instanceof TreeItem) { + TreeItem item = (TreeItem) widget; + Font[] fonts = getFonts(fontData); + for (int i = 0; i < fonts.length; i++) { + item.setFont(i, fonts[i]); + } + } + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#getParent(org.eclipse.swt.widgets.Widget) + */ + protected Widget getParent(Widget widget) { + return (Widget) fItemToParentItem.get(widget); + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#acceptsSelection(org.eclipse.jface.viewers.ISelection) + */ + protected boolean acceptsSelection(ISelection selection) { + return selection instanceof TreeSelection; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousViewer#getEmptySelection() + */ + protected ISelection getEmptySelection() { + return new TreeSelection(new TreePath[0]); + } + + /** + * Adds the item specified by the given tree path to this tree. Can be + * called in a non-UI thread. + * + * @param treePath + */ + public synchronized void add(TreePath treePath) { + Object element = treePath.getLastSegment(); + int parentIndex = treePath.getSegmentCount() - 2; + Object parentElement = getInput(); + if (parentIndex > 0) { + parentElement = treePath.getSegment(parentIndex); + } + + ViewerFilter[] filters = getFilters(); + for (int i = 0; i < filters.length; i++) { + ViewerFilter filter = filters[i]; + boolean select = filter.select(this, parentElement, element); + if (!select) { + return; // added item filtered out. + } } - } - - - /** - * Removes the item specified in the given tree path from this tree. - * Can be called in a non-UI thread. - * - * @param treePath - */ - public synchronized void remove(TreePath treePath) { + + //TODO Sorting... if there's a sorter, it needs to be consulted + + if (parentIndex >= 0) { + // find the paths to the parent, if it's present + Object parent = treePath.getSegment(parentIndex); + TreePath[] paths = getTreePaths(parent); + if (paths != null) { + // find the right path + for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + if (treePath.startsWith(path)) { + Widget widget = path.getTreeItem(); + if (widget == null) { + widget = getTree(); + } + AddRequestMonitor addRequest = new AddRequestMonitor(widget, treePath, this); + schedule(addRequest); + addRequest.done(); + return; + } + } + } + // refresh the leaf parent, if any + for (int i = parentIndex - 1; i >= 0; i++) { + parent = treePath.getSegment(i); + paths = getTreePaths(parent); + if (paths != null) { + for (int j = 0; j < paths.length; j++) { + TreePath path = paths[j]; + if (treePath.startsWith(path)) { + Widget widget = path.getTreeItem(); + if (widget == null) { + widget = getTree(); + } + internalRefresh(parent, widget); + return; + } + } + } + } + } + } + + /** + * Removes the item specified in the given tree path from this tree. Can be + * called in a non-UI thread. + * + * @param treePath + */ + public synchronized void remove(TreePath treePath) { for (Iterator i = fPendingExpansion.iterator(); i.hasNext();) { TreePath expansionPath = (TreePath) i.next(); if (expansionPath.startsWith(treePath)) { i.remove(); } } - if (treePath.getSegmentCount() > 1) { - // find the paths to the element, if it's present - Object element = treePath.getLastSegment(); - TreePath[] paths = getTreePaths(element); - if (paths != null) { - // find the right path - for (int i = 0; i < paths.length; i++) { - TreePath path = paths[i]; - if (treePath.equals(path)) { - TreeItem item = path.getTreeItem(); - RemoveRequestMonitor request = new RemoveRequestMonitor(item, treePath, this); - schedule(request); - request.done(); - return; - } - } - } - // refresh the parent, if present - if (treePath.getSegmentCount() >= 2) { - element = treePath.getSegment(treePath.getSegmentCount() - 2); - paths = getTreePaths(element); - if (paths != null) { - // find the right path - for (int i = 0; i < paths.length; i++) { - TreePath path = paths[i]; - if (treePath.startsWith(path)) { - Widget widget = path.getTreeItem(); - if (widget == null) { - widget = getTree(); - } - internalRefresh(element, widget); - return; - } - } - } - } - } - } + if (treePath.getSegmentCount() > 1) { + // find the paths to the element, if it's present + Object element = treePath.getLastSegment(); + TreePath[] paths = getTreePaths(element); + if (paths != null) { + // find the right path + for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + if (treePath.equals(path)) { + TreeItem item = path.getTreeItem(); + RemoveRequestMonitor request = new RemoveRequestMonitor(item, treePath, this); + schedule(request); + request.done(); + return; + } + } + } + // refresh the parent, if present + if (treePath.getSegmentCount() >= 2) { + element = treePath.getSegment(treePath.getSegmentCount() - 2); + paths = getTreePaths(element); + if (paths != null) { + // find the right path + for (int i = 0; i < paths.length; i++) { + TreePath path = paths[i]; + if (treePath.startsWith(path)) { + Widget widget = path.getTreeItem(); + if (widget == null) { + widget = getTree(); + } + internalRefresh(element, widget); + return; + } + } + } + } + } + } - synchronized void remove(final Widget widget) { - preservingSelection(new Runnable() { - public void run() { - Object element = widget.getData(); - unmap(element, widget); - widget.dispose(); - } - }); - } - - void setLabels(Widget widget, String[] text, ImageDescriptor[] image) { - if (widget instanceof TreeItem) { - TreeItem item = (TreeItem) widget; - item.setText(text); - item.setImage(getImages(image)); - } - } + synchronized void remove(final Widget widget) { + preservingSelection(new Runnable() { + public void run() { + if (!(widget instanceof TreeItem)) { + return; + } + + fContentManager.remove(widget); + TreeItem item = (TreeItem) widget; + Widget parentWidget = item.getParentItem(); + if (parentWidget == null) + parentWidget = fTree; + + Object element = getElement(widget); + if (element != null) + unmap(element, widget); + + item.dispose(); + setItemCount(parentWidget, fContentManager.getChildCount(parentWidget)); + } + }); + } + + synchronized void setLabels(Widget widget, String[] text, ImageDescriptor[] image) { + if (widget instanceof TreeItem) { + TreeItem item = (TreeItem) widget; + if (!item.isDisposed()) { + item.setText(text); + item.setImage(getImages(image)); + } + } + } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewerContentManager.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewerContentManager.java new file mode 100644 index 000000000..fda668fe3 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousTreeViewerContentManager.java @@ -0,0 +1,100 @@ +/******************************************************************************* + * Copyright (c) 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.debug.internal.ui.viewers; + +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; + +public class AsynchronousTreeViewerContentManager { + + private static final Object[] EMPTY = new Object[0]; + private Map fWidgetToChildElements = new HashMap(); + + public void clearAll() { + fWidgetToChildElements.clear(); + } + + public Object[] getChildElements(Widget parentWidget) { + Object[] childElements = (Object[]) fWidgetToChildElements.get(parentWidget); + if (childElements == null) + childElements = EMPTY; + return childElements; + } + + public void addChildElement(Widget parentWidget, Object child) { + Object[] originalChildren = getChildElements(parentWidget); + Object[] newChildren = new Object[originalChildren.length + 1]; + System.arraycopy(originalChildren, 0, newChildren, 0, originalChildren.length); + newChildren[originalChildren.length] = child; + fWidgetToChildElements.put(parentWidget, newChildren); + } + + public int getChildCount(Widget parentWidget) { + return getChildElements(parentWidget).length; + } + + public void setChildElements(Widget parentWidget, Object[] children) { + fWidgetToChildElements.put(parentWidget, children); + } + + public Object getChildElement(Widget parentWidget, int index) { + Object[] childElements = getChildElements(parentWidget); + if (index < childElements.length) + return childElements[index]; + else + return null; + } + + public void remove(Widget widget) { + if (widget instanceof TreeItem) { + TreeItem treeItem = (TreeItem) widget; + removeChildren(treeItem); + + Object data = treeItem.getData(); + if (data != null) { + Widget parentWidget = treeItem.getParentItem(); + if (parentWidget == null) + parentWidget = treeItem.getParent(); + + removeFromParent(parentWidget, data); + } + } else { + fWidgetToChildElements.clear(); + } + } + + private void removeFromParent(Widget parentWidget, Object data) { + Object[] childElements = getChildElements(parentWidget); + for (int i = 0; i < childElements.length; i++) { + Object object = childElements[i]; + if (data.equals(object)) { + Object[] newChildren = new Object[childElements.length - 1]; + System.arraycopy(childElements, 0, newChildren, 0, i); + System.arraycopy(childElements, i + 1, newChildren, i, newChildren.length - i); + fWidgetToChildElements.put(parentWidget, newChildren); + return; + } + } + } + + private void removeChildren(TreeItem item) { + TreeItem[] items = item.getItems(); + for (int i = 0; i < items.length; i++) { + removeChildren(items[i]); + } + + fWidgetToChildElements.remove(item); + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousViewer.java index f2e48b95b..c6a9246aa 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/AsynchronousViewer.java @@ -63,6 +63,8 @@ import org.eclipse.ui.progress.WorkbenchJob; */ public abstract class AsynchronousViewer extends StructuredViewer { + public static boolean DEBUG_CACHE; + /** * A map of elements to associated tree items or tree */ @@ -344,6 +346,11 @@ public abstract class AsynchronousViewer extends StructuredViewer { fElementsToWidgets.clear(); fWidgetsToElements.clear(); disposeAllModelProxies(); + if (DEBUG_CACHE) { + System.out.println("unmapped all elements"); //$NON-NLS-1$ + System.out.println("\tfWidgetsToElements.size() " + fWidgetsToElements.size()); //$NON-NLS-1$ + System.out.println("\tfElementsToWidgets.size() " + fElementsToWidgets.size()); //$NON-NLS-1$ + } } /** @@ -390,7 +397,13 @@ public abstract class AsynchronousViewer extends StructuredViewer { fElementsToWidgets.put(element, items); } installModelProxy(element); - } + + if (DEBUG_CACHE) { + System.out.println("mapped " + element + " to " + item + "(hashcode: " + item.hashCode()+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + System.out.println("\tfWidgetsToElements.size() " + fWidgetsToElements.size()); //$NON-NLS-1$ + System.out.println("\tfElementsToWidgets.size() " + fElementsToWidgets.size()); //$NON-NLS-1$ + } + } /** * Updates the cached information that maps the given element to the @@ -402,9 +415,23 @@ public abstract class AsynchronousViewer extends StructuredViewer { */ protected void remap(Object element, Widget item) { item.setData(element); + installModelProxy(element); fWidgetsToElements.put(item, element); - Object widgets = fElementsToWidgets.remove(element); - fElementsToWidgets.put(element, widgets); + Widget[] widgets = (Widget[]) fElementsToWidgets.remove(element); + if (widgets == null) { + fElementsToWidgets.put(element, new Widget[] {item}); + } else { + Widget[] newArray = new Widget[widgets.length + 1]; + System.arraycopy(widgets, 0, newArray, 0, widgets.length); + newArray[widgets.length] = item; + fElementsToWidgets.put(element, newArray); + } + + if (DEBUG_CACHE) { + System.out.println("remapped " + element + " to " + item + "(hashcode: " + item.hashCode()+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + System.out.println("\tfWidgetsToElements.size() " + fWidgetsToElements.size()); //$NON-NLS-1$ + System.out.println("\tfElementsToWidgets.size() " + fElementsToWidgets.size()); //$NON-NLS-1$ + } } public IUpdatePolicy createUpdatePolicy() { @@ -478,9 +505,8 @@ public abstract class AsynchronousViewer extends StructuredViewer { if (kid == null) { // when unmapping a dummy item return; - } + } Widget[] widgets = getWidgets(kid); - fWidgetsToElements.remove(widget); if (widgets != null) { for (int i = 0; i < widgets.length; i++) { Widget item = widgets[i]; @@ -500,6 +526,14 @@ public abstract class AsynchronousViewer extends StructuredViewer { } } } + + fWidgetsToElements.remove(widget); + + if (DEBUG_CACHE) { + System.out.println("unmapped " + kid + " to " + widget + "(hashcode: " + widget.hashCode()+")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ + System.out.println("\tfWidgetsToElements.size() " + fWidgetsToElements.size()); //$NON-NLS-1$ + System.out.println("\tfElementsToWidgets.size() " + fElementsToWidgets.size()); //$NON-NLS-1$ + } } Image[] getImages(ImageDescriptor[] descriptors) { @@ -669,7 +703,9 @@ public abstract class AsynchronousViewer extends StructuredViewer { if (!force && !overrideSelection(fCurrentSelection, selection)) { return; } + fPendingSelection = selection; + if (getControl().getDisplay().getThread() == Thread.currentThread()) { attemptSelection(reveal); } else { @@ -783,21 +819,25 @@ public abstract class AsynchronousViewer extends StructuredViewer { * * @param reveal whether to reveal the selection */ - protected synchronized void attemptSelection(boolean reveal) { - if (fPendingSelection != null) { - ISelection remaining = doAttemptSelectionToWidget(fPendingSelection, reveal); - if (!fPendingSelection.equals(remaining) || - (fPendingSelection.isEmpty() && fPendingSelection.equals(remaining))) { - fPendingSelection = remaining; - if (fPendingSelection.isEmpty()) { - fPendingSelection = null; - } - ISelection currentSelection = newSelectionFromWidget(); - if (isSuppressEqualSelections() && currentSelection.equals(fCurrentSelection)) { - return; - } - updateSelection(currentSelection); - } + protected void attemptSelection(boolean reveal) { + if (fPendingSelection != null && !fPendingSelection.isEmpty()) { + ISelection currentSelection = null; + + synchronized (this) { + ISelection remaining = doAttemptSelectionToWidget(fPendingSelection, reveal); + + if (!fPendingSelection.equals(remaining) || (fPendingSelection.isEmpty() && fPendingSelection.equals(remaining))) { + fPendingSelection = remaining; + currentSelection = newSelectionFromWidget(); + if (isSuppressEqualSelections() && currentSelection.equals(fCurrentSelection)) { + return; + } + } + } + + if (currentSelection != null) { + updateSelection(currentSelection); + } } } @@ -805,11 +845,11 @@ public abstract class AsynchronousViewer extends StructuredViewer { * Controls whether selection change notification is sent even when * successive selections are equal. * - * TODO: what we really want is to fire selection change on ACTIVATE - * model change, even when selection is the same. + * TODO: what we really want is to fire selection change on ACTIVATE model + * change, even when selection is the same. * * @return whether to suppress change notification for equal successive - * selections + * selections */ protected boolean isSuppressEqualSelections() { return true; @@ -871,7 +911,7 @@ public abstract class AsynchronousViewer extends StructuredViewer { * @see org.eclipse.jface.viewers.StructuredViewer#preservingSelection(java.lang.Runnable) */ protected synchronized void preservingSelection(Runnable updateCode) { - if (fPendingSelection == null) { + if (fPendingSelection == null || fPendingSelection.isEmpty()) { ISelection oldSelection = null; try { // preserve selection diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ThreadEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ThreadEventHandler.java index 4d7a9a694..a0a40589a 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ThreadEventHandler.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ThreadEventHandler.java @@ -61,7 +61,7 @@ public class ThreadEventHandler extends DebugEventHandler { IThread thread = (IThread) event.getSource(); if (event.isEvaluation()) { ModelDelta delta = buildRootDelta(); - ModelDelta node = addPathToThraed(delta, thread); + ModelDelta node = addPathToThread(delta, thread); try { IStackFrame frame = thread.getTopStackFrame(); if (frame != null) { @@ -121,14 +121,14 @@ public class ThreadEventHandler extends DebugEventHandler { return new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NOCHANGE); } - private ModelDelta addPathToThraed(ModelDelta delta, IThread thread) { + private ModelDelta addPathToThread(ModelDelta delta, IThread thread) { delta = delta.addNode(thread.getLaunch(), IModelDelta.NOCHANGE); - return delta.addNode(thread.getDebugTarget(), IModelDelta.NOCHANGE); + return delta.addNode(thread.getDebugTarget(), IModelDelta.EXPAND); } private void fireDeltaAndClearTopFrame(IThread thread, int flags) { ModelDelta delta = buildRootDelta(); - ModelDelta node = addPathToThraed(delta, thread); + ModelDelta node = addPathToThread(delta, thread); node.addNode(thread, flags); synchronized (this) { fLastTopFrame.remove(thread); @@ -138,7 +138,7 @@ public class ThreadEventHandler extends DebugEventHandler { private void fireDeltaUpdatingTopFrame(IThread thread, int flags) { ModelDelta delta = buildRootDelta(); - ModelDelta node = addPathToThraed(delta, thread); + ModelDelta node = addPathToThread(delta, thread); IStackFrame prev = null; synchronized (this) { prev = (IStackFrame) fLastTopFrame.get(thread); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesView.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesView.java index f0ef0ef30..bf1cc15d4 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesView.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesView.java @@ -604,7 +604,7 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis setSashForm(new SashForm(parent, SWT.NONE)); // add tree viewer - final VariablesViewer variablesViewer = new VariablesViewer(getSashForm(), SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL, this); + final VariablesViewer variablesViewer = new VariablesViewer(getSashForm(), SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL, this); variablesViewer.setUseHashlookup(false); variablesViewer.getControl().addFocusListener(new FocusAdapter() { /* (non-Javadoc) diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewer.java index 0c7434e1d..315d955e4 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewer.java @@ -53,7 +53,7 @@ public class VariablesViewer extends AsynchronousTreeViewer{ */ protected void internalRefresh(Object element, Widget item) { super.internalRefresh(element, item); - if (element.equals(((IStructuredSelection)getSelection()).getFirstElement())) { + if (fView != null && element.equals(((IStructuredSelection)getSelection()).getFirstElement())) { fView.populateDetailPane(); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/InspectPopupDialog.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/InspectPopupDialog.java index 66644e00c..783bfcc78 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/InspectPopupDialog.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/ui/InspectPopupDialog.java @@ -122,7 +122,7 @@ public class InspectPopupDialog extends DebugPopup { VariablesView view = getViewToEmulate(); - fVariablesViewer = new VariablesViewer(fSashForm, SWT.NO_TRIM, null); + fVariablesViewer = new VariablesViewer(fSashForm, SWT.NO_TRIM | SWT.VIRTUAL, null); fVariablesViewer.setContext(new PresentationContext(view)); fModelPresentation = new VariablesViewModelPresentation(); |