diff options
69 files changed, 6024 insertions, 419 deletions
diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java index bb57a7a89..9a54149d9 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/ExpressionManager.java @@ -12,7 +12,6 @@ package org.eclipse.debug.internal.core; import java.io.IOException; -import com.ibm.icu.text.MessageFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -34,9 +33,7 @@ import org.eclipse.core.runtime.PlatformObject; import org.eclipse.core.runtime.Preferences; import org.eclipse.core.runtime.SafeRunner; import org.eclipse.core.runtime.Status; -import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugPlugin; -import org.eclipse.debug.core.IDebugEventSetListener; import org.eclipse.debug.core.IExpressionListener; import org.eclipse.debug.core.IExpressionManager; import org.eclipse.debug.core.IExpressionsListener; @@ -48,14 +45,16 @@ import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; +import com.ibm.icu.text.MessageFormat; + /** * The expression manager manages all registered expressions - * for the debug plugin. It is instantiated by the debug plugin + * for the debug plug-in. It is instantiated by the debug plug-in * at startup. * * @see IExpressionManager */ -public class ExpressionManager extends PlatformObject implements IExpressionManager, IDebugEventSetListener { +public class ExpressionManager extends PlatformObject implements IExpressionManager { /** * Collection of registered expressions. @@ -68,7 +67,7 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana private ListenerList fListeners = null; /** - * List of (multi) expressions listeners + * List of expressions listeners (plural) */ private ListenerList fExpressionsListeners = null; @@ -166,7 +165,6 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana return; } NodeList list= root.getChildNodes(); - boolean expressionsAdded= false; for (int i= 0, numItems= list.getLength(); i < numItems; i++) { Node node= list.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { @@ -183,15 +181,11 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana fExpressions= new Vector(list.getLength()); } fExpressions.add(expression); - expressionsAdded= true; } else { DebugPlugin.logMessage("Invalid expression entry encountered while loading watch expressions. Expression text is empty.", null); //$NON-NLS-1$ } } } - if (expressionsAdded) { - DebugPlugin.getDefault().addDebugEventListener(this); - } } /** @@ -275,7 +269,6 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana fExpressions = new Vector(expressions.length); } boolean addedWatchExpression= false; - boolean wasEmpty = fExpressions.isEmpty(); List added = new ArrayList(expressions.length); for (int i = 0; i < expressions.length; i++) { IExpression expression = expressions[i]; @@ -287,9 +280,6 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana } } } - if (wasEmpty) { - DebugPlugin.getDefault().addDebugEventListener(this); - } if (!added.isEmpty()) { fireUpdate((IExpression[])added.toArray(new IExpression[added.size()]), ADDED); } @@ -351,9 +341,6 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana expression.dispose(); } } - if (fExpressions.isEmpty()) { - DebugPlugin.getDefault().removeDebugEventListener(this); - } if (!removed.isEmpty()) { fireUpdate((IExpression[])removed.toArray(new IExpression[removed.size()]), REMOVED); storeWatchExpressions(); @@ -381,32 +368,6 @@ public class ExpressionManager extends PlatformObject implements IExpressionMana } /** - * @see IDebugEventSetListener#handleDebugEvent(DebugEvent) - */ - public void handleDebugEvents(DebugEvent[] events) { - List changed = null; - for (int i = 0; i < events.length; i++) { - DebugEvent event = events[i]; - if (event.getSource() instanceof IExpression) { - switch (event.getKind()) { - case DebugEvent.CHANGE: - if (changed == null) { - changed = new ArrayList(1); - } - changed.add(event.getSource()); - break; - default: - break; - } - } - } - if (changed != null) { - IExpression[] array = (IExpression[])changed.toArray(new IExpression[changed.size()]); - fireUpdate(array, CHANGED); - } - } - - /** * The given watch expression has changed. Update the persisted * expressions to store this change as indicated * diff --git a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java index c02fcdefd..d146514c1 100644 --- a/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java +++ b/org.eclipse.debug.core/core/org/eclipse/debug/internal/core/WatchExpression.java @@ -127,7 +127,15 @@ public class WatchExpression implements IWatchExpression { */ public void setResult(IWatchExpressionResult result) { fResult= result; - DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {new DebugEvent(this, DebugEvent.CHANGE)}); + fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.CONTENT)); + } + + /** + * Fires the given debug event + * @param event + */ + protected void fireEvent(DebugEvent event) { + DebugPlugin.getDefault().fireDebugEventSet(new DebugEvent[] {event}); } /** @@ -241,7 +249,7 @@ public class WatchExpression implements IWatchExpression { */ protected void setPending(boolean pending) { fPending= pending; - watchExpressionChanged(false); + fireEvent(new DebugEvent(this, DebugEvent.CHANGE, DebugEvent.STATE)); } /* (non-Javadoc) diff --git a/org.eclipse.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.debug.ui/META-INF/MANIFEST.MF index 31d230098..67dc4a421 100644 --- a/org.eclipse.debug.ui/META-INF/MANIFEST.MF +++ b/org.eclipse.debug.ui/META-INF/MANIFEST.MF @@ -21,6 +21,9 @@ Export-Package: org.eclipse.debug.internal.ui;x-internal:=true, org.eclipse.debug.internal.ui.launchConfigurations;x-internal:=true, org.eclipse.debug.internal.ui.memory;x-internal:=true, org.eclipse.debug.internal.ui.memory.provisional;x-internal:=true, + org.eclipse.debug.internal.ui.model;x-internal:=true, + org.eclipse.debug.internal.ui.model.elements;x-internal:=true, + org.eclipse.debug.internal.ui.model.viewers;x-internal:=true, org.eclipse.debug.internal.ui.preferences;x-internal:=true, org.eclipse.debug.internal.ui.sourcelookup;x-internal:=true, org.eclipse.debug.internal.ui.sourcelookup.browsers;x-internal:=true, diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/CollapseAllAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/CollapseAllAction.java index ec3e10397..76c1110be 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/CollapseAllAction.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/CollapseAllAction.java @@ -12,17 +12,17 @@ package org.eclipse.debug.internal.ui.actions; import org.eclipse.debug.internal.ui.DebugPluginImages; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; -import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; import org.eclipse.jface.action.Action; +import org.eclipse.jface.viewers.TreeViewer; /** * CollapseAllAction */ public class CollapseAllAction extends Action { - private AsynchronousTreeViewer fViewer; + private TreeViewer fViewer; - public CollapseAllAction(AsynchronousTreeViewer viewer) { + public CollapseAllAction(TreeViewer viewer) { super(ActionMessages.CollapseAllAction_0, DebugPluginImages.getImageDescriptor(IInternalDebugUIConstants.IMG_ELCL_COLLAPSE_ALL)); setToolTipText(ActionMessages.CollapseAllAction_0); setDisabledImageDescriptor(DebugPluginImages.getImageDescriptor(IInternalDebugUIConstants.IMG_DLCL_COLLAPSE_ALL)); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ConfigureColumnsAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ConfigureColumnsAction.java index 059c11a7a..353b7bbba 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ConfigureColumnsAction.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/ConfigureColumnsAction.java @@ -18,7 +18,7 @@ import java.util.Map; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; -import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; +import org.eclipse.debug.internal.ui.model.viewers.TreeModelViewer; import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; import org.eclipse.jface.action.Action; import org.eclipse.jface.resource.ImageDescriptor; @@ -38,7 +38,7 @@ import org.eclipse.ui.texteditor.IUpdate; */ public class ConfigureColumnsAction extends Action implements IUpdate { - private AsynchronousTreeViewer fViewer; + private TreeModelViewer fViewer; class ColumnContentProvider implements IStructuredContentProvider { @@ -98,7 +98,7 @@ public class ConfigureColumnsAction extends Action implements IUpdate { } - public ConfigureColumnsAction(AsynchronousTreeViewer viewer) { + public ConfigureColumnsAction(TreeModelViewer viewer) { setText(ActionMessages.ConfigureColumnsAction_0); setId(DebugUIPlugin.getUniqueIdentifier() + ".ConfigureColumnsAction"); //$NON-NLS-1$ PlatformUI.getWorkbench().getHelpSystem().setHelp(this, IDebugHelpContextIds.CONFIGURE_COLUMNS_ACTION); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/expressions/RemoveExpressionAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/expressions/RemoveExpressionAction.java index 603f5a68c..4382ccbae 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/expressions/RemoveExpressionAction.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/actions/expressions/RemoveExpressionAction.java @@ -34,7 +34,7 @@ public class RemoveExpressionAction extends AbstractRemoveActionDelegate { List expressions = new ArrayList(); for (int i = paths.length-1; i >=0; i--) { TreePath path = paths[i]; - Object segment = path.getSegment(1); + Object segment = path.getFirstSegment(); if (segment instanceof IExpression) { expressions.add(segment); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenCountUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenCountUpdate.java new file mode 100644 index 000000000..b17e86425 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenCountUpdate.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + + +/** + * Request monitor used to collect the number of children for an element in a viewer. + * + * @since 3.3 + */ +public interface IChildrenCountUpdate extends IPresentationUpdate { + + /** + * Returns the parent elements that children counts have been requested for. + * + * @return parent elements + */ + public Object[] getParents(); + + /** + * Sets the number of children for the given parent. + * + * @param parent parent element + * @param numChildren number of children + */ + public void setChildCount(Object parent, int numChildren); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenUpdate.java new file mode 100644 index 000000000..0e7528444 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IChildrenUpdate.java @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +/** + * Context sensitive children update request for a parent and subrange of its + * children. + * + * @since 3.3 + */ +public interface IChildrenUpdate extends IPresentationUpdate { + + /** + * Returns the parent element that children are being requested for. + * + * @return parent element + */ + public Object getParent(); + + /** + * Returns the offset at which children have been requested for. This is + * the index of the first child being requested. + * + * @return offset at which children have been requested for + */ + public int getOffset(); + + /** + * Returns the number of children requested. + * + * @return number of children requested + */ + public int getLength(); + + /** + * Sets the child for this request's parent at the given offset. + * + * @param child child + * @param index child offset + * + * TODO: what to do with <code>null</code> + */ + public void setChild(Object child, int offset); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementCompareRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementCompareRequest.java new file mode 100644 index 000000000..aafd86812 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementCompareRequest.java @@ -0,0 +1,42 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +import org.eclipse.ui.IMemento; + +/** + * Request to compare an element to a previously created memento. + * + * @since 3.3 + */ +public interface IElementCompareRequest extends IPresentationUpdate { + + /** + * The element to compare against this request's memento. + * + * @return element + */ + public Object getElement(); + + /** + * The memento to compare this request's element. + * + * @return memento + */ + public IMemento getMemento(); + + /** + * Sets whether this request's memento represents this requests's element. + * + * @param equal whether the memento represents the element + */ + public void setEqual(boolean equal); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementContentProvider.java new file mode 100644 index 000000000..ba6c68f28 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementContentProvider.java @@ -0,0 +1,36 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + + +/** + * Provides content for an element in a virtual viewer. + * + * @since 3.3 + */ +public interface IElementContentProvider { + + /** + * Updates the number of children for the given parent elements in the + * specified request. + * + * @param update specifies counts to update and stores result + */ + public void update(IChildrenCountUpdate update); + + /** + * Updates children as requested by the update. + * + * @param update specifies children to update and stores result + */ + public void update(IChildrenUpdate update); + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementLabelProvider.java new file mode 100644 index 000000000..ffa41b992 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementLabelProvider.java @@ -0,0 +1,28 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +/** + * Provides context sensitive labels. Can be registered as an adapter for an element, + * or implemented directly. + * + * @since 3.3 + */ +public interface IElementLabelProvider { + + /** + * Updates the specified label. + * + * @param update specifies the element and context for which a label is requested and + * stores updated label attributes + */ + public void update(ILabelUpdate update); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoProvider.java new file mode 100644 index 000000000..06b69f00e --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoProvider.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +/** + * Used to save and restore viewer selection/expansion state. A memento + * provider adapter should be available from a viewer input element + * in order to support viewer state save/restore. + * + * @since 3.3 + */ +public interface IElementMementoProvider { + + /** + * Creates and stores a memento for the element specified in the request. + * The request should be cancelled if a memento is not supported for the + * specified element or context. + * + * @param request specifies element and provides memento store + */ + public void encodeElement(IElementMementoRequest request); + + /** + * Determines if a memento represents the element specified in the request. + * + * @param request specifies element and previously created memento + */ + public void compareElement(IElementCompareRequest request); + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoRequest.java new file mode 100644 index 000000000..b0900ca8b --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IElementMementoRequest.java @@ -0,0 +1,35 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +import org.eclipse.ui.IMemento; + +/** + * Request to store a memento for an element in a specific context. + * + * @since 3.3 + */ +public interface IElementMementoRequest extends IPresentationUpdate { + + /** + * Returns the element for which the memento has been requested. + * + * @return element + */ + public Object getElement(); + + /** + * Returns the memento used to persist the element. + * + * @return memento + */ + public IMemento getMemento(); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/ILabelUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/ILabelUpdate.java new file mode 100644 index 000000000..d4aee312d --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/ILabelUpdate.java @@ -0,0 +1,72 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; + +/** + * Context sensitive label update request for an element. + * + * @since 3.3 + */ +public interface ILabelUpdate extends IPresentationUpdate { + + /** + * Returns the element the label update is for. + * + * @return associated element + */ + public Object getElement(); + + /** + * Returns the column the label is for, or <code>null</code> if no columns. + * + * @return column id or <code>null</code> + */ + public String getColumnId(); + + /** + * Sets the text of the label. Cannot be <code>null</code>. + * + * @param text + */ + public void setLabel(String text); + + /** + * Sets the font of the label. + * + * @param fontData + */ + public void setFontData(FontData fontData); + + /** + * Sets the image of the label. + * + * @param image + */ + public void setImageDescriptor(ImageDescriptor image); + + /** + * Sets the foreground color of the label. + * + * @param foreground + */ + public void setForeground(RGB foreground); + + /** + * Sets the background color of the label. + * + * @param background + */ + public void setBackground(RGB background); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IPresentationUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IPresentationUpdate.java new file mode 100644 index 000000000..d64454b0a --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/IPresentationUpdate.java @@ -0,0 +1,29 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model; + +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; + +/** + * A context sensitive update request. + * + * @since 3.3 + */ +public interface IPresentationUpdate extends IAsynchronousRequestMonitor { + + /** + * Returns the presentation context this update was requested in. + * + * @return context this update was requested in + */ + public IPresentationContext getPresentationContext(); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugElementLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugElementLabelProvider.java new file mode 100644 index 000000000..421413df4 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugElementLabelProvider.java @@ -0,0 +1,109 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IDebugElement; +import org.eclipse.debug.internal.ui.DelegatingModelPresentation; +import org.eclipse.debug.internal.ui.LazyModelPresentation; +import org.eclipse.debug.internal.ui.model.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.views.launch.DebugElementHelper; +import org.eclipse.debug.ui.IDebugModelPresentation; +import org.eclipse.debug.ui.IDebugView; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.IWorkbenchPart; + +/** + * @since 3.3 + */ +public class DebugElementLabelProvider extends ElementLabelProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#retrieveLabel(org.eclipse.debug.internal.ui.model.ILabelUpdate) + */ + protected void retrieveLabel(ILabelUpdate update) throws CoreException { + DelegatingModelPresentation presentation = DebugElementHelper.getPresentation(); + // Honor view specific settings in a debug view by copying model presentation settings + // into the debug element helper's presentation before we get the label. This allows + // for qualified name and type name settings to remain in tact. + Object element = update.getElement(); + IPresentationContext context = update.getPresentationContext(); + if (element instanceof IDebugElement && context.getPart() instanceof IDebugView) { + IWorkbenchPart part = context.getPart(); + if (part instanceof IDebugView) { + IDebugModelPresentation pres = ((IDebugView)part).getPresentation(((IDebugElement)element).getModelIdentifier()); + Map settings = null; + synchronized (presentation) { + if (pres instanceof DelegatingModelPresentation) { + settings = ((DelegatingModelPresentation)pres).getAttributes(); + } else if (pres instanceof LazyModelPresentation) { + settings = ((LazyModelPresentation)pres).getAttributes(); + } + if (settings != null) { + Iterator iterator = settings.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Entry) iterator.next(); + presentation.setAttribute((String) entry.getKey(), entry.getValue()); + } + super.retrieveLabel(update); + return; + } + } + } + } + super.retrieveLabel(update); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getLabel(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, java.lang.String) + */ + protected String getLabel(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return DebugElementHelper.getLabel(element); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getBackground(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, java.lang.String) + */ + protected RGB getBackground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return DebugElementHelper.getBackground(element); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getFontDatas(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, java.lang.String) + */ + protected FontData getFontData(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return DebugElementHelper.getFont(element); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getForeground(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, java.lang.String) + */ + protected RGB getForeground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return DebugElementHelper.getForeground(element); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getImageDescriptor(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, java.lang.String) + */ + protected ImageDescriptor getImageDescriptor(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return DebugElementHelper.getImageDescriptor(element); + } + + + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugTargetContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugTargetContentProvider.java new file mode 100644 index 000000000..9e3a0a8fc --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/DebugTargetContentProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IDebugTarget; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @since 3.3 + */ +public class DebugTargetContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return ((IDebugTarget)element).getThreads().length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return IDebugUIConstants.ID_DEBUG_VIEW.equals(id); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(((IDebugTarget)parent).getThreads(), index, length); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementContentProvider.java new file mode 100644 index 000000000..4e02a4105 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementContentProvider.java @@ -0,0 +1,207 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.internal.ui.model.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.model.IChildrenUpdate; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.AsynchronousSchedulingRuleFactory; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; + +/** + * @since 3.3 + */ +public abstract class ElementContentProvider implements IElementContentProvider { + + protected static final Object[] EMPTY = new Object[0]; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementContentProvider#updateChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext, org.eclipse.debug.internal.ui.model.IElementRequestMonitor) + */ + public void update(final IChildrenUpdate update) { + Job job = new Job("Retrieving Children") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (!monitor.isCanceled()) { + retrieveChildren(update); + } + return Status.OK_STATUS; + } + }; + job.setSystem(true); + job.setRule(getRetrieveChildRule(update.getParent(), update.getPresentationContext())); // TODO: + job.schedule(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementContentProvider#update(org.eclipse.debug.internal.ui.model.IChildrenCountUpdate) + */ + public void update(final IChildrenCountUpdate update) { + Job job = new Job("Computing hasChildren") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (!monitor.isCanceled()) { + retrieveChildCount(update); + } + return Status.OK_STATUS; + } + }; + job.setSystem(true); + // TODO: rule + job.schedule(); + } + + /** + * Returns the scheduling rule for jobs retrieving children. + * + * @param parent + * @param context + * @return scheduling rule or <code>null</code> + */ + protected ISchedulingRule getRetrieveChildRule(Object parent, IPresentationContext context) { + return AsynchronousSchedulingRuleFactory.getDefault().newSerialPerPartRule(context); + } + + /** + * Returns the scheduling rule for jobs determining an element's child count. + * + * @param parent + * @param context + * @return scheduling rule or <code>null</code> + */ + protected ISchedulingRule getRetrieveChildCountRule(Object parent, IPresentationContext context) { + return AsynchronousSchedulingRuleFactory.getDefault().newSerialPerPartRule(context); + } + + /** + * Computes the children for the given parent in the specified context. + * + * @param update update request + */ + protected void retrieveChildren(IChildrenUpdate update) { + if (!update.isCanceled()) { + IStatus status = Status.OK_STATUS; + try { + IPresentationContext context = update.getPresentationContext(); + if (supportsContext(context)) { + int offset = update.getOffset(); + Object[] children = getChildren(update.getParent(), offset, update.getLength(), context); + if (children != null) { + for (int i = 0; i < children.length; i++) { + update.setChild(children[i], offset + i); + } + } + } + } catch (CoreException e) { + status = e.getStatus(); + } + update.setStatus(status); + update.done(); + } + } + + /** + * Computes whether the given element is a container. + * + * @param parent potential parent + * @param context presentation context + * @param monitor result to report to + */ + protected void retrieveChildCount(IChildrenCountUpdate update) { + if (!update.isCanceled()) { + IStatus status = Status.OK_STATUS; + try { + IPresentationContext context = update.getPresentationContext(); + Object[] parents = update.getParents(); + if (supportsContext(context)) { + for (int i = 0; i < parents.length; i++) { + Object parent = parents[i]; + update.setChildCount(parent, getChildCount(parent, context)); + } + } else { + for (int i = 0; i < parents.length; i++) { + Object parent = parents[i]; + update.setChildCount(parent, 0); + } + } + } catch (CoreException e) { + status = e.getStatus(); + } + update.setStatus(status); + update.done(); + } + } + + /** + * Returns the children for the given parent at the specified index in the specified context + * or <code>null</code> if none. + * + * @param parent element to retrieve children for + * @param index child index + * @param length number of children to retrieve + * @param context context children will be presented in + * @return child or <code>null</code> + * @throws CoreException if an exception occurs retrieving child + */ + protected abstract Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException; + + /** + * Returns the number of children for the given element. + * + * @param element element that may have children + * @param context context element will be presented in + * @return number of children + * @throws CoreException if an exception occurs determining child count + */ + protected abstract int getChildCount(Object element, IPresentationContext context) throws CoreException; + + /** + * Returns whether this adapter supports the given context. + * + * @param context + * @return whether this adapter supports the given context + */ + protected boolean supportsContext(IPresentationContext context) { + return supportsContextId(context.getId()); + } + + /** + * Returns whether this adapter provides content in the specified context id. + * + * @param id part id + * @return whether this adapter provides content in the specified context id + */ + protected abstract boolean supportsContextId(String id); + + /** + * Returns the element at the given index or <code>null</code> if none. + * + * @param elements + * @param index + * @return element or <code>null</code> + */ + protected Object[] getElements(Object[] elements, int index, int length) { + int max = elements.length; + if (index < max && ((index + length) > max)) { + length = max - index; + } + if ((index + length) <= elements.length) { + Object[] sub = new Object[length]; + System.arraycopy(elements, index, sub, 0, length); + return sub; + } + return null; + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementLabelProvider.java new file mode 100644 index 000000000..63118d413 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementLabelProvider.java @@ -0,0 +1,255 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import java.util.LinkedList; +import java.util.NoSuchElementException; + +import org.eclipse.core.runtime.CoreException; +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.debug.internal.ui.model.IElementLabelProvider; +import org.eclipse.debug.internal.ui.model.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.ui.progress.UIJob; + +/** + * @since 3.3 + */ +public abstract class ElementLabelProvider implements IElementLabelProvider { + + private Job fLabelJob = null; + + interface ILabelJob { + /** + * Returns whether the update was queued. + * + * @param update update + * @return whether the update was queued + */ + public boolean queue(ILabelUpdate update); + } + + class LabelJob extends Job implements ILabelJob { + + private LabelUpdater fUpdater = new LabelUpdater(); + + public LabelJob() { + super("Label Job"); //$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) { + fUpdater.run(); + return Status.OK_STATUS; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider.ILabelJob#queue(org.eclipse.debug.internal.ui.model.ILabelUpdate) + */ + public boolean queue(ILabelUpdate update) { + return fUpdater.queue(update); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#shouldRun() + */ + public boolean shouldRun() { + return fUpdater.shouldRun(); + } + + } + + class UILabelJob extends UIJob implements ILabelJob { + + private LabelUpdater fUpdater = new LabelUpdater(); + + public UILabelJob() { + super("Label Job"); //$NON-NLS-1$ + setSystem(true); + } + + /* (non-Javadoc) + * @see org.eclipse.ui.progress.UIJob#runInUIThread(org.eclipse.core.runtime.IProgressMonitor) + */ + public IStatus runInUIThread(IProgressMonitor monitor) { + fUpdater.run(); + return Status.OK_STATUS; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider.ILabelJob#queue(org.eclipse.debug.internal.ui.model.ILabelUpdate) + */ + public boolean queue(ILabelUpdate update) { + return fUpdater.queue(update); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.jobs.Job#shouldRun() + */ + public boolean shouldRun() { + return fUpdater.shouldRun(); + } + } + + /** + * Queue of label updates + */ + class LabelUpdater implements Runnable { + + LinkedList fQueue = new LinkedList(); + + public synchronized boolean queue(ILabelUpdate update) { + if (fQueue == null) { + return false; + } else { + fQueue.addLast(update); + return true; + } + } + + /* (non-Javadoc) + * @see java.lang.Runnable#run() + */ + public void run() { + ILabelUpdate update = getNextUpdate(); + while (update != null) { + try { + retrieveLabel(update); + } catch (CoreException e) { + update.setStatus(e.getStatus()); + } + update.done(); + update = getNextUpdate(); + } + } + + public synchronized ILabelUpdate getNextUpdate() { + if (fQueue == null) { + return null; + } + ILabelUpdate update = null; + try { + update = (ILabelUpdate) fQueue.removeFirst(); + } catch (NoSuchElementException e) { + fQueue = null; + } + return update; + } + + public boolean shouldRun() { + return fQueue != null; + } + } + + /** + * Retrieves label attributes for the specified update. + * + * @param update + */ + protected void retrieveLabel(ILabelUpdate update) throws CoreException { + String columnId = update.getColumnId(); + IPresentationContext presentationContext = update.getPresentationContext(); + Object element = update.getElement(); + update.setLabel(getLabel(element, presentationContext, columnId)); + update.setImageDescriptor(getImageDescriptor(element, presentationContext, columnId)); + update.setBackground(getBackground(element, presentationContext, columnId)); + update.setForeground(getForeground(element, presentationContext, columnId)); + update.setFontData(getFontData(element, presentationContext, columnId)); + } + + /** + * @param element + * @param presentationContext + * @param columnId + * @return font information or <code>null</code> + */ + protected FontData getFontData(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return null; + } + + /** + * @param element + * @param presentationContext + * @param columnId + * @return color or <code>null</code> + */ + protected RGB getForeground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return null; + } + + /** + * @param element + * @param presentationContext + * @param columnId + * @return color or <code>null</code> + */ + protected RGB getBackground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return null; + } + + /** + * @param element + * @param presentationContext + * @param columnId + * @return image descriptor or <code>null</code> + */ + protected ImageDescriptor getImageDescriptor(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + return null; + } + + /** + * @param element + * @param presentationContext + * @param columnId + * @return label + */ + protected abstract String getLabel(Object element, IPresentationContext presentationContext, String columnId) throws CoreException; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementLabelProvider#updateLabel(org.eclipse.debug.internal.ui.model.ILabelUpdate) + */ + public synchronized void update(ILabelUpdate update) { + if (fLabelJob == null) { + fLabelJob = newLabelJob(update); + } + if (!((ILabelJob)fLabelJob).queue(update)) { + fLabelJob = newLabelJob(update); + ((ILabelJob)fLabelJob).queue(update); + } + // TODO: rule + fLabelJob.schedule(); + } + + private Job newLabelJob(ILabelUpdate update) { + if (requiresUIJob(update)) { + return new UILabelJob(); + } else { + return new LabelJob(); + } + } + + /** + * Returns whether a UI job should be used for updates versus a non-UI job. + */ + protected boolean requiresUIJob(ILabelUpdate update) { + return false; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementMementoProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementMementoProvider.java new file mode 100644 index 000000000..32d6447b4 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ElementMementoProvider.java @@ -0,0 +1,94 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +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.debug.internal.ui.model.IElementCompareRequest; +import org.eclipse.debug.internal.ui.model.IElementMementoProvider; +import org.eclipse.debug.internal.ui.model.IElementMementoRequest; +import org.eclipse.ui.IMemento; + +/** + * @since 3.3 + */ +public abstract class ElementMementoProvider implements IElementMementoProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementMementoProvider#compareElement(org.eclipse.debug.internal.ui.model.IElementCompareRequest) + */ + public void compareElement(final IElementCompareRequest request) { + Job job = new Job("compare element") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + Object element = request.getElement(); + IMemento memento = request.getMemento(); + try { + request.setEqual(isEqual(element, memento)); + } catch (CoreException e) { + request.setStatus(e.getStatus()); + } + request.done(); + return Status.OK_STATUS; + } + }; + job.setSystem(true); + // TODO: rule + job.schedule(); + } + + /** + * Returns whether the memento represents the given element. + * + * @param element + * @param memento + * @return whether the memento represents the given element + */ + protected abstract boolean isEqual(Object element, IMemento memento) throws CoreException; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementMementoProvider#encodeElement(org.eclipse.debug.internal.ui.model.IElementMementoRequest) + */ + public void encodeElement(final IElementMementoRequest request) { + Job job = new Job("encode element") { //$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + Object element = request.getElement(); + IMemento memento = request.getMemento(); + try { + if (!encodeElement(element, memento)) { + request.setCanceled(true); + } + } catch (CoreException e) { + request.setStatus(e.getStatus()); + } + request.done(); + return Status.OK_STATUS; + } + }; + job.setSystem(true); + // TODO: rule + job.schedule(); + } + + /** + * Encodes the specified element into the given memento. + * Returns whether the element could be encoded + * + * @param element + * @param memento + * @return false if cancelled/not supported + * @throws CoreException + */ + protected abstract boolean encodeElement(Object element, IMemento memento) throws CoreException; + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionContentProvider.java new file mode 100644 index 000000000..e83e1ad3f --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionContentProvider.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IErrorReportingExpression; +import org.eclipse.debug.core.model.IExpression; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; + +/** + * @since 3.3 + */ +public class ExpressionContentProvider extends VariableContentProvider { + + protected Object[] getAllChildren(Object parent, IPresentationContext context) throws CoreException { + if (parent instanceof IErrorReportingExpression) { + IErrorReportingExpression expression = (IErrorReportingExpression) parent; + if (expression.hasErrors()) { + return expression.getErrorMessages(); + } + } + if (parent instanceof IExpression) { + IExpression expression = (IExpression) parent; + IValue value = expression.getValue(); + if (value != null) { + return getValueChildren(expression, value, context); + } + } + return EMPTY; + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionLabelProvider.java new file mode 100644 index 000000000..b09c72a82 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionLabelProvider.java @@ -0,0 +1,33 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IErrorReportingExpression; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.swt.graphics.RGB; + +/** + * @since 3.3 + */ +public class ExpressionLabelProvider extends VariableLabelProvider { + + protected RGB getForeground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + if (element instanceof IErrorReportingExpression) { + IErrorReportingExpression expression = (IErrorReportingExpression) element; + if (expression.hasErrors()) { + return new RGB(255, 0, 0); + } + } + return super.getForeground(element, presentationContext, columnId); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionManagerContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionManagerContentProvider.java new file mode 100644 index 000000000..3979c7a23 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ExpressionManagerContentProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.IExpressionManager; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * + */ +public class ExpressionManagerContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return ((IExpressionManager) element).getExpressions().length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return ((IExpressionManager) parent).getExpressions(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return id.equals(IDebugUIConstants.ID_EXPRESSION_VIEW); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchContentProvider.java new file mode 100644 index 000000000..22b5a9c52 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchContentProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @since 3.3 + */ +public class LaunchContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return ((ILaunch)element).getChildren().length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return IDebugUIConstants.ID_DEBUG_VIEW.equals(id); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(((ILaunch)parent).getChildren(), index, length); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchManagerContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchManagerContentProvider.java new file mode 100644 index 000000000..cc8f1cabf --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/LaunchManagerContentProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.ILaunchManager; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @since 3.3 + */ +public class LaunchManagerContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return ((ILaunchManager)element).getLaunches().length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return IDebugUIConstants.ID_DEBUG_VIEW.equals(id); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(((ILaunchManager)parent).getLaunches(),index, length); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/StackFrameContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/StackFrameContentProvider.java new file mode 100644 index 000000000..8ef0348e4 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/StackFrameContentProvider.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IStackFrame; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @since 3.3 + */ +public class StackFrameContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return getAllChildren(element, context).length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(getAllChildren(parent, context), index, length); + } + + protected Object[] getAllChildren(Object parent, IPresentationContext context) throws CoreException { + String id = context.getId(); + IStackFrame frame = (IStackFrame) parent; + if (id.equals(IDebugUIConstants.ID_VARIABLE_VIEW)) { + return frame.getVariables(); + } else if (id.equals(IDebugUIConstants.ID_REGISTER_VIEW)) { + return frame.getRegisterGroups(); + } + return EMPTY; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return id.equals(IDebugUIConstants.ID_VARIABLE_VIEW) || id.equals(IDebugUIConstants.ID_REGISTER_VIEW); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ThreadContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ThreadContentProvider.java new file mode 100644 index 000000000..16d67a25d --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/ThreadContentProvider.java @@ -0,0 +1,44 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IThread; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; + +/** + * @since 3.3 + */ +public class ThreadContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return ((IThread)element).getStackFrames().length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return IDebugUIConstants.ID_DEBUG_VIEW.equals(id); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(((IThread)parent).getStackFrames(), index, length); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableContentProvider.java new file mode 100644 index 000000000..94e7a57e5 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableContentProvider.java @@ -0,0 +1,213 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import java.util.ArrayList; +import java.util.List; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILogicalStructureType; +import org.eclipse.debug.core.model.IDebugElement; +import org.eclipse.debug.core.model.IIndexedValue; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.core.model.IVariable; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.views.variables.IndexedVariablePartition; +import org.eclipse.debug.internal.ui.views.variables.VariablesView; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.ui.IWorkbenchPart; + +/** + * @since 3.3 + */ +public class VariableContentProvider extends ElementContentProvider { + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return getAllChildren(element, context).length; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return getElements(getAllChildren(parent, context), index, length); + } + + protected Object[] getAllChildren(Object parent, IPresentationContext context) throws CoreException { + IVariable variable = (IVariable) parent; + IValue value = variable.getValue(); + if (value != null) { + return getValueChildren(variable, value, context); + } + return EMPTY; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return id.equals(IDebugUIConstants.ID_EXPRESSION_VIEW) || id.equals(IDebugUIConstants.ID_VARIABLE_VIEW) || id.equals(IDebugUIConstants.ID_REGISTER_VIEW); + } + + /** + * Return whether to show compute a logical structure or a raw structure + * in the specified context + * + * @return whether to show compute a logical structure or a raw structure + * in the specified context + */ + protected boolean isShowLogicalStructure(IPresentationContext context) { + IWorkbenchPart part = context.getPart(); + if (part instanceof VariablesView) { + return ((VariablesView) part).isShowLogicalStructure(); + } + return false; + } + + /** + * Returns the number of entries that should be displayed in each partition + * of an indexed collection. + * + * @return the number of entries that should be displayed in each partition + * of an indexed collection + */ + protected int getArrayPartitionSize() { + // TODO: should fix this with a user preference + return 100; + } + + /** + * Returns any logical value for the raw value in the specified context + * + * @param value + * @param context + * @return + */ + protected IValue getLogicalValue(IValue value, IPresentationContext context) { + return getLogicalValue(value, new ArrayList(), context); + } + + /** + * Returns children for the given value, creating array partitions if + * required + * + * @param parent expression or variable containing the given value + * @param value the value to retrieve children for + * @param context the context in which children have been requested + * @return children for the given value, creating array partitions if + * required + * @throws CoreException + */ + protected Object[] getValueChildren(IDebugElement parent, IValue value, IPresentationContext context) throws CoreException { + if (value == null) { + return EMPTY; + } + IValue logicalValue = getLogicalValue(value, context); + if (logicalValue instanceof IIndexedValue) { + IIndexedValue indexedValue = (IIndexedValue) logicalValue; + int partitionSize = computeParitionSize(indexedValue); + if (partitionSize > 1) { + int offset = indexedValue.getInitialOffset(); + int length = indexedValue.getSize(); + int numPartitions = length / partitionSize; + int remainder = length % partitionSize; + if (remainder > 0) { + numPartitions++; + } + IVariable[] partitions = new IVariable[numPartitions]; + for (int i = 0; i < (numPartitions - 1); i++) { + partitions[i] = new IndexedVariablePartition(parent, indexedValue, offset, partitionSize); + offset = offset + partitionSize; + } + if (remainder == 0) { + remainder = partitionSize; + } + partitions[numPartitions - 1] = new IndexedVariablePartition(parent, indexedValue, offset, remainder); + return partitions; + } + } + if (logicalValue == null) { + // safeguard against an structure type returning null + logicalValue = value; + } + return logicalValue.getVariables(); + } + + /** + * Returns the partition size to use for the given indexed value. The + * partition size is computed by determining the number of levels that an + * indexed collection must be nested in order to partition the collection + * sub-collections of the preferred partition size. + * + * @param value + * indexed value + * @return size of paritions the value should be subdivided into + */ + protected int computeParitionSize(IIndexedValue value) { + int partitionSize = 1; + try { + int length = value.getSize(); + int partitionDepth = 0; + int preferredSize = getArrayPartitionSize(); + int remainder = length % preferredSize; + length = length / preferredSize; + while (length > 0) { + if (remainder == 0 && length == 1) { + break; + } + partitionDepth++; + remainder = length % preferredSize; + length = length / preferredSize; + } + for (int i = 0; i < partitionDepth; i++) { + partitionSize = partitionSize * preferredSize; + } + } catch (DebugException e) { + } + return partitionSize; + } + + /** + * Returns any logical value for the raw value. This method will recurse + * over the returned value until the same structure is encountered again (to + * avoid infinite recursion). + * + * @param value + * @param previousStructureIds + * the list of logical structures that have already been applied + * to the returned value during the recursion of this method. + * Callers should always pass in a new, empty list. + * @return + */ + protected IValue getLogicalValue(IValue value, List previousStructureIds, IPresentationContext context) { + if (isShowLogicalStructure(context)) { + ILogicalStructureType[] types = DebugPlugin.getLogicalStructureTypes(value); + if (types.length > 0) { + ILogicalStructureType type = DebugPlugin.getDefaultStructureType(types); + if (type != null && !previousStructureIds.contains(type.getId())) { + try { + value = type.getLogicalStructure(value); + previousStructureIds.add(type.getId()); + return getLogicalValue(value, previousStructureIds, context); + } catch (CoreException e) { + // unable to display logical structure + } + } + } + } + return value; + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableLabelProvider.java new file mode 100644 index 000000000..dc33c79df --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariableLabelProvider.java @@ -0,0 +1,143 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IValue; +import org.eclipse.debug.core.model.IVariable; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.DefaultLabelProvider; +import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; +import org.eclipse.debug.internal.ui.elements.adapters.VariableColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.ui.IDebugUIConstants; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.swt.graphics.RGB; + +/** + * @since 3.3 + */ +public class VariableLabelProvider extends DebugElementLabelProvider { + + protected RGB getBackground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + if (columnId != null) { + if (element instanceof IVariable) { + IVariable variable = (IVariable) element; + if (variable.hasValueChanged()) { + return DebugUIPlugin.getPreferenceColor(IInternalDebugUIConstants.PREF_CHANGED_VALUE_BACKGROUND).getRGB(); + } + } + } + return super.getBackground(element, presentationContext, columnId); + } + + protected RGB getForeground(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + if (columnId == null) { + if (element instanceof IVariable) { + IVariable variable = (IVariable) element; + if (variable.hasValueChanged()) { + return DebugUIPlugin.getPreferenceColor(IDebugUIConstants.PREF_CHANGED_DEBUG_ELEMENT_COLOR).getRGB(); + } + } + } + return super.getForeground(element, presentationContext, columnId); + } + + protected ImageDescriptor getImageDescriptor(Object element, IPresentationContext presentationContext, String columnId) throws CoreException { + if (columnId == null || VariableColumnPresentation.COLUMN_VARIABLE_NAME.equals(columnId)) { + return super.getImageDescriptor(element, presentationContext, columnId); + } + return null; + } + + protected String getLabel(Object element, IPresentationContext context, String columnId) throws CoreException { + if (columnId == null) { + return escapeSpecialChars(super.getLabel(element, context, columnId)); + } else { + IVariable variable = (IVariable) element; + IValue value = variable.getValue(); + return getColumnText(variable, value, context, columnId); + } + } + + /** + * Returns text for a specific columns for the variable/value. + * + * @param variable + * @param value + * @param context + * @param columnId + * @return + * @throws CoreException + */ + protected String getColumnText(IVariable variable, IValue value, IPresentationContext context, String columnId) throws CoreException { + if (VariableColumnPresentation.COLUMN_VARIABLE_NAME.equals(columnId)) { + return getVariableName(variable, context); + } else if (VariableColumnPresentation.COLUMN_VARIABLE_TYPE.equals(columnId)) { + return getVariableTypeName(variable, context); + } else if (VariableColumnPresentation.COLUMN_VARIABLE_VALUE.equals(columnId)) { + return getValueText(variable, value, context); + } else if (VariableColumnPresentation.COLUMN_VALUE_TYPE.equals(columnId)) { + return getValueTypeName(variable, value, context); + } + return null; + } + + /** + * Returns the name of the given variable to display in <code>COLUMN_VARIABLE_NAME</code>. + * + * @param variable + * @return variable name + * @throws CoreException + */ + protected String getVariableName(IVariable variable, IPresentationContext context) throws CoreException { + return variable.getName(); + } + + /** + * Returns the type name of the given variable to display in <code>COLUMN_VARIABLE_TYPE</code>. + * + * @param variable + * @return variable type name + * @throws CoreException + */ + protected String getVariableTypeName(IVariable variable, IPresentationContext context) throws CoreException { + return variable.getReferenceTypeName(); + } + + /** + * Returns the label for the given value's type to display in <code>COLUMN_VARIABLE_VALUE</code> + * + * @param variable + * @param value + * @return value label + * @throws CoreException + */ + protected String getValueTypeName(IVariable variable, IValue value, IPresentationContext context) throws CoreException { + return value.getReferenceTypeName(); + } + + /** + * Returns the label for the given value to display in <code>COLUMN_VALUE_TYPE</code> + * + * @param variable + * @param value + * @return value label + * @throws CoreException + */ + protected String getValueText(IVariable variable, IValue value, IPresentationContext context) throws CoreException { + return escapeSpecialChars(value.getValueString()); + } + + protected String escapeSpecialChars(String label) { + return DefaultLabelProvider.escapeSpecialChars(label); + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariablesViewElementMementoProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariablesViewElementMementoProvider.java new file mode 100644 index 000000000..2ebac02be --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/elements/VariablesViewElementMementoProvider.java @@ -0,0 +1,65 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.elements; + +import org.eclipse.core.runtime.CoreException; +import org.eclipse.debug.core.model.IStackFrame; +import org.eclipse.debug.core.model.IVariable; +import org.eclipse.ui.IMemento; + +/** + * @since 3.3 + */ +public class VariablesViewElementMementoProvider extends ElementMementoProvider { + + /** + * memento attribute + */ + private static final String ELEMENT_NAME = "ELEMENT_NAME"; //$NON-NLS-1$ + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementMementoProvider#encodeElement(java.lang.Object, org.eclipse.ui.IMemento) + */ + protected boolean encodeElement(Object element, IMemento memento) throws CoreException { + if (element instanceof IStackFrame) { + IStackFrame frame = (IStackFrame) element; + memento.putString(ELEMENT_NAME, frame.getName()); + } else if (element instanceof IVariable) { + IVariable variable = (IVariable) element; + memento.putString(ELEMENT_NAME, variable.getName()); + } else { + return false; + } + return true; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementMementoProvider#isEqual(java.lang.Object, org.eclipse.ui.IMemento) + */ + protected boolean isEqual(Object element, IMemento memento) throws CoreException { + String mementoName = memento.getString(ELEMENT_NAME); + if (mementoName != null) { + String elementName = null; + if (element instanceof IStackFrame) { + IStackFrame frame = (IStackFrame) element; + elementName = frame.getName(); + } else if (element instanceof IVariable) { + IVariable variable = (IVariable) element; + elementName = variable.getName(); + } + if (elementName != null) { + return elementName.equals(mementoName); + } + } + return false; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenCountUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenCountUpdate.java new file mode 100644 index 000000000..689093bc9 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenCountUpdate.java @@ -0,0 +1,113 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.eclipse.debug.internal.ui.model.IChildrenCountUpdate; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; + +/** + * @since 3.3 + */ +class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountUpdate { + + private Map fCounts; + private Set fParents = new HashSet(); + private boolean fStarted = false; + private IElementContentProvider fPresentation; + + /** + * @param contentProvider + */ + public ChildrenCountUpdate(ModelContentProvider contentProvider, IElementContentProvider presentation) { + super(contentProvider); + fPresentation = presentation; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ViewerUpdateMonitor#performUpdate() + */ + protected void performUpdate() { + Iterator iterator = fCounts.entrySet().iterator(); + while (iterator.hasNext()) { + Entry entry = (Entry) iterator.next(); + int count = ((Integer)(entry.getValue())).intValue(); + Object parent = entry.getKey(); + TreePath[] treePaths = getContentProvider().getTreePaths(parent); + int viewCount = count; + if (treePaths.length > 0) { + // all children are filtered the same per parent occurrence + if (count == 0) { + getContentProvider().clearFilters(treePaths[0]); + } else { + viewCount = getContentProvider().modelToViewChildCount(treePaths[0], count); + } + } + //System.out.println("setChildCount(" + parent + ", modelCount: " + count + " viewCount: " + viewCount + ")"); + ((TreeViewer)(getContentProvider().getViewer())).setChildCount(parent, viewCount); + if (treePaths.length > 0) { + if (treePaths[0].getSegmentCount() > 0) { + getContentProvider().doRestore(treePaths[0]); + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildCountRequestMonitor#setChildCount(int) + */ + public void setChildCount(Object parent, int numChildren) { + if (fCounts == null) { + fCounts = new HashMap(); + } + fCounts.put(parent, new Integer(numChildren)); + } + + /** + * @param element + * @return + */ + protected boolean coalesce(Object element) { + fParents.add(element); + return true; + } + + /** + * + */ + protected void start() { + synchronized (this) { + if (fStarted) { + return; + } + fStarted = true; + } + TreeModelContentProvider contentProvider = (TreeModelContentProvider)getContentProvider(); + contentProvider.countRequestStarted(fPresentation); + fPresentation.update(this); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildrenCountUpdate#getParents() + */ + public Object[] getParents() { + return fParents.toArray(); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenUpdate.java new file mode 100644 index 000000000..8d6eef274 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ChildrenUpdate.java @@ -0,0 +1,146 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.model.IChildrenUpdate; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.jface.viewers.TreePath; + +/** + * @since 3.3 + */ +class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpdate { + + private Object fParent; + private TreePath fParentPath; + private Object[] fElements; + private int fIndex; + private int fLength; + private IElementContentProvider fContentProvider; + private boolean fStarted = false; + + /** + * Constructs a request to update an element + * + * @param node node to update + * @param model model containing the node + */ + public ChildrenUpdate(ModelContentProvider provider, Object parent, TreePath parentPath, int index, IElementContentProvider presentation) { + super(provider); + fParentPath = parentPath; + fIndex = index; + fLength = 1; + fContentProvider = presentation; + fParent = parent; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.ui.viewers.AsynchronousRequestMonitor#performUpdate() + */ + protected void performUpdate() { + TreeModelContentProvider provider = (TreeModelContentProvider) getContentProvider(); + if (fElements != null) { + TreeModelViewer viewer = (TreeModelViewer) provider.getViewer(); + for (int i = 0; i < fElements.length; i++) { + int modelIndex = fIndex + i; + Object element = fElements[i]; + if (element != null) { + int viewIndex = provider.modelToViewIndex(fParentPath, modelIndex); + if (provider.shouldFilter(fParentPath, element)) { + if (provider.addFilteredIndex(fParentPath, modelIndex)) { + //System.out.println("REMOVE(" + fParent + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); + viewer.remove(fParent, viewIndex); + } + } else { + if (provider.isFiltered(fParentPath, modelIndex)) { + provider.clearFilteredChild(fParentPath, modelIndex); + int insertIndex = provider.modelToViewIndex(fParentPath, modelIndex); + //System.out.println("insert(" + fParentPath.getLastSegment() + ", modelIndex: " + modelIndex + " insertIndex: " + insertIndex + ", " + element + ")"); + if (fParentPath.getSegmentCount() == 0) { + viewer.insert(fParent, element, insertIndex); + } else { + viewer.insert(fParentPath, element, insertIndex); + } + } else { + //System.out.println("replace(" + fParent + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); + viewer.replace(fParent, viewIndex, element); + } + provider.updateChildCount(element, 0); + } + } + } + } else { + provider.updateChildCount(fParent, 0); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildrenUpdate#setChild(java.lang.Object, int) + */ + public void setChild(Object child, int index) { + if (fElements == null) { + fElements = new Object[fLength]; + } + fElements[index - fIndex] = child; + } + + /** + * Coalesce the request with the given index. Return whether the requests can be + * coalesced. + * + * @param index + * @return whether it worked + */ + public boolean coalesce(int index) { + if (index == fIndex + fLength) { + fLength++; + return true; + } + return false; + } + + public void start() { + synchronized (this) { + if (fStarted) { + return; + } + fStarted = true; + } + //System.out.println("\tRequest (" + fParent + "): " + fIndex + " length: " + fLength); + TreeModelContentProvider contentProvider = (TreeModelContentProvider)getContentProvider(); + contentProvider.childRequestStarted(this); + fContentProvider.update(this); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildrenUpdate#getLength() + */ + public int getLength() { + return fLength; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildrenUpdate#getOffset() + */ + public int getOffset() { + return fIndex; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IChildrenUpdate#getParent() + */ + public Object getParent() { + return fParent; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementCompareRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementCompareRequest.java new file mode 100644 index 000000000..d6d580b5b --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementCompareRequest.java @@ -0,0 +1,58 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.model.IElementCompareRequest; +import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; +import org.eclipse.ui.IMemento; + +/** + * @since 3.3 + */ +public class ElementCompareRequest extends MementoUpdate implements IElementCompareRequest { + + private boolean fEqual; + private ModelDelta fDelta; + private ModelContentProvider fProvider; + + /** + * @param context + * @param element + * @param memento + */ + public ElementCompareRequest(ModelContentProvider provider, Object element, IMemento memento, ModelDelta delta) { + super(provider.getPresentationContext(), element, memento); + fProvider = provider; + fDelta = delta; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IElementCompareRequest#setEqual(boolean) + */ + public void setEqual(boolean equal) { + fEqual = equal; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IProgressMonitor#done() + */ + public void done() { + if (isEqual()) { + fDelta.setElement(getElement()); + fProvider.doRestore(fDelta); + } + } + + boolean isEqual() { + return fEqual; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementMementoRequest.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementMementoRequest.java new file mode 100644 index 000000000..24b14b1bb --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ElementMementoRequest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.model.IElementMementoRequest; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; +import org.eclipse.ui.IMemento; + +/** + * @since 3.3 + */ +public class ElementMementoRequest extends MementoUpdate implements IElementMementoRequest { + + private IMementoManager fManager; + private ModelDelta fDelta; + + /** + * @param context + * @param element + * @param memento + */ + public ElementMementoRequest(IMementoManager manager, IPresentationContext context, Object element, IMemento memento, ModelDelta delta) { + super(context, element, memento); + fManager = manager; + fDelta = delta; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IProgressMonitor#done() + */ + public void done() { + if (!isCanceled() && (getStatus() == null || getStatus().isOK())) { + // replace the element with a memento + fDelta.setElement(getMemento()); + } + fManager.requestComplete(this); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/FilterTransform.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/FilterTransform.java new file mode 100644 index 000000000..88a9b30f5 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/FilterTransform.java @@ -0,0 +1,365 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + ******************************************************************************/ + +package org.eclipse.debug.internal.ui.model.viewers; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jface.viewers.TreePath; + + +/** + * Helper class to support filtering in virtual tree viewer. + * Translates indexes from viewer to model coordinate space (and visa versa). + * <p> + * This filter transform maintains a tree representing filtered elements in the + * viewer. The filtering is performed dynamically as elements are 'replaced' in the tree + * by a lazy tree content provider. + * </p> + * <p> + * This class not intended to be subclassed or instantiated. For internal use only. + * </p> + * <p> + * <strong>EXPERIMENTAL</strong>. This class or interface has been added as + * part of a work in progress. There is no guarantee that this API will + * remain unchanged during the 3.3 release cycle. Please do not use this API + * without consulting with the Platform/Debug team. + * </p> + * @since 3.3 + */ +class FilterTransform { + + private Node root = new Node(); + + class Node { + private int[] filteredChildren = null; // only set for leaves + private Map children = null; // only set for parent nodes, indexed by child + + Node() { + } + + boolean addFilter(TreePath path, int childIndex, int pathIndex) { + if (pathIndex == path.getSegmentCount()) { + if (filteredChildren == null) { + filteredChildren = new int[]{childIndex}; + return true; + } + int location = Arrays.binarySearch(filteredChildren, childIndex); + if (location >= 0) { + return false; + } + location = 0 - (location + 1); + int[] next = new int[filteredChildren.length + 1]; + if (location == 0) { + next[0] = childIndex; + System.arraycopy(filteredChildren, 0, next, 1, filteredChildren.length); + } else if (location == filteredChildren.length) { + next[filteredChildren.length] = childIndex; + System.arraycopy(filteredChildren, 0, next, 0, filteredChildren.length); + } else { + System.arraycopy(filteredChildren, 0, next, 0, location); + next[location] = childIndex; + System.arraycopy(filteredChildren, location, next, location + 1, filteredChildren.length - location); + } + filteredChildren = next; + return true; + } + + if (children == null) { + children = new HashMap(); + } + Object element = path.getSegment(pathIndex); + Node node = (Node) children.get(element); + if (node == null) { + node = new Node(); + children.put(element, node); + } + return node.addFilter(path, childIndex, pathIndex + 1); + } + + boolean clear(TreePath path, int pathIndex) { + if (pathIndex == path.getSegmentCount()) { + return true; + } + if (children == null) { + return false; + } + Object child = path.getSegment(pathIndex); + Node node = (Node) children.get(child); + if (node != null) { + if (node.clear(path, pathIndex + 1)) { + children.remove(child); + } + } + return children.isEmpty(); + } + + boolean clear(TreePath path, int childIndex, int pathIndex) { + if (pathIndex == path.getSegmentCount()) { + if (filteredChildren != null) { + int location = Arrays.binarySearch(filteredChildren, childIndex); + if (location >= 0) { + // remove it + if (location == 0) { + if (filteredChildren.length == 1) { + filteredChildren = null; + return true; + } else { + int[] next = new int[filteredChildren.length - 1]; + System.arraycopy(filteredChildren, 1, next, 0, next.length); + filteredChildren = next; + } + } else if (location == (filteredChildren.length - 1)) { + int[] next = new int[filteredChildren.length - 1]; + System.arraycopy(filteredChildren, 0, next, 0, location); + filteredChildren = next; + } else { + int[] next = new int[filteredChildren.length - 1]; + System.arraycopy(filteredChildren, 0, next, 0, location); + System.arraycopy(filteredChildren, location + 1, next, location, next.length - location); + filteredChildren = next; + } + return false; + } + } else { + return false; + } + } + if (children == null) { + return false; + } + Object element = path.getSegment(pathIndex); + Node node = (Node) children.get(element); + if (node == null) { + return false; + } + boolean remove = node.clear(path, childIndex, pathIndex + 1); + if (remove) { + children.remove(element); + return filteredChildren == null && children.isEmpty(); + } else { + return false; + } + } + + Node find(TreePath path, int pathIndex) { + if (pathIndex == path.getSegmentCount()) + return this; + if (children == null) { + return null; + } + Object child = path.getSegment(pathIndex); + Node node = (Node) children.get(child); + if (node != null) { + return node.find(path, pathIndex + 1); + } + return null; + } + + int viewToModel(int childIndex) { + if (filteredChildren == null) { + return childIndex; + } + // If there are filtered children, then we want to find the + // (n+1)th missing number in the list of filtered indexes (missing + // entries are visible in the view). For example, if the request + // has asked for the model index corresponding to the 4th viewer + // index, then we want to find the 5th missing number in the + // filtered index sequence. + + int count = -1; // count from 0, 1, 2... + int missingNumbers = 0; // how many numbers missing from the filtered index + int offset = 0; // offset into the filtered index + + while (missingNumbers < (childIndex + 1)) { + count++; + if (offset < filteredChildren.length) { + if (filteredChildren[offset] == count) { + // not missing + offset++; + } else { + // missing + missingNumbers++; + } + } else { + missingNumbers++; + } + } + return count; + } + + int modelToView(int childIndex) { + if (filteredChildren == null) { + return childIndex; + } + int offset = 0; + for (int i = 0; i < filteredChildren.length; i++) { + if (childIndex == filteredChildren[i] ) { + return -1; + } else if (childIndex > filteredChildren[i]) { + offset++; + } else { + break; + } + } + return childIndex - offset; + } + + int modelToViewCount(int childCount) { + if (filteredChildren == null) { + return childCount; + } + return childCount - filteredChildren.length; + } + + boolean isFiltered(int index) { + if (filteredChildren != null) { + int location = Arrays.binarySearch(filteredChildren, index); + return location >= 0; + } + return false; + } + } + + /** + * Filters the specified child of the given parent and returns + * whether the child was already filtered. + * + * @param parentPath path to parent element + * @param childIndex index of filtered child relative to parent (in model coordinates) + * @return whether the child was already filtered + */ + public synchronized boolean addFilteredIndex(TreePath parentPath, int childIndex) { + return root.addFilter(parentPath, childIndex, 0); + } + + /** + * Clears all filtered elements. + */ + public synchronized void clear() { + root = new Node(); + } + + /** + * Clears all filters in the subtree of the given element. + * + * @param path element path + */ + public synchronized void clear(TreePath path) { + root.clear(path, 0); + } + + /** + * Clears the given filtered index of the specified parent. + * + * @param path parent path + * @param index index to clear + */ + public synchronized void clear(TreePath parentPath, int index) { + root.clear(parentPath, index, 0); + } + + /** + * Translates and returns the given model index (raw index) into + * a view index (filtered index), or -1 if filtered. + * + * @param parentPath path to parent element + * @param childIndex index of child element in model space + * @return the given index in view coordinates, or -1 if filtered. + */ + public synchronized int modelToViewIndex(TreePath parentPath, int childIndex) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return childIndex; + } + return parentNode.modelToView(childIndex); + } + + /** + * Translates and returns the given view index (filtered) into + * a model index (raw index). + * + * @param parentPath path to parent element + * @param childIndex index of child element in view space + * @return the given index in model coordinates + */ + public synchronized int viewToModelIndex(TreePath parentPath, int childIndex) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return childIndex; + } + return parentNode.viewToModel(childIndex); + } + + /** + * Returns the number of children for the given parent, in the model. + * + * @param parentPath path to parent element + * @param viewCount number of children in the view + * @return number of children in the model + */ + public synchronized int viewToModelCount(TreePath parentPath, int viewCount) { + Node parentNode = root.find(parentPath, 0); + if (parentNode != null) { + if (parentNode.filteredChildren != null) { + return viewCount + parentNode.filteredChildren.length; + } + } + return viewCount; + } + + /** + * Translates and returns the given model child count (raw) into + * a view count (filtered). + * + * @param parentPath path to parent element + * @param count child count in model space + * @return the given count in view coordinates + */ + public synchronized int modelToViewCount(TreePath parentPath, int count) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return count; + } + return parentNode.modelToViewCount(count); + } + + /** + * Returns whether the given index of the specified parent is currently filtered. + * + * @param parentPath path to parent element + * @param index index of child element + * @return whether the child is currently filtered + */ + public synchronized boolean isFiltered(TreePath parentPath, int index) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return false; + } + return parentNode.isFiltered(index); + } + + /** + * Returns filtered children of the given parent, or <code>null</code> if none. + * + * @param parentPath + * @return filtered children or <code>null</code> + */ + public int[] getFilteredChildren(TreePath parentPath) { + Node parentNode = root.find(parentPath, 0); + if (parentNode == null) { + return null; + } + return parentNode.filteredChildren; + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IMementoManager.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IMementoManager.java new file mode 100644 index 000000000..5350dbb11 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IMementoManager.java @@ -0,0 +1,38 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.model.IElementMementoRequest; + +/** + * @since 3.3 + */ +interface IMementoManager { + + /** + * Adds the request to this manager. + * + * @param memento request + */ + public void addRequest(IElementMementoRequest request); + + /** + * Notification the request is complete. + * + * @param request + */ + public void requestComplete(IElementMementoRequest request); + + /** + * Process the queued requests. Accepts no more new requests. + */ + public void processReqeusts(); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IViewerUpdateListener.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IViewerUpdateListener.java new file mode 100644 index 000000000..9925e7c8f --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/IViewerUpdateListener.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor; + +/** + * Notified of viewer updates. + * + * @since 3.3 + */ +public interface IViewerUpdateListener { + + /** + * Notification that a sequence of viewer updates are starting. + */ + public void viewerUpdatesBegin(); + + /** + * Notification that viewer updates are complete. Corresponds to + * a <code>viewerUpdatesBegin()</code> notification. + */ + public void viewerUpdatesComplete(); + + /** + * Notification that a specific update has started within + * a sequence of updates. + * + * @param update update + */ + public void updateStarted(IAsynchronousRequestMonitor update); + + /** + * Notification that a specific update has completed within a + * sequence of updates. + * + * @param update update + */ + public void updateComplete(IAsynchronousRequestMonitor update); +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/LabelUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/LabelUpdate.java new file mode 100644 index 000000000..61e2a2ab7 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/LabelUpdate.java @@ -0,0 +1,122 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.actions.context.AbstractRequestMonitor; +import org.eclipse.debug.internal.ui.model.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ViewerRow; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.RGB; + +/** + * @since 3.3 + */ +class LabelUpdate extends AbstractRequestMonitor implements ILabelUpdate { + + private Object fElement; + private String fColumnId; + private RGB fBackground; + private RGB fForeground; + private ImageDescriptor fImageDescriptor; + private String fLabel; + private FontData fFontData; + private TreeModelLabelProvider fProvider; + private int fColumnIndex; + + /** + * @param element element the label is for + * @param provider label provider to callback to + * @param columnId column identifier or <code>null</code> + */ + public LabelUpdate(Object element, TreeModelLabelProvider provider, String columnId, int columnIndex) { + fElement = element; + fProvider = provider; + fColumnId = columnId; + fColumnIndex = columnIndex; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#getColumnId() + */ + public String getColumnId() { + return fColumnId; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#getElement() + */ + public Object getElement() { + return fElement; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#setBackground(org.eclipse.swt.graphics.RGB) + */ + public void setBackground(RGB background) { + fBackground = background; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#setFontData(org.eclipse.swt.graphics.FontData) + */ + public void setFontData(FontData fontData) { + fFontData = fontData; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#setForeground(org.eclipse.swt.graphics.RGB) + */ + public void setForeground(RGB foreground) { + fForeground = foreground; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#setImageDescriptor(org.eclipse.jface.resource.ImageDescriptor) + */ + public void setImageDescriptor(ImageDescriptor image) { + fImageDescriptor = image; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.ILabelUpdate#setLabel(java.lang.String) + */ + public void setLabel(String text) { + fLabel = text; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IPresentationUpdate#getPresentationContext() + */ + public IPresentationContext getPresentationContext() { + return fProvider.getPresentationContext(); + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IProgressMonitor#done() + */ + public void done() { + fProvider.complete(this); + } + + /** + * Applies settings to viewer cell + */ + public void update(ViewerRow row) { + row.setText(fColumnIndex, fLabel); + row.setImage(fColumnIndex, fProvider.getImage(fImageDescriptor)); + row.setForeground(fColumnIndex, fProvider.getColor(fForeground)); + row.setBackground(fColumnIndex, fProvider.getColor(fBackground)); + row.setFont(fColumnIndex, fProvider.getFont(fFontData)); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/MementoUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/MementoUpdate.java new file mode 100644 index 000000000..f789d7834 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/MementoUpdate.java @@ -0,0 +1,55 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.debug.internal.ui.actions.context.AbstractRequestMonitor; +import org.eclipse.debug.internal.ui.model.IPresentationUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.ui.IMemento; + +/** + * @since 3.3 + */ +abstract class MementoUpdate extends AbstractRequestMonitor implements IPresentationUpdate { + + private IPresentationContext fContext; + private Object fElement; + private IMemento fMemento; + + /** + * Constructs a viewer state request. + * + * @param viewer viewer + * @param element element + * @param memento memento + */ + public MementoUpdate(IPresentationContext context, Object element, IMemento memento) { + fContext = context; + fElement = element; + fMemento = memento; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IPresentationUpdate#getPresentationContext() + */ + public IPresentationContext getPresentationContext() { + return fContext; + } + + public Object getElement() { + return fElement; + } + + public IMemento getMemento() { + return fMemento; + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ModelContentProvider.java new file mode 100644 index 000000000..f408c0155 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ModelContentProvider.java @@ -0,0 +1,915 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Set; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.ISafeRunnable; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.ListenerList; +import org.eclipse.core.runtime.SafeRunner; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.Job; +import org.eclipse.debug.internal.ui.DebugUIPlugin; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.debug.internal.ui.model.IElementMementoProvider; +import org.eclipse.debug.internal.ui.model.IElementMementoRequest; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelProxy; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelProxyFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; +import org.eclipse.debug.ui.AbstractDebugView; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.Viewer; +import org.eclipse.jface.viewers.ViewerFilter; +import org.eclipse.ui.IMemento; +import org.eclipse.ui.IWorkbenchPart; +import org.eclipse.ui.XMLMemento; +import org.eclipse.ui.progress.UIJob; +import org.eclipse.ui.progress.WorkbenchJob; + +/** + * Content provider for a virtual viewer. + * + * @since 3.3 + */ +abstract class ModelContentProvider implements IContentProvider, + IModelChangedListener { + + private Viewer fViewer; + + private Map fModelProxies = new HashMap(); // model proxy by element + + /** + * Map of nodes that have been filtered from the viewer. + */ + private FilterTransform fTransform = new FilterTransform(); + + /** + * Model listeners + */ + private ListenerList fModelListeners = new ListenerList(); + + /** + * Update listeners + */ + private ListenerList fUpdateListeners = new ListenerList(); + + /** + * Nesting count of updates + */ + private int fUpdateNestingCount = 0; + + /** + * Map of viewer states keyed by viewer input mementos + */ + private Map fViewerStates = new LRUMap(20); + + /** + * Pending viewer state to be restored + */ + private ModelDelta fPendingState = null; + + /** + * Used to determine when restoration delta has been processed + */ + class CheckState implements IModelDeltaVisitor { + private boolean complete = true; + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor#visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, int) + */ + public boolean visit(IModelDelta delta, int depth) { + if (delta.getFlags() != IModelDelta.NO_CHANGE) { + complete = false; + return false; + } + return true; + } + + public boolean isComplete() { + return complete; + } + } + + /** + * LRU cache for viewer states + */ + class LRUMap extends LinkedHashMap { + private static final long serialVersionUID= 1L; + private int fMaxSize; + LRUMap(int maxSize) { + super(); + fMaxSize = maxSize; + } + protected boolean removeEldestEntry(Entry eldest) { + return size() > fMaxSize; + } + } + + /** + * Update type constants + */ + private static final int UPDATE_SEQUENCE_BEGINS = 0; + private static final int UPDATE_SEQUENCE_COMPLETE = 1; + private static final int UPDATE_BEGINS = 2; + private static final int UPDATE_COMPLETE = 3; + + /** + * Constant for an empty tree path. + */ + protected static final TreePath EMPTY_TREE_PATH = new TreePath(new Object[]{}); + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#dispose() + */ + public synchronized void dispose() { + fModelListeners.clear(); + fUpdateListeners.clear(); + disposeAllModelProxies(); + fViewer = null; + } + + public synchronized boolean isDisposed() { + return fViewer == null; + } + + /* + * (non-Javadoc) + * + * @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer, + * java.lang.Object, java.lang.Object) + */ + public synchronized void inputChanged(Viewer viewer, Object oldInput, Object newInput) { + fViewer = viewer; + if (oldInput != null) { + saveViewerState(oldInput); + } + if (newInput != oldInput) { + disposeAllModelProxies(); + fTransform.clear(); + if (newInput != null) { + installModelProxy(newInput); + restoreViewerState(newInput); + } + } + } + + /** + * Restores viewer state for the new input + * + * @param newInput + */ + protected void restoreViewerState(final Object input) { + fPendingState = null; + final IElementMementoProvider stateProvider = getViewerStateAdapter(input); + if (stateProvider != null) { + // build a model delta representing expansion and selection state + final ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE); + final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ + final IMementoManager manager = new IMementoManager() { + + private IElementMementoRequest fRequest; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#requestComplete(org.eclipse.debug.internal.ui.model.IElementMementoRequest) + */ + public synchronized void requestComplete(IElementMementoRequest request) { + if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { + XMLMemento keyMemento = (XMLMemento) delta.getElement(); + StringWriter writer = new StringWriter(); + try { + keyMemento.save(writer); + final ModelDelta stateDelta = (ModelDelta) fViewerStates.remove(writer.toString()); + if (stateDelta != null) { + //System.out.println("RESTORE: " + stateDelta.toString()); + stateDelta.setElement(input); + // begin restoration + UIJob job = new UIJob("restore state") { + public IStatus runInUIThread(IProgressMonitor monitor) { + if (input.equals(getViewer().getInput())) { + fPendingState = stateDelta; + doInitialRestore(); + } + return Status.OK_STATUS; + } + + }; + job.setSystem(true); + job.schedule(); + } + } catch (IOException e) { + // TODO log + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#processReqeusts() + */ + public void processReqeusts() { + stateProvider.encodeElement(fRequest); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#addRequest(org.eclipse.debug.internal.ui.model.IElementMementoRequest) + */ + public synchronized void addRequest(IElementMementoRequest req) { + fRequest = req; + } + + }; + manager.addRequest(new ElementMementoRequest(manager, getPresentationContext(), + delta.getElement(), inputMemento, delta)); + manager.processReqeusts(); + } + } + + /** + * Restore selection/expansion based on items already in the tree + */ + abstract protected void doInitialRestore(); + + /** + * Perform any restoration required for the given tree path. + * + * @param path + */ + protected void doRestore(final TreePath path) { + if (fPendingState == null) { + return; + } + IModelDeltaVisitor visitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if (delta.getParentDelta() == null) { + return true; + } + Object element = delta.getElement(); + Object potentialMatch = path.getSegment(depth - 1); + if (element instanceof IMemento) { + IElementMementoProvider provider = getViewerStateAdapter(getViewer().getInput()); + if (provider != null) { + provider.compareElement( + new ElementCompareRequest(ModelContentProvider.this, + potentialMatch, (IMemento) element, (ModelDelta)delta)); + } + } else { + if (element.equals(potentialMatch)) { + // already processed - visit children + return true; + } + } + return false; + } + }; + fPendingState.accept(visitor); + } + + /** + * Saves the viewer's state for the previous input. + * + * @param oldInput + */ + protected void saveViewerState(Object input) { + IElementMementoProvider stateProvider = getViewerStateAdapter(input); + if (stateProvider != null) { + // build a model delta representing expansion and selection state + ModelDelta delta = new ModelDelta(input, IModelDelta.NO_CHANGE); + buildViewerState(delta); + if (delta.getChildDeltas().length > 0) { + // encode delta with mementos in place of elements, in non-UI thread + encodeDelta(delta, stateProvider); + } + } + } + + /** + * Encodes delta elements into mementos using the given provider. + * + * @param delta + * @param stateProvider + */ + protected void encodeDelta(final ModelDelta rootDelta, final IElementMementoProvider stateProvider) { + final XMLMemento inputMemento = XMLMemento.createWriteRoot("VIEWER_INPUT_MEMENTO"); //$NON-NLS-1$ + final XMLMemento childrenMemento = XMLMemento.createWriteRoot("CHILDREN_MEMENTO"); //$NON-NLS-1$ + final IMementoManager manager = new IMementoManager() { + + private Set requests = new HashSet(); + private boolean abort = false; + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#requestComplete(org.eclipse.debug.internal.ui.model.IElementMementoRequest) + */ + public synchronized void requestComplete(IElementMementoRequest request) { + if (!abort) { + if (!request.isCanceled() && (request.getStatus() == null || request.getStatus().isOK())) { + requests.remove(request); + if (requests.isEmpty()) { + XMLMemento keyMemento = (XMLMemento) rootDelta.getElement(); + StringWriter writer = new StringWriter(); + try { + keyMemento.save(writer); + fViewerStates.put(writer.toString(), rootDelta); + } catch (IOException e) { + // TODO log + } + } + } else { + abort = true; + Iterator iterator = requests.iterator(); + while (iterator.hasNext()) { + IElementMementoRequest req = (IElementMementoRequest) iterator.next(); + req.setCanceled(true); + } + requests.clear(); + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#processReqeusts() + */ + public void processReqeusts() { + IElementMementoRequest[] req = (IElementMementoRequest[]) requests.toArray(new IElementMementoRequest[requests.size()]); + for (int i = 0; i < req.length; i++) { + stateProvider.encodeElement(req[i]); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IMementoManager#addRequest(org.eclipse.debug.internal.ui.model.IElementMementoRequest) + */ + public synchronized void addRequest(IElementMementoRequest request) { + requests.add(request); + } + + }; + IModelDeltaVisitor visitor = new IModelDeltaVisitor() { + public boolean visit(IModelDelta delta, int depth) { + if (delta.getParentDelta() == null) { + manager.addRequest( + new ElementMementoRequest(manager, getPresentationContext(), + delta.getElement(), inputMemento, (ModelDelta)delta)); + } else { + manager.addRequest( + new ElementMementoRequest(manager, getPresentationContext(), + delta.getElement(), childrenMemento.createChild("CHILD_ELEMENT"), (ModelDelta)delta)); //$NON-NLS-1$ + } + return true; + } + }; + rootDelta.accept(visitor); + manager.processReqeusts(); + } + + /** + * Builds a delta with the given root delta for expansion/selection state. + * + * @param delta root delta + */ + protected abstract void buildViewerState(ModelDelta delta); + + /** + * Uninstalls the model proxy installed for the given element, if any. + * + * @param element + */ + protected synchronized void disposeModelProxy(Object element) { + IModelProxy proxy = (IModelProxy) fModelProxies.remove(element); + if (proxy != null) { + proxy.dispose(); + } + } + + /** + * Uninstalls each model proxy + */ + protected synchronized void disposeAllModelProxies() { + Iterator updatePolicies = fModelProxies.values().iterator(); + while (updatePolicies.hasNext()) { + IModelProxy proxy = (IModelProxy) updatePolicies.next(); + proxy.dispose(); + } + fModelProxies.clear(); + } + + /** + * Installs the model proxy for the given element into this content provider + * if not already installed. + * + * @param element + * element to install an update policy for + */ + protected synchronized void installModelProxy(Object element) { + if (!fModelProxies.containsKey(element)) { + IModelProxyFactoryAdapter modelProxyFactory = getModelProxyFactoryAdapter(element); + if (modelProxyFactory != null) { + final IModelProxy proxy = modelProxyFactory.createModelProxy( + element, getPresentationContext()); + if (proxy != null) { + fModelProxies.put(element, proxy); + Job job = new Job("Model Proxy installed notification job") {//$NON-NLS-1$ + protected IStatus run(IProgressMonitor monitor) { + if (!monitor.isCanceled()) { + proxy.init(getPresentationContext()); + Object[] mcls = fModelListeners.getListeners(); + for (int i = 0; i < mcls.length; i++) { + proxy.addModelChangedListener((IModelChangedListener) mcls[i]); + } + proxy + .addModelChangedListener(ModelContentProvider.this); + proxy.installed(); + } + return Status.OK_STATUS; + } + }; + job.setSystem(true); + job.schedule(); + } + } + } + } + + /** + * Returns the model proxy factory for the given element or + * <code>null</code> if none. + * + * @param element + * element to retrieve adapter for + * @return model proxy factory adapter or <code>null</code> + */ + protected IModelProxyFactoryAdapter getModelProxyFactoryAdapter(Object element) { + IModelProxyFactoryAdapter adapter = null; + if (element instanceof IModelProxyFactoryAdapter) { + adapter = (IModelProxyFactoryAdapter) element; + } else if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + adapter = (IModelProxyFactoryAdapter) adaptable.getAdapter(IModelProxyFactoryAdapter.class); + } + return adapter; + } + + /** + * Returns the viewer state adapter for the given element or + * <code>null</code> if none. + * + * @param element + * element to retrieve adapter for + * @return viewer state adapter or <code>null</code> + */ + protected IElementMementoProvider getViewerStateAdapter(Object element) { + IElementMementoProvider adapter = null; + if (element instanceof IElementMementoProvider) { + adapter = (IElementMementoProvider) element; + } else if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + adapter = (IElementMementoProvider) adaptable.getAdapter(IElementMementoProvider.class); + } + return adapter; + } + + /** + * Returns the presentation context for this content provider. + * + * @return presentation context + */ + protected abstract IPresentationContext getPresentationContext(); + + /* + * (non-Javadoc) + * + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + public void modelChanged(final IModelDelta delta) { + WorkbenchJob job = new WorkbenchJob("process model delta") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + updateNodes(new IModelDelta[] { delta }); + return Status.OK_STATUS; + } + }; + job.setSystem(true); + job.schedule(); + + } + + protected void updateNodes(IModelDelta[] nodes) { + for (int i = 0; i < nodes.length; i++) { + IModelDelta node = nodes[i]; + int flags = node.getFlags(); + + if ((flags & IModelDelta.ADDED) != 0) { + handleAdd(node); + } + if ((flags & IModelDelta.REMOVED) != 0) { + handleRemove(node); + } + if ((flags & IModelDelta.CONTENT) != 0) { + handleContent(node); + } + if ((flags & IModelDelta.EXPAND) != 0) { + handleExpand(node); + } + if ((flags & IModelDelta.SELECT) != 0) { + handleSelect(node); + } + if ((flags & IModelDelta.STATE) != 0) { + handleState(node); + } + if ((flags & IModelDelta.INSERTED) != 0) { + handleInsert(node); + } + if ((flags & IModelDelta.REPLACED) != 0) { + handleReplace(node); + } + if ((flags & IModelDelta.INSTALL) != 0) { + handleInstall(node); + } + if ((flags & IModelDelta.UNINSTALL) != 0) { + handleUninstall(node); + } + updateNodes(node.getChildDeltas()); + } + } + + /** + * Returns the content adapter for the given element or + * <code>null</code> if none. + * + * @param element + * element to retrieve adapter for + * @return content adapter or <code>null</code> + */ + protected IElementContentProvider getContentAdapter(Object element) { + IElementContentProvider adapter = null; + if (element instanceof IElementContentProvider) { + adapter = (IElementContentProvider) element; + } else if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + adapter = (IElementContentProvider) adaptable.getAdapter(IElementContentProvider.class); + } + return adapter; + } + + protected abstract void handleState(IModelDelta delta); + + protected abstract void handleSelect(IModelDelta delta); + + protected abstract void handleExpand(IModelDelta delta); + + protected abstract void handleContent(IModelDelta delta); + + protected abstract void handleRemove(IModelDelta delta); + + protected abstract void handleAdd(IModelDelta delta); + + protected abstract void handleInsert(IModelDelta delta); + + protected abstract void handleReplace(IModelDelta delta); + + protected void handleInstall(IModelDelta delta) { + installModelProxy(delta.getElement()); + } + + protected void handleUninstall(IModelDelta delta) { + disposeModelProxy(delta.getElement()); + } + + /** + * Returns a tree path for the node, *not* including the root element. + * + * @param node + * model delta + * @return corresponding tree path + */ + protected TreePath getTreePath(IModelDelta node) { + ArrayList list = new ArrayList(); + IModelDelta parentDelta = node.getParentDelta(); + while (parentDelta != null) { + list.add(0, node.getElement()); + node = parentDelta; + parentDelta = node.getParentDelta(); + } + return new TreePath(list.toArray()); + } + + /** + * Returns the viewer this content provider is working for. + * + * @return viewer + */ + protected Viewer getViewer() { + return fViewer; + } + + protected void handlePresentationFailure(IAsynchronousRequestMonitor request, IStatus status) { + IWorkbenchPart part = getPresentationContext().getPart(); + if (part instanceof AbstractDebugView) { + AbstractDebugView view = (AbstractDebugView) part; + view.showMessage(status.getMessage()); + } + } + + + /** + * Translates and returns the given child index from the viewer coordinate + * space to the model coordinate space. + * + * @param parentPath path to parent element + * @param index index of child element in viewer (filtered) space + * @return index of child element in model (raw) space + */ + public /* protected */ int viewToModelIndex(TreePath parentPath, int index) { + return fTransform.viewToModelIndex(parentPath, index); + } + + /** + * Translates and returns the given child count from the viewer coordinate + * space to the model coordinate space. + * + * @param parentPath path to parent element + * @param count number of child elements in viewer (filtered) space + * @return number of child elements in model (raw) space + */ + public /* protected */ int viewToModelCount(TreePath parentPath, int count) { + return fTransform.viewToModelCount(parentPath, count); + } + + /** + * Translates and returns the given child index from the model coordinate + * space to the viewer coordinate space. + * + * @param parentPath path to parent element + * @param index index of child element in model (raw) space + * @return index of child element in viewer (filtered) space + */ + protected int modelToViewIndex(TreePath parentPath, int index) { + return fTransform.modelToViewIndex(parentPath, index); + } + + /** + * Translates and returns the given child count from the model coordinate + * space to the viewer coordinate space. + * + * @param parentPath path to parent element + * @param count child count element in model (raw) space + * @return child count in viewer (filtered) space + */ + protected int modelToViewChildCount(TreePath parentPath, int count) { + return fTransform.modelToViewCount(parentPath, count); + } + + /** + * Notes that the child at the specified index of the given parent element + * has been filtered from the viewer. Returns whether the child at the given + * index was already filtered. + * + * @param parentPath path to parent element + * @param index index of child element to be filtered + * @return whether the child was already filtered + */ + protected boolean addFilteredIndex(TreePath parentPath, int index) { + return fTransform.addFilteredIndex(parentPath, index); + } + + /** + * Returns whether the given element is filtered. + * + * @param parentElementOrTreePath + * the parent element or path + * @param element + * the child element + * @return whether to filter the element + */ + protected boolean shouldFilter(Object parentElementOrTreePath, Object element) { + ViewerFilter[] filters = ((StructuredViewer)fViewer).getFilters(); + if (filters.length > 0) { + for (int j = 0; j < filters.length; j++) { + if (!(filters[j].select(fViewer, parentElementOrTreePath, element))) { + return true; + } + } + } + return false; + } + + /** + * Returns whether the given index of the specified parent was previously filtered. + * + * @param parentPath + * @param index + * @return whether the element at the given index was filtered + */ + protected boolean isFiltered(TreePath parentPath, int index) { + return fTransform.isFiltered(parentPath, index); + } + + /** + * Notification that a structural refresh is occurring at the specified path + * + * @param path + */ + protected void refreshingStructure(TreePath path) { + } + + /** + * Notification the given element is being unmapped. + * + * @param path + */ + protected void unmapPath(TreePath path) { + //System.out.println("Unmap " + path.getLastSegment()); + fTransform.clear(path); + } + + /** + * Return tree paths to the given element in the viewer or <code>null</code> + * + * @param element + * @return tree paths or <code>null</code> + */ + protected TreePath[] getTreePaths(Object element) { + if (fViewer instanceof TreeModelViewer) { + TreeModelViewer tmv = (TreeModelViewer) fViewer; + return tmv.getTreePaths(element); + } + return null; + } + + /** + * Returns filtered children or <code>null</code> + * @param parent + * @return filtered children or <code>null</code> + */ + protected int[] getFilteredChildren(TreePath parent) { + return fTransform.getFilteredChildren(parent); + } + + protected void clearFilteredChild(TreePath parent, int modelIndex) { + fTransform.clear(parent, modelIndex); + } + + protected void clearFilters(TreePath parent) { + fTransform.clear(parent); + } + + /** + * @param delta + */ + void doRestore(final ModelDelta delta) { + if (delta.getFlags() != IModelDelta.NO_CHANGE) { + UIJob job = new UIJob("restore delta") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + TreePath treePath = getTreePath(delta); + TreeModelViewer viewer = (TreeModelViewer)getViewer(); + if ((delta.getFlags() & IModelDelta.EXPAND) != 0) { + viewer.expandToLevel(treePath, 1); + } + if ((delta.getFlags() & IModelDelta.SELECT) != 0) { + viewer.setSelection(new TreeSelection(treePath)); + } + delta.setFlags(IModelDelta.NO_CHANGE); + checkIfRestoreComplete(); + return Status.OK_STATUS; + } + }; + job.setSystem(true); + job.schedule(); + } + } + + protected void checkIfRestoreComplete() { + CheckState state = new CheckState(); + fPendingState.accept(state); + if (state.isComplete()) { + fPendingState = null; + //System.out.println("RESTORE COMPELTE"); + } + } + + void addViewerUpdateListener(IViewerUpdateListener listener) { + fUpdateListeners.add(listener); + } + + void removeViewerUpdateListener(IViewerUpdateListener listener) { + fUpdateListeners.remove(listener); + } + + /** + * Notification an update request has started + * + * @param update + */ + void updateStarted(IAsynchronousRequestMonitor update) { + boolean begin = false; + synchronized (this) { + begin = fUpdateNestingCount == 0; + fUpdateNestingCount++; + } + if (begin) { + notifyUpdate(UPDATE_SEQUENCE_BEGINS, null); + } + notifyUpdate(UPDATE_BEGINS, update); + } + + /** + * Notification an update request has completed + * + * @param update + */ + void updateComplete(IAsynchronousRequestMonitor update) { + boolean end = false; + synchronized (this) { + fUpdateNestingCount--; + end = fUpdateNestingCount == 0; + } + notifyUpdate(UPDATE_COMPLETE, update); + if (end) { + notifyUpdate(UPDATE_SEQUENCE_COMPLETE, null); + } + } + + protected void notifyUpdate(final int type, final IAsynchronousRequestMonitor update) { + if (!fUpdateListeners.isEmpty()) { + Object[] listeners = fUpdateListeners.getListeners(); + for (int i = 0; i < listeners.length; i++) { + final IViewerUpdateListener listener = (IViewerUpdateListener) listeners[i]; + SafeRunner.run(new ISafeRunnable() { + public void run() throws Exception { + switch (type) { + case UPDATE_SEQUENCE_BEGINS: + listener.viewerUpdatesBegin(); + break; + case UPDATE_SEQUENCE_COMPLETE: + listener.viewerUpdatesComplete(); + break; + case UPDATE_BEGINS: + listener.updateStarted(update); + break; + case UPDATE_COMPLETE: + listener.updateComplete(update); + break; + } + } + public void handleException(Throwable exception) { + DebugUIPlugin.log(exception); + } + }); + } + } + } + + /** + * Registers the given listener for model delta notification. + * + * @param listener model delta listener + */ + void addModelChangedListener(IModelChangedListener listener) { + fModelListeners.add(listener); + Iterator proxies = fModelProxies.values().iterator(); + while (proxies.hasNext()) { + IModelProxy proxy = (IModelProxy) proxies.next(); + proxy.addModelChangedListener(listener); + } + } + + /** + * Unregisters the given listener from model delta notification. + * + * @param listener model delta listener + */ + void removeModelChangedListener(IModelChangedListener listener) { + fModelListeners.remove(listener); + Iterator proxies = fModelProxies.values().iterator(); + while (proxies.hasNext()) { + IModelProxy proxy = (IModelProxy) proxies.next(); + proxy.removeModelChangedListener(listener); + } + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelContentProvider.java new file mode 100644 index 000000000..92f810594 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelContentProvider.java @@ -0,0 +1,317 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.Timer; +import java.util.TimerTask; + +import org.eclipse.debug.internal.ui.model.IChildrenUpdate; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; +import org.eclipse.jface.viewers.IBasicPropertyConstants; +import org.eclipse.jface.viewers.ILazyTreeContentProvider; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeSelection; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeItem; + +/** + * Content provider for a virtual tree. + * + * @since 3.3 + */ +class TreeModelContentProvider extends ModelContentProvider implements ILazyTreeContentProvider { + + protected static final String[] STATE_PROPERTIES = new String[]{IBasicPropertyConstants.P_TEXT, IBasicPropertyConstants.P_IMAGE}; + + private Map fPendingChildRequests = new HashMap(); + private Map fPendingCountRequests = new HashMap(); + + private Timer fTimer = new Timer(); + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ILazyTreeContentProvider#getParent(java.lang.Object) + */ + public Object getParent(Object element) { + return null; + } + + /** + * Re-filters any filtered children of the given parent element. + * + * @param path parent element + */ + protected void refilterChildren(TreePath path) { + if (getViewer() != null) { + int[] filteredChildren = getFilteredChildren(path); + if (filteredChildren != null) { + Object parent = getViewer().getInput(); + if (path.getSegmentCount() > 0) { + parent = path.getLastSegment(); + } + for (int i = 0; i < filteredChildren.length; i++) { + doUpdateElement(parent, path, filteredChildren[i]); + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ILazyTreeContentProvider#updateChildCount(java.lang.Object, int) + */ + public synchronized void updateChildCount(Object element, int currentChildCount) { + //System.out.println("updateChildCount(" + element + ")"); + TreePath[] treePaths = getTreePaths(element); + for (int i = 0; i < treePaths.length; i++) { + // re-filter children when asked to update the child count for an element (i.e. + // when refreshing, see if filtered children are still filtered) + refilterChildren(treePaths[i]); + } + doUpdateChildCount(element, currentChildCount); + } + + protected synchronized void doUpdateChildCount(Object element, int currentChildCount) { + IElementContentProvider contentAdapter = getContentAdapter(element); + if (contentAdapter != null) { + ChildrenCountUpdate request = (ChildrenCountUpdate) fPendingCountRequests.get(contentAdapter); + if (request != null) { + if (request.coalesce(element)) { + return; + } else { + request.start(); + } + } + final ChildrenCountUpdate newRequest = new ChildrenCountUpdate(this, contentAdapter); + newRequest.coalesce(element); + fPendingCountRequests.put(contentAdapter, newRequest); + fTimer.schedule(new TimerTask() { + public void run() { + newRequest.start(); + } + }, 10L); + } + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ILazyTreeContentProvider#updateElement(java.lang.Object, int) + */ + public synchronized void updateElement(Object parent, int viewIndex) { + //System.out.println("updateElement(" + parent + ", " + viewIndex + ")"); + TreePath[] paths = getTreePaths(parent); + if (paths.length > 0) { + TreePath path = paths[0]; // all children filter the same, per parent occurrence + int modelIndex = viewToModelIndex(path, viewIndex); + //System.out.println("updateElement("+ parent + ", " + viewIndex + ") > modelIndex = " + modelIndex); + doUpdateElement(parent, path, modelIndex); + } + } + + protected synchronized void doUpdateElement(Object parent, TreePath parentPath, int modelIndex) { + ChildrenUpdate request = (ChildrenUpdate) fPendingChildRequests.get(parent); + if (request != null) { + if (request.coalesce(modelIndex)) { + return; + } else { + request.start(); + } + } + IElementContentProvider contentAdapter = getContentAdapter(parent); + if (contentAdapter != null) { + final ChildrenUpdate newRequest = new ChildrenUpdate(this, parent, parentPath, modelIndex, contentAdapter); + fPendingChildRequests.put(parent, newRequest); + fTimer.schedule(new TimerTask() { + public void run() { + newRequest.start(); + } + }, 10L); + } + } + + protected synchronized void childRequestStarted(IChildrenUpdate update) { + fPendingChildRequests.remove(update.getParent()); + } + + protected synchronized void countRequestStarted(Object key) { + fPendingCountRequests.remove(key); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#getPresentationContext() + */ + protected IPresentationContext getPresentationContext() { + return ((TreeModelViewer)getViewer()).getPresentationContext(); + } + + /** + * Returns the tree viewer this content provider is working for + * + * @return tree viewer + */ + protected TreeViewer getTreeViewer() { + return (TreeViewer)getViewer(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleAdd(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleAdd(IModelDelta delta) { + doUpdateChildCount(delta.getParentDelta().getElement(), 0); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleContent(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleContent(IModelDelta delta) { + getTreeViewer().refresh(delta.getElement()); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleExpand(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleExpand(IModelDelta delta) { + // expand each parent, then this node + IModelDelta parentDelta = delta.getParentDelta(); + if (parentDelta != null) { + handleExpand(parentDelta); + expand(delta); + } + } + + protected void expand(IModelDelta delta) { + int childCount = delta.getChildCount(); + int index = delta.getIndex(); + TreeViewer treeViewer = getTreeViewer(); + if (index >= 0) { + treeViewer.replace(delta.getParentDelta().getElement(), index, delta.getElement()); + } + if (childCount > 0) { + treeViewer.setChildCount(delta.getElement(), childCount); + treeViewer.expandToLevel(getTreePath(delta), 1); + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleInsert(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleInsert(IModelDelta delta) { + // TODO: filters + getTreeViewer().insert(getTreePath(delta.getParentDelta()), delta.getElement(), delta.getIndex()); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleRemove(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleRemove(IModelDelta delta) { + // refresh the parent to properly update for non-visible children + getTreeViewer().refresh(delta.getParentDelta().getElement()); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleReplace(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleReplace(IModelDelta delta) { + getTreeViewer().replace(delta.getParentDelta().getElement(), delta.getIndex(), delta.getElement()); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleSelect(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleSelect(IModelDelta delta) { + int index = delta.getIndex(); + TreeViewer treeViewer = getTreeViewer(); + if (index >= 0) { + treeViewer.replace(delta.getParentDelta().getElement(), index, delta.getElement()); + } + treeViewer.setSelection(new TreeSelection(getTreePath(delta))); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#handleState(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + protected void handleState(IModelDelta delta) { + getTreeViewer().update(delta.getElement(), STATE_PROPERTIES); + } + + public synchronized void dispose() { + fTimer.cancel(); + fPendingChildRequests.clear(); + fPendingCountRequests.clear(); + super.dispose(); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#buildViewerState(org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta) + */ + protected void buildViewerState(ModelDelta delta) { + Tree tree = (Tree) getViewer().getControl(); + TreeItem[] selection = tree.getSelection(); + Set set = new HashSet(); + for (int i = 0; i < selection.length; i++) { + set.add(selection[i]); + } + TreeItem[] items = tree.getItems(); + for (int i = 0; i < items.length; i++) { + buildViewerState(delta, items[i], set); + } + } + + /** + * @param delta parent delta to build on + * @param item item + * @param set set of selected tree items + */ + private void buildViewerState(ModelDelta delta, TreeItem item, Set set) { + Object element = item.getData(); + if (element != null) { + boolean expanded = item.getExpanded(); + boolean selected = set.contains(item); + if (expanded || selected) { + int flags = IModelDelta.NO_CHANGE; + if (expanded) { + flags = flags | IModelDelta.EXPAND; + } + if (selected) { + flags = flags | IModelDelta.SELECT; + } + ModelDelta childDelta = delta.addNode(element, flags); + if (expanded) { + TreeItem[] items = item.getItems(); + for (int i = 0; i < items.length; i++) { + buildViewerState(childDelta, items[i], set); + } + } + } + } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.ModelContentProvider#doInitialRestore() + */ + protected void doInitialRestore() { + Tree tree = (Tree) getViewer().getControl(); + TreeItem[] items = tree.getItems(); + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + Object data = item.getData(); + if (data != null) { + doRestore(new TreePath(new Object[]{data})); + } + } + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelLabelProvider.java new file mode 100644 index 000000000..68a9365e9 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelLabelProvider.java @@ -0,0 +1,237 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; + +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.debug.internal.ui.model.IElementLabelProvider; +import org.eclipse.debug.internal.ui.model.ILabelUpdate; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.ColumnLabelProvider; +import org.eclipse.jface.viewers.ViewerCell; +import org.eclipse.swt.graphics.Color; +import org.eclipse.swt.graphics.Font; +import org.eclipse.swt.graphics.FontData; +import org.eclipse.swt.graphics.Image; +import org.eclipse.swt.graphics.RGB; +import org.eclipse.swt.widgets.Display; +import org.eclipse.ui.progress.UIJob; + +/** + * @since 3.3 + */ +class TreeModelLabelProvider extends ColumnLabelProvider { + + private TreeModelViewer fViewer; + private List fComplete; + + /** + * Cache of images used for elements in this label provider. Label updates + * use the method <code>getImage(...)</code> to cache images for + * image descriptors. The images are disposed with this label provider. + */ + private Map fImageCache = new HashMap(); + + /** + * Cache of the fonts used for elements in this label provider. Label updates + * use the method <code>getFont(...)</code> to cache fonts for + * FontData objects. The fonts are disposed with this label provider. + */ + private Map fFontCache = new HashMap(); + + /** + * Cache of the colors used for elements in this label provider. Label updates + * use the method <code>getColor(...)</code> to cache colors for + * RGB values. The colors are disposed with this label provider. + */ + private Map fColorCache = new HashMap(); + + /** + * Constructs a new label provider on the given display + */ + public TreeModelLabelProvider(TreeModelViewer viewer) { + fViewer = viewer; + } + + /** + * Returns an image for the given image descriptor or <code>null</code>. Adds the image + * to a cache of images if it does not already exist. + * + * @param descriptor image descriptor or <code>null</code> + * @return image or <code>null</code> + */ + protected Image getImage(ImageDescriptor descriptor) { + if (descriptor == null) { + return null; + } + Image image = (Image) fImageCache.get(descriptor); + if (image == null) { + image = new Image(getDisplay(), descriptor.getImageData()); + fImageCache.put(descriptor, image); + } + return image; + } + + /** + * Returns the display to use for resource allocation. + * + * @return display + */ + private Display getDisplay() { + return fViewer.getControl().getDisplay(); + } + + /** + * Returns a font for the given font data or <code>null</code>. Adds the font to the font + * cache if not yet created. + * + * @param fontData font data or <code>null</code> + * @return font font or <code>null</code> + */ + protected Font getFont(FontData fontData) { + if (fontData == null) { + return null; + } + Font font = (Font) fFontCache.get(fontData); + if (font == null) { + font = new Font(getDisplay(), fontData); + fFontCache.put(fontData, font); + } + return font; + } + + /** + * Returns a color for the given RGB or <code>null</code>. Adds the color to the color + * cache if not yet created. + * + * @param rgb RGB or <code>null</code> + * @return color or <code>null</code> + */ + protected Color getColor(RGB rgb) { + if (rgb == null) { + return null; + } + Color color = (Color) fColorCache.get(rgb); + if (color == null) { + color = new Color(getDisplay(), rgb); + fColorCache.put(rgb, color); + } + return color; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.BaseLabelProvider#dispose() + */ + public void dispose() { + Iterator images = fImageCache.values().iterator(); + while (images.hasNext()) { + Image image = (Image) images.next(); + image.dispose(); + } + fImageCache.clear(); + + Iterator fonts = fFontCache.values().iterator(); + while (fonts.hasNext()) { + Font font = (Font) fonts.next(); + font.dispose(); + } + fFontCache.clear(); + + Iterator colors = fColorCache.values().iterator(); + while (colors.hasNext()) { + Color color = (Color) colors.next(); + color.dispose(); + } + fColorCache.clear(); + + super.dispose(); + } + + public synchronized void update(ViewerCell cell) { + String[] visibleColumns = fViewer.getVisibleColumns(); + Object element = cell.getElement(); + String columnId = null; + if (visibleColumns != null) { + columnId = visibleColumns[cell.getColumnIndex()]; + } + IElementLabelProvider presentation = getLabelAdapter(element); + if (presentation != null) { + presentation.update(new LabelUpdate(element, this, columnId, cell.getColumnIndex())); + } else if (element instanceof String) { + // for example, expression error messages + cell.setText((String)element); + } + } + + /** + * Returns the presentation context for this label provider. + * + * @return presentation context + */ + protected IPresentationContext getPresentationContext() { + return fViewer.getPresentationContext(); + } + + /** + * Returns the label provider for the given element or + * <code>null</code> if none. + * + * @param element + * element to retrieve adapter for + * @return label adapter or <code>null</code> + */ + protected IElementLabelProvider getLabelAdapter(Object element) { + IElementLabelProvider adapter = null; + if (element instanceof IElementLabelProvider) { + adapter = (IElementLabelProvider) element; + } else if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + adapter = (IElementLabelProvider) adaptable.getAdapter(IElementLabelProvider.class); + } + return adapter; + } + + /** + * A label update is complete. + * + * @param update + */ + protected synchronized void complete(ILabelUpdate update) { + if (fComplete == null) { + fComplete = new ArrayList(); + UIJob job = new UIJob(getDisplay(), "Label Updates") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + LabelUpdate[] updates = null; + synchronized (TreeModelLabelProvider.this) { + updates = (LabelUpdate[]) fComplete.toArray(new LabelUpdate[fComplete.size()]); + fComplete = null; + } + //System.out.println("Changed Labels: " + updates.length); + fViewer.apply(updates); + return Status.OK_STATUS; + } + }; + job.setSystem(true); + job.schedule(10L); + } + fComplete.add(update); + } + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelViewer.java new file mode 100644 index 000000000..5660c15cf --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/TreeModelViewer.java @@ -0,0 +1,998 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import org.eclipse.core.runtime.IAdaptable; +import org.eclipse.debug.internal.ui.viewers.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditor; +import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentation; +import org.eclipse.debug.internal.ui.viewers.provisional.IColumnPresentationFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelSelectionPolicy; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelSelectionPolicyFactoryAdapter; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.jface.resource.ImageDescriptor; +import org.eclipse.jface.viewers.CellEditor; +import org.eclipse.jface.viewers.ICellModifier; +import org.eclipse.jface.viewers.IContentProvider; +import org.eclipse.jface.viewers.ISelection; +import org.eclipse.jface.viewers.IStructuredSelection; +import org.eclipse.jface.viewers.TreePath; +import org.eclipse.jface.viewers.TreeViewer; +import org.eclipse.jface.viewers.ViewerRow; +import org.eclipse.swt.SWT; +import org.eclipse.swt.events.ControlEvent; +import org.eclipse.swt.events.ControlListener; +import org.eclipse.swt.events.DisposeEvent; +import org.eclipse.swt.events.PaintEvent; +import org.eclipse.swt.events.PaintListener; +import org.eclipse.swt.widgets.Composite; +import org.eclipse.swt.widgets.Item; +import org.eclipse.swt.widgets.Tree; +import org.eclipse.swt.widgets.TreeColumn; +import org.eclipse.swt.widgets.TreeItem; +import org.eclipse.swt.widgets.Widget; +import org.eclipse.ui.IMemento; + +/** + * A tree viewer that displays a model. + * + * @since 3.3 + */ +public class TreeModelViewer extends TreeViewer { + + private IPresentationContext fContext; + + /** + * Current column presentation or <code>null</code> + */ + private IColumnPresentation fColumnPresentation = null; + + /** + * Map of columns presentation id to its visible columns id's (String[]) + * When a columns presentation is not in the map, default settings are used. + */ + private Map fVisibleColumns = new HashMap(); + + /** + * Map of column id's to persisted sizes + */ + private Map fColumnSizes = new HashMap(); + + /** + * Map of column presentation id's to an array of integers representing the column order + * for that presentation, or <code>null</code> if default. + */ + private Map fColumnOrder = new HashMap(); + + /** + * Map of column presentation id to whether columns should be displayed + * for that presentation (the user can toggle columns on/off when a + * presentation is optional. + */ + private Map fShowColumns = new HashMap(); + + /** + * Item's tree path cache + */ + private static final String TREE_PATH_KEY = "TREE_PATH_KEY"; //$NON-NLS-1$ + + /** + * Memento type for column sizes. Sizes are keyed by column presentation id + */ + private static final String COLUMN_SIZES = "COLUMN_SIZES"; //$NON-NLS-1$ + /** + * Memento type for the column order for a presentation context. + * A memento is created for each column presentation + */ + private static final String COLUMN_ORDER = "COLUMN_ORDER"; //$NON-NLS-1$ + /** + * Memento type for the visible columns for a presentation context. + * A memento is created for each column presentation keyed by column number + */ + private static final String VISIBLE_COLUMNS = "VISIBLE_COLUMNS"; //$NON-NLS-1$ + /** + * Memento type for whether columns are visible for a presentation context. + * Booleans are keyed by column presentation id + */ + private static final String SHOW_COLUMNS = "SHOW_COLUMNS"; //$NON-NLS-1$ + /** + * Memento key for the number of visible columns in a VISIBLE_COLUMNS memento + * or for the width of a column + */ + private static final String SIZE = "SIZE"; //$NON-NLS-1$ + /** + * Memento key prefix a visible column + */ + private static final String COLUMN = "COLUMN"; //$NON-NLS-1$ + + /** + * True while performing an insert... we allow insert with filters + */ + private boolean fInserting = false; + + /** + * Persist column sizes when they change. + * + * @since 3.2 + */ + class ColumnListener implements ControlListener { + /* (non-Javadoc) + * @see org.eclipse.swt.events.ControlListener#controlMoved(org.eclipse.swt.events.ControlEvent) + */ + public void controlMoved(ControlEvent e) { + persistColumnOrder(); + } + + /* (non-Javadoc) + * @see org.eclipse.swt.events.ControlListener#controlResized(org.eclipse.swt.events.ControlEvent) + */ + public void controlResized(ControlEvent e) { + persistColumnSizes(); + } + } + + private ColumnListener fListener = new ColumnListener(); + + /** + * Proxy to cell modifier/editor support + */ + class CellModifierProxy implements ICellModifier { + + private IColumnEditor fColumnEditor = null; + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICellModifier#canModify(java.lang.Object, java.lang.String) + */ + public boolean canModify(Object element, String property) { + updateColumnEditor(element); + if (fColumnEditor != null) { + boolean canModify = fColumnEditor.getCellModifier().canModify(element, property); + if (canModify) { + // install cell editor + CellEditor cellEditor = fColumnEditor.getCellEditor(property, element, (Composite)getControl()); + if (cellEditor == null) { + // contradiction, oh well + return false; + } + disposeCellEditors(); + CellEditor[] newEditors = new CellEditor[getVisibleColumns().length]; + for (int i = 0; i < newEditors.length; i++) { + newEditors[i] = cellEditor; + } + setCellEditors(newEditors); + } + return canModify; + } + return false; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICellModifier#getValue(java.lang.Object, java.lang.String) + */ + public Object getValue(Object element, String property) { + if (fColumnEditor != null) { + return fColumnEditor.getCellModifier().getValue(element, property); + } + return null; + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ICellModifier#modify(java.lang.Object, java.lang.String, java.lang.Object) + */ + public void modify(Object element, String property, Object value) { + if (fColumnEditor != null) { + if (element instanceof Item) { + element = ((Item)element).getData(); + } + fColumnEditor.getCellModifier().modify(element, property, value); + } + } + + /** + * Disposes client's column editor and cell editors + */ + protected void dispose() { + disposeCellEditors(); + setCellEditors(null); + if (fColumnEditor != null) { + fColumnEditor.dispose(); + } + } + + /** + * Disposes current cell editors + */ + protected void disposeCellEditors() { + CellEditor[] cellEditors = getCellEditors(); + if (cellEditors != null) { + for (int i = 0; i < cellEditors.length; i++) { + CellEditor editor = cellEditors[i]; + if (editor != null) { + editor.dispose(); + } + } + } + } + + /** + * Sets the column editor for the given element + * + * @param element + */ + protected void updateColumnEditor(Object element) { + IColumnEditorFactoryAdapter factoryAdapter = getColumnEditorFactoryAdapter(element); + if (factoryAdapter != null) { + if (fColumnEditor != null) { + if (fColumnEditor.getId().equals(factoryAdapter.getColumnEditorId(getPresentationContext(), element))) { + // no change + return; + } else { + // dispose current + fColumnEditor.dispose(); + } + } + // create new one + fColumnEditor = factoryAdapter.createColumnEditor(getPresentationContext(), element); + if (fColumnEditor != null) { + fColumnEditor.init(getPresentationContext()); + } + } else { + // no editor - dispose current + if (fColumnEditor != null) { + fColumnEditor.dispose(); + fColumnEditor = null; + } + } + } + + /** + * Returns the column editor factory for the given element or <code>null</code>. + * + * @param input + * @return column editor factory of <code>null</code> + */ + protected IColumnEditorFactoryAdapter getColumnEditorFactoryAdapter(Object input) { + if (input instanceof IColumnEditorFactoryAdapter) { + return (IColumnEditorFactoryAdapter) input; + } + if (input instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) input; + return (IColumnEditorFactoryAdapter) adaptable.getAdapter(IColumnEditorFactoryAdapter.class); + } + return null; + } + } + + private CellModifierProxy fCellModifier; + + /** + * @param parent + * @param style + */ + public TreeModelViewer(Composite parent, int style, IPresentationContext context) { + super(parent, style); + if ((style & SWT.VIRTUAL) == 0) { + throw new IllegalArgumentException("style must include SWT.VIRTUAL"); //$NON-NLS-1$ + } + setUseHashlookup(true); + fCellModifier = new CellModifierProxy(); + fContext = context; + setContentProvider(new TreeModelContentProvider()); + setLabelProvider(new TreeModelLabelProvider(this)); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.ContentViewer#handleDispose(org.eclipse.swt.events.DisposeEvent) + */ + protected void handleDispose(DisposeEvent event) { + if (fColumnPresentation != null) { + fColumnPresentation.dispose(); + } + fCellModifier.dispose(); + super.handleDispose(event); + } + + /** + * Returns this viewer's presentation context or <code>null</code> if none. + * + * @return presentation context or <code>null</code> + */ + public IPresentationContext getPresentationContext() { + return fContext; + } + + /* (non-Javadoc) + * + * Clear filters when refreshing an element structurally. + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefresh(org.eclipse.swt.widgets.Widget, java.lang.Object, boolean, boolean) + */ + protected void internalRefresh(Widget widget, Object element, boolean doStruct, boolean updateLabels) { + if (doStruct) { + IContentProvider provider = getContentProvider(); + if (provider instanceof ModelContentProvider) { + ModelContentProvider mcp = (ModelContentProvider) provider; + if (widget instanceof TreeItem) { + mcp.refreshingStructure(getTreePathFromItem((TreeItem)widget)); + } else { + mcp.refreshingStructure(ModelContentProvider.EMPTY_TREE_PATH); + } + } + } + super.internalRefresh(widget, element, doStruct, updateLabels); + } + + protected void unmapElement(Object element, Widget widget) { + IContentProvider provider = getContentProvider(); + if (provider instanceof ModelContentProvider) { + ((ModelContentProvider) provider).unmapPath((TreePath) widget.getData(TREE_PATH_KEY)); + } + super.unmapElement(element, widget); + } + + /* (non-Javadoc) + * + * We need tree paths when disposed/unmapped in any order so cache the tree path. + * + * @see org.eclipse.jface.viewers.TreeViewer#mapElement(java.lang.Object, org.eclipse.swt.widgets.Widget) + */ + protected void mapElement(Object element, Widget widget) { + super.mapElement(element, widget); + if (widget instanceof Item) { + widget.setData(TREE_PATH_KEY, getTreePathFromItem((Item)widget)); + } else { + widget.setData(TREE_PATH_KEY, ModelContentProvider.EMPTY_TREE_PATH); + } + } + + + protected TreePath[] getTreePaths(Object element) { + Widget[] widgets = findItems(element); + TreePath[] paths = new TreePath[widgets.length]; + for (int i = 0; i < widgets.length; i++) { + TreePath path = (TreePath) widgets[i].getData(TREE_PATH_KEY); + if (path == null) { + if (widgets[i] instanceof Item) { + path = getTreePathFromItem((Item)widgets[i]); + } else { + path = ModelContentProvider.EMPTY_TREE_PATH; + } + } + paths[i] = path; + } + return paths; + } + + /* (non-Javadoc) + * + * Override because we allow inserting with filters present. + * + * @see org.eclipse.jface.viewers.AbstractTreeViewer#insert(java.lang.Object, java.lang.Object, int) + */ + public void insert(Object parentElementOrTreePath, Object element, int position) { + try { + fInserting = true; + super.insert(parentElementOrTreePath, element, position); + } finally { + fInserting = false; + } + } + + /* (non-Javadoc) + * + * Override because we allow inserting with filters present. + * + * @see org.eclipse.jface.viewers.StructuredViewer#hasFilters() + */ + protected boolean hasFilters() { + if (fInserting) { + return false; + } + return super.hasFilters(); + } + + /** + * Removes the element at the specified index of the parent. + * + * @param parent parent element + * @param index child index + */ + public void remove(final Object parent, final int index) { + preservingSelection(new Runnable() { + public void run() { + if (parent.equals(getInput())) { + Tree tree = (Tree) getControl(); + if (index < tree.getItemCount()) { + TreeItem item = tree.getItem(index); + Object element = item.getData(); + if (element != null) { + unmapElement(element, item); + } + item.dispose(); + } + } else { + Widget[] parentItems = findItems(parent); + for (int i = 0; i < parentItems.length; i++) { + TreeItem parentItem = (TreeItem) parentItems[i]; + if (index < parentItem.getItemCount()) { + TreeItem item = parentItem.getItem(index); + Object element = item.getData(); + if (element != null) { + unmapElement(element, item); + } + item.dispose(); + } + } + } + } + }); + } + + /* (non-Javadoc) + * @see org.eclipse.jface.viewers.AbstractTreeViewer#inputChanged(java.lang.Object, java.lang.Object) + */ + protected void inputChanged(Object input, Object oldInput) { + super.inputChanged(input, oldInput); + resetColumns(input); + } + + /** + * Configures the columns for the given viewer input. + * + * @param input + */ + protected void resetColumns(Object input) { + if (input != null) { + // only change columns if the input is non-null (persist when empty) + IColumnPresentationFactoryAdapter factory = getColumnPresenetationFactoryAdapter(input); + PresentationContext context = (PresentationContext) getPresentationContext(); + String type = null; + if (factory != null) { + type = factory.getColumnPresentationId(context, input); + } + if (type != null) { + if (fColumnPresentation != null) { + if (!fColumnPresentation.getId().equals(type)) { + // dispose old, create new + fColumnPresentation.dispose(); + fColumnPresentation = null; + } + } + if (fColumnPresentation == null) { + fColumnPresentation = factory.createColumnPresentation(context, input); + if (fColumnPresentation != null) { + fColumnPresentation.init(context); + configureColumns(); + } + } + } else { + if (fColumnPresentation != null) { + fColumnPresentation.dispose(); + fColumnPresentation = null; + configureColumns(); + } + } + } + } + + /** + * Returns the column presentation factory for the given element or <code>null</code>. + * + * @param input + * @return column presentation factory of <code>null</code> + */ + protected IColumnPresentationFactoryAdapter getColumnPresenetationFactoryAdapter(Object input) { + if (input instanceof IColumnPresentationFactoryAdapter) { + return (IColumnPresentationFactoryAdapter) input; + } + if (input instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) input; + return (IColumnPresentationFactoryAdapter) adaptable.getAdapter(IColumnPresentationFactoryAdapter.class); + } + return null; + } + + /** + * Configures the columns based on the current settings. + * + * @param input + */ + protected void configureColumns() { + if (fColumnPresentation != null) { + IColumnPresentation build = null; + if (isShowColumns(fColumnPresentation.getId())) { + build = fColumnPresentation; + } + buildColumns(build); + } else { + // get rid of columns + buildColumns(null); + } + } + + /** + * Toggles columns on/off for the current column presentation, if any. + * + * @param show whether to show columns if the current input supports + * columns + */ + public void setShowColumns(boolean show) { + if (show) { + if (!isShowColumns()) { + fShowColumns.remove(fColumnPresentation.getId()); + } + } else { + if (isShowColumns()){ + fShowColumns.put(fColumnPresentation.getId(), Boolean.FALSE); + } + } + refreshColumns(); + } + + /** + * Resets any persisted column size for the given columns + */ + public void resetColumnSizes(String[] columnIds) { + for (int i = 0; i < columnIds.length; i++) { + fColumnSizes.remove(columnIds[i]); + } + } + + /** + * Sets the id's of visible columns, or <code>null</code> to set default columns. + * Only effects the current column presentation. + * + * @param ids visible columns + */ + public void setVisibleColumns(String[] ids) { + IColumnPresentation presentation = getColumnPresentation(); + if (presentation != null) { + fColumnOrder.remove(presentation.getId()); + fVisibleColumns.remove(presentation.getId()); + if (ids != null) { + // put back in table if not default + String[] columns = presentation.getInitialColumns(); + if (columns.length == ids.length) { + for (int i = 0; i < columns.length; i++) { + if (!ids[i].equals(columns[i])) { + fVisibleColumns.put(presentation.getId(), ids); + break; + } + } + } else { + fVisibleColumns.put(presentation.getId(), ids); + } + } + PresentationContext presentationContext = (PresentationContext) getPresentationContext(); + presentationContext.setColumns(getVisibleColumns()); + refreshColumns(); + } + } + + /** + * Refreshes the columns in the view, based on the viewer input. + */ + public void refreshColumns() { + configureColumns(); + refresh(); + } + + /** + * Returns whether columns are being displayed currently. + * + * @return + */ + public boolean isShowColumns() { + if (fColumnPresentation != null) { + return isShowColumns(fColumnPresentation.getId()); + } + return false; + } + + /** + * Returns whether columns can be toggled on/off for the current input. + * + * @return whether columns can be toggled on/off for the current input + */ + public boolean canToggleColumns() { + return fColumnPresentation != null && fColumnPresentation.isOptional(); + } + + protected boolean isShowColumns(String columnPresentationId) { + Boolean bool = (Boolean) fShowColumns.get(columnPresentationId); + if (bool == null) { + return true; + } + return bool.booleanValue(); + } + + /** + * Creates new columns for the given presentation. + * + * TODO: does this need to be async? + * + * @param presentation + */ + protected void buildColumns(IColumnPresentation presentation) { + // dispose current columns, persisting their weights + Tree tree = getTree(); + final TreeColumn[] columns = tree.getColumns(); + String[] visibleColumnIds = getVisibleColumns(); + for (int i = 0; i < columns.length; i++) { + TreeColumn treeColumn = columns[i]; + treeColumn.removeControlListener(fListener); + treeColumn.dispose(); + } + PresentationContext presentationContext = (PresentationContext) getPresentationContext(); + if (presentation != null) { + for (int i = 0; i < visibleColumnIds.length; i++) { + String id = visibleColumnIds[i]; + String header = presentation.getHeader(id); + // TODO: allow client to specify style + TreeColumn column = new TreeColumn(tree, SWT.LEFT, i); + column.setMoveable(true); + column.setText(header); + column.setWidth(1); + ImageDescriptor image = presentation.getImageDescriptor(id); + if (image != null) { + column.setImage(((TreeModelLabelProvider)getLabelProvider()).getImage(image)); + } + column.setData(id); + } + int[] order = (int[]) fColumnOrder.get(presentation.getId()); + if (order != null) { + tree.setColumnOrder(order); + } + tree.setHeaderVisible(true); + tree.setLinesVisible(true); + presentationContext.setColumns(getVisibleColumns()); + setColumnProperties(getVisibleColumns()); + setCellModifier(fCellModifier); + } else { + tree.setHeaderVisible(false); + tree.setLinesVisible(false); + presentationContext.setColumns(null); + setCellModifier(null); + setColumnProperties(null); + } + + + int avg = tree.getSize().x; + if (visibleColumnIds != null) + avg /= visibleColumnIds.length; + + if (avg == 0) { + tree.addPaintListener(new PaintListener() { + public void paintControl(PaintEvent e) { + Tree tree2 = getTree(); + String[] visibleColumns = getVisibleColumns(); + if (visibleColumns != null) { + int avg1 = tree2.getSize().x / visibleColumns.length; + initColumns(avg1); + } + tree2.removePaintListener(this); + } + }); + } else { + initColumns(avg); + } + } + + private void initColumns(int widthHint) { + TreeColumn[] columns = getTree().getColumns(); + for (int i = 0; i < columns.length; i++) { + TreeColumn treeColumn = columns[i]; + Integer width = (Integer) fColumnSizes.get(treeColumn.getData()); + if (width == null) { + treeColumn.setWidth(widthHint); + } else { + treeColumn.setWidth(width.intValue()); + } + treeColumn.addControlListener(fListener); + } + } + + /** + * Returns the current column presentation for this viewer, or <code>null</code> + * if none. + * + * @return column presentation or <code>null</code> + */ + public IColumnPresentation getColumnPresentation() { + return fColumnPresentation; + } + + /** + * Returns identifiers of the visible columns in this viewer, or <code>null</code> + * if there is currently no column presentation. + * + * @return visible columns or <code>null</code> + */ + public String[] getVisibleColumns() { + if (isShowColumns()) { + IColumnPresentation presentation = getColumnPresentation(); + if (presentation != null) { + String[] columns = (String[]) fVisibleColumns.get(presentation.getId()); + if (columns == null) { + return presentation.getInitialColumns(); + } + return columns; + } + } + return null; + } + + /** + * Persists column sizes in cache + */ + protected void persistColumnSizes() { + Tree tree = getTree(); + TreeColumn[] columns = tree.getColumns(); + for (int i = 0; i < columns.length; i++) { + TreeColumn treeColumn = columns[i]; + Object id = treeColumn.getData(); + fColumnSizes.put(id, new Integer(treeColumn.getWidth())); + } + } + + /** + * Persists column ordering + */ + protected void persistColumnOrder() { + IColumnPresentation presentation = getColumnPresentation(); + if (presentation != null) { + Tree tree = getTree(); + int[] order = tree.getColumnOrder(); + if (order.length > 0) { + for (int i = 0; i < order.length; i++) { + if (i != order[i]) { + // non default order + fColumnOrder.put(presentation.getId(), order); + return; + } + } + } + // default order + fColumnOrder.remove(presentation.getId()); + } + } + + /** + * Save viewer state into the given memento. + * + * @param memento + */ + public void saveState(IMemento memento) { + if (!fColumnSizes.isEmpty()) { + Iterator iterator = fColumnSizes.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Entry) iterator.next(); + IMemento sizes = memento.createChild(COLUMN_SIZES, (String)entry.getKey()); + sizes.putInteger(SIZE, ((Integer)entry.getValue()).intValue()); + } + } + if (!fShowColumns.isEmpty()) { + Iterator iterator = fShowColumns.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Entry) iterator.next(); + IMemento sizes = memento.createChild(SHOW_COLUMNS, (String)entry.getKey()); + sizes.putString(SHOW_COLUMNS, ((Boolean)entry.getValue()).toString()); + } + } + if (!fVisibleColumns.isEmpty()) { + Iterator iterator = fVisibleColumns.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Entry) iterator.next(); + String id = (String) entry.getKey(); + IMemento visible = memento.createChild(VISIBLE_COLUMNS, id); + String[] columns = (String[]) entry.getValue(); + visible.putInteger(SIZE, columns.length); + for (int i = 0; i < columns.length; i++) { + visible.putString(COLUMN+Integer.toString(i), columns[i]); + } + } + } + if (!fColumnOrder.isEmpty()) { + Iterator iterator = fColumnOrder.entrySet().iterator(); + while (iterator.hasNext()) { + Map.Entry entry = (Entry) iterator.next(); + String id = (String) entry.getKey(); + IMemento orderMemento = memento.createChild(COLUMN_ORDER, id); + int[] order = (int[]) entry.getValue(); + orderMemento.putInteger(SIZE, order.length); + for (int i = 0; i < order.length; i++) { + orderMemento.putInteger(COLUMN+Integer.toString(i), order[i]); + } + } + } + } + + /** + * Initializes viewer state from the memento + * + * @param memento + */ + public void initState(IMemento memento) { + IMemento[] mementos = memento.getChildren(COLUMN_SIZES); + for (int i = 0; i < mementos.length; i++) { + IMemento child = mementos[i]; + String id = child.getID(); + Integer size = child.getInteger(SIZE); + if (size != null) { + fColumnSizes.put(id, size); + } + } + mementos = memento.getChildren(SHOW_COLUMNS); + for (int i = 0; i < mementos.length; i++) { + IMemento child = mementos[i]; + String id = child.getID(); + Boolean bool = Boolean.valueOf(child.getString(SHOW_COLUMNS)); + if (!bool.booleanValue()) { + fShowColumns.put(id, bool); + } + } + mementos = memento.getChildren(VISIBLE_COLUMNS); + for (int i = 0; i < mementos.length; i++) { + IMemento child = mementos[i]; + String id = child.getID(); + Integer integer = child.getInteger(SIZE); + if (integer != null) { + int length = integer.intValue(); + String[] columns = new String[length]; + for (int j = 0; j < length; j++) { + columns[j] = child.getString(COLUMN+Integer.toString(j)); + } + fVisibleColumns.put(id, columns); + } + } + mementos = memento.getChildren(COLUMN_ORDER); + for (int i = 0; i < mementos.length; i++) { + IMemento child = mementos[i]; + String id = child.getID(); + Integer integer = child.getInteger(SIZE); + if (integer != null) { + int length = integer.intValue(); + int[] order = new int[length]; + for (int j = 0; j < length; j++) { + order[j] = child.getInteger(COLUMN+Integer.toString(j)).intValue(); + } + fColumnOrder.put(id, order); + } + } + } + + /** + * @param updates + */ + protected void apply(LabelUpdate[] updates) { + Object prevElement = null; + Widget[] widgets = null; + for (int i = 0; i < updates.length; i++) { + LabelUpdate update = updates[i]; + Object element = update.getElement(); + if (!element.equals(prevElement)) { + prevElement = element; + widgets = findItems(element); + } + for (int j = 0; j < widgets.length; j++) { + Widget widget = widgets[j]; + if (widget instanceof TreeItem) { + TreeItem item = (TreeItem) widget; + ViewerRow row = getRowPartFromItem(item); + update.update(row); + } + } + } + } + + /** + * Returns whether the candidate selection should override the current + * selection. + * + * @param current + * @param curr + * @return + */ + protected boolean overrideSelection(ISelection current, ISelection candidate) { + IModelSelectionPolicy selectionPolicy = getSelectionPolicy(current); + if (selectionPolicy == null) { + return true; + } + if (selectionPolicy.contains(candidate, getPresentationContext())) { + return selectionPolicy.overrides(current, candidate, getPresentationContext()); + } + return !selectionPolicy.isSticky(current, getPresentationContext()); + } + + /** + * Returns the selection policy associated with the given selection + * or <code>null</code> if none. + * + * @param selection or <code>null</code> + * @return selection policy or <code>null</code> + */ + protected IModelSelectionPolicy getSelectionPolicy(ISelection selection) { + if (selection instanceof IStructuredSelection) { + IStructuredSelection ss = (IStructuredSelection) selection; + Object element = ss.getFirstElement(); + if (element instanceof IAdaptable) { + IAdaptable adaptable = (IAdaptable) element; + IModelSelectionPolicyFactoryAdapter factory = (IModelSelectionPolicyFactoryAdapter) adaptable.getAdapter(IModelSelectionPolicyFactoryAdapter.class); + if (factory != null) { + return factory.createModelSelectionPolicyAdapter(adaptable, getPresentationContext()); + } + } + } + return null; + } + + /* (non-Javadoc) + * + * Consider selection policy + * + * @see org.eclipse.jface.viewers.StructuredViewer#setSelection(org.eclipse.jface.viewers.ISelection, boolean) + */ + public void setSelection(ISelection selection, boolean reveal) { + if (!overrideSelection(getSelection(), selection)) { + return; + } + super.setSelection(selection, reveal); + } + + /** + * Registers the specified listener for view update notifications. + * + * @param listener listener + */ + public void addViewerUpdateListener(IViewerUpdateListener listener) { + ((ModelContentProvider)getContentProvider()).addViewerUpdateListener(listener); + } + + /** + * Removes the specified listener from update notifications. + * + * @param listener listener + */ + public void removeViewerUpdateListener(IViewerUpdateListener listener) { + ModelContentProvider cp = (ModelContentProvider)getContentProvider(); + if (cp != null) { + cp.removeViewerUpdateListener(listener); + } + } + + /** + * Registers the given listener for model delta notification. + * + * @param listener model delta listener + */ + public void addModelChangedListener(IModelChangedListener listener) { + ((ModelContentProvider)getContentProvider()).addModelChangedListener(listener); + } + + /** + * Unregisters the given listener from model delta notification. + * + * @param listener model delta listener + */ + public void removeModelChangedListener(IModelChangedListener listener) { + ModelContentProvider cp = (ModelContentProvider)getContentProvider(); + if (cp != null) { + cp.removeModelChangedListener(listener); + } + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ViewerUpdateMonitor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ViewerUpdateMonitor.java new file mode 100644 index 000000000..5b2e8b8f7 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/model/viewers/ViewerUpdateMonitor.java @@ -0,0 +1,124 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.model.viewers; + +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.core.runtime.jobs.ISchedulingRule; +import org.eclipse.debug.internal.ui.actions.context.AbstractRequestMonitor; +import org.eclipse.debug.internal.ui.model.IPresentationUpdate; +import org.eclipse.debug.internal.ui.viewers.AsynchronousSchedulingRuleFactory; +import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; +import org.eclipse.ui.progress.WorkbenchJob; + +/** + * @since 3.3 + */ +abstract class ViewerUpdateMonitor extends AbstractRequestMonitor implements IPresentationUpdate { + + private ModelContentProvider fContentProvider; + + /** + * Whether this request's 'done' method has been called. + */ + private boolean fDone = false; + + protected WorkbenchJob fViewerUpdateJob = new WorkbenchJob("Asynchronous viewer update") { //$NON-NLS-1$ + public IStatus runInUIThread(IProgressMonitor monitor) { + // necessary to check if viewer is disposed + try { + if (!isCanceled() && !getContentProvider().isDisposed()) { + IStatus status = getStatus(); + if (status != null && !status.isOK()) { + getContentProvider().handlePresentationFailure(ViewerUpdateMonitor.this, status); + } else { + performUpdate(); + } + } + } finally { + getContentProvider().updateComplete(ViewerUpdateMonitor.this); + } + return Status.OK_STATUS; + } + }; + + /** + * Constructs an update for the given content provider + * + * @param contentProvider content provider + */ + public ViewerUpdateMonitor(ModelContentProvider contentProvider) { + fContentProvider = contentProvider; + // serialize updates per viewer + fViewerUpdateJob.setRule(getUpdateSchedulingRule()); + fViewerUpdateJob.setSystem(true); + contentProvider.updateStarted(this); + } + + /** + * Returns the scheduling rule for viewer update job. + * + * @return rule or <code>null</code> + */ + protected ISchedulingRule getUpdateSchedulingRule() { + return AsynchronousSchedulingRuleFactory.getDefault().newSerialPerObjectRule(getContentProvider()); + } + + /** + * Returns the model content provider this update is being performed for. + * + * @return the model content provider this update is being performed for + */ + protected ModelContentProvider getContentProvider() { + return fContentProvider; + } + + /* (non-Javadoc) + * @see org.eclipse.core.runtime.IProgressMonitor#done() + */ + public final void done() { + synchronized (this) { + fDone = true; + } + scheduleViewerUpdate(0L); + } + + /** + * Returns whether this request is done yet. + * + * @return + */ + protected synchronized boolean isDone() { + return fDone; + } + + protected void scheduleViewerUpdate(long ms) { + if(!isCanceled()) { + fViewerUpdateJob.schedule(ms); + } else { + getContentProvider().updateComplete(this); + } + } + + /** + * Notification this update has been completed and should now be applied to + * this update's viewer. This method is called in the UI thread. + */ + protected abstract void performUpdate(); + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.IPresentationUpdate#getPresentationContext() + */ + public IPresentationContext getPresentationContext() { + return fContentProvider.getPresentationContext(); + } +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/PresentationContext.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/PresentationContext.java index 24c0549f9..020aedcf2 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/PresentationContext.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/PresentationContext.java @@ -94,7 +94,7 @@ public class PresentationContext implements IPresentationContext { * * @param ids column identifiers */ - protected void setColumns(String[] ids) { + public void setColumns(String[] ids) { String[] oldValue = fColumns; fColumns = ids; firePropertyChange(IPresentationContext.PROPERTY_COLUMNS, oldValue, ids); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TableUpdatePolicy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TableUpdatePolicy.java index 4e81f8631..9281635fa 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TableUpdatePolicy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TableUpdatePolicy.java @@ -75,7 +75,7 @@ public class TableUpdatePolicy extends org.eclipse.debug.internal.ui.viewers.Abs handleInsert(node); } - IModelDelta[] childNodes = node.getNodes(); + IModelDelta[] childNodes = node.getChildDeltas(); updateNodes(childNodes); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TreeUpdatePolicy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TreeUpdatePolicy.java index 2f1a31ad0..4f1342911 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TreeUpdatePolicy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/TreeUpdatePolicy.java @@ -69,7 +69,7 @@ public class TreeUpdatePolicy extends AbstractUpdatePolicy implements IModelChan // TODO } - updateNodes(node.getNodes()); + updateNodes(node.getChildDeltas()); } } @@ -101,8 +101,8 @@ public class TreeUpdatePolicy extends AbstractUpdatePolicy implements IModelChan if (node != fNode) { ArrayList list = new ArrayList(); list.add(0, node.getElement()); - while (node.getParent() != null) { - node = node.getParent(); + while (node.getParentDelta() != null) { + node = node.getParentDelta(); list.add(0, node.getElement()); } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java index 7608f3732..91c611120 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/AbstractModelProxy.java @@ -118,8 +118,5 @@ public abstract class AbstractModelProxy implements IModelProxy { */ public void installed() { } - - - } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDelta.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDelta.java index d945086ae..8db3e5b86 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDelta.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDelta.java @@ -84,13 +84,26 @@ public interface IModelDelta { * Suggests that the element should be selected, as described by its path. */ public static int SELECT = 1 << 21; + + /** + * Indicates a model proxy should be installed for the given element + * @since 3.3 + */ + public static int INSTALL = 1 << 22; + + /** + * Indicates a model proxy should be uninstalled for the given element + * @since 3.3 + */ + public static int UNINSTALL = 1 << 23; + /** * Returns the parent of this node, or <code>null</code> if this is * a root node. * * @return parent node or <code>null</code> if this is a root node */ - public IModelDelta getParent(); + public IModelDelta getParentDelta(); /** * Returns the model element this node describes. @@ -112,7 +125,7 @@ public interface IModelDelta { * * @return changed children, possibly empty */ - public ModelDelta[] getNodes(); + public ModelDelta[] getChildDeltas(); /** * When a node indicates the <code>IModelDelta.REPLACED</code> flag, this method @@ -123,12 +136,35 @@ public interface IModelDelta { public Object getReplacementElement(); /** + * Returns this node's index in its parents child collection or -1 if unknown. + * This attribute is required when expanding or selecting an element. + * <p> * When a node indicates the <code>IModelDelta.INSERTED</code> flag, this method * returns the index that the new element should be inserted at relative to its * parents children, otherwise -1. - * + * </p> * @return insertion index or -1 */ public int getIndex(); + /** + * Returns the total number of children this element has, or -1 if unknown. Note + * that this number may be greater than the number of child delta nodes for this + * node, since not all children may be reporting deltas. + * <p> + * This attribute is required when expanding or selecting an element. + * </p> + * + * @return total number of child elements this element has + */ + public int getChildCount(); + + /** + * Accepts the given visitor. + * + * @param visitor + * @since 3.3 + */ + public void accept(IModelDeltaVisitor visitor); + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDeltaVisitor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDeltaVisitor.java new file mode 100644 index 000000000..8d198cd77 --- /dev/null +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/IModelDeltaVisitor.java @@ -0,0 +1,31 @@ +/******************************************************************************* + * Copyright (c) 2006 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + *******************************************************************************/ +package org.eclipse.debug.internal.ui.viewers.provisional; + + +/** + * An objects that visits model deltas. + * + * @since 3.3 + */ +public interface IModelDeltaVisitor { + + /** + * Visits the given model delta. + * + * @param delta the delta to visit + * @param depth depth in the delta where 0 == root node + * @return <code>true</code> if the model delta's children should + * be visited; <code>false</code> if they should be skipped. + */ + public boolean visit(IModelDelta delta, int depth); + +} diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/ModelDelta.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/ModelDelta.java index 7fe6d8e0c..1f0777aea 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/ModelDelta.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/provisional/ModelDelta.java @@ -27,7 +27,8 @@ public class ModelDelta implements IModelDelta { private int fFlags; private ModelDelta[] fNodes = EMPTY_NODES; private Object fReplacement; - private int fIndex; + private int fIndex = -1; + private int fChildCount = -1; private static final ModelDelta[] EMPTY_NODES = new ModelDelta[0]; /** @@ -68,6 +69,22 @@ public class ModelDelta implements IModelDelta { fIndex = index; fFlags = flags; } + + /** + * Constructs a new delta for the given element at the specified index + * relative to its parent with the given number of children. + * + * @param element model element + * @param index insertion position + * @param flags change flags + * @param childCount number of children this node has + */ + public ModelDelta(Object element, int index, int flags, int childCount) { + fElement = element; + fIndex = index; + fFlags = flags; + fChildCount = childCount; + } /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getElement() @@ -132,6 +149,23 @@ public class ModelDelta implements IModelDelta { } /** + * Adds a child delta to this delta at the specified index with the + * given number of children, and returns the newly created child delta. + * + * @param element child element in insert + * @param index index of the element relative to parent + * @param flags change flags + * @param numChildren the number of children the element has + * @return newly created child delta + */ + public ModelDelta addNode(Object element, int index, int flags, int numChildren) { + ModelDelta node = new ModelDelta(element, index, flags, numChildren); + node.setParent(this); + addDelta(node); + return node; + } + + /** * Sets the parent delta of this delta * * @param node parent delta @@ -143,7 +177,7 @@ public class ModelDelta implements IModelDelta { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getParent() */ - public IModelDelta getParent() { + public IModelDelta getParentDelta() { return fParent; } @@ -164,7 +198,7 @@ public class ModelDelta implements IModelDelta { /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.IModelDelta#getNodes() */ - public ModelDelta[] getNodes() { + public ModelDelta[] getChildDeltas() { return fNodes; } @@ -220,11 +254,63 @@ public class ModelDelta implements IModelDelta { if ((flags & IModelDelta.STATE) > 0) { buf.append("STATE | "); //$NON-NLS-1$ } + if ((flags & IModelDelta.INSTALL) > 0) { + buf.append("INSTALL | "); //$NON-NLS-1$ + } + if ((flags & IModelDelta.UNINSTALL) > 0) { + buf.append("UNINSTALL | "); //$NON-NLS-1$ + } } buf.append('\n'); - ModelDelta[] nodes = delta.getNodes(); + buf.append("\t\tIndex: "); //$NON-NLS-1$ + buf.append(fIndex); + buf.append(" Child Count: "); //$NON-NLS-1$ + buf.append(fChildCount); + buf.append('\n'); + ModelDelta[] nodes = delta.getChildDeltas(); for (int i = 0; i < nodes.length; i++) { appendDetail(buf, nodes[i]); } } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#getChildCount() + */ + public int getChildCount() { + return fChildCount; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta#accept(org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor) + */ + public void accept(IModelDeltaVisitor visitor) { + doAccept(visitor, 0); + } + + protected void doAccept(IModelDeltaVisitor visitor, int depth) { + if (visitor.visit(this, depth)) { + ModelDelta[] childDeltas = getChildDeltas(); + for (int i = 0; i < childDeltas.length; i++) { + childDeltas[i].doAccept(visitor, depth+1); + } + } + } + + /** + * Sets this delta's element + * + * @param element + */ + public void setElement(Object element) { + fElement = element; + } + + /** + * Sets this delta's flags. + * + * @param flags + */ + public void setFlags(int flags) { + fFlags = flags; + } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugEventHandler.java index fe26a476c..1b4fbdce6 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugEventHandler.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugEventHandler.java @@ -173,4 +173,14 @@ public abstract class DebugEventHandler { protected synchronized boolean isDisposed() { return fModelProxy == null; } + + protected int indexOf(Object[] list, Object element) { + for (int i = 0; i < list.length; i++) { + if (element.equals(list[i])) { + return i; + } + } + return -1; + } + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetEventHandler.java index d569f4519..172ff610e 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetEventHandler.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetEventHandler.java @@ -60,7 +60,7 @@ public class DebugTargetEventHandler extends DebugEventHandler { } protected void handleTerminate(DebugEvent event) { - fireDelta((IDebugTarget) event.getSource(), IModelDelta.STATE); + fireDelta((IDebugTarget) event.getSource(), IModelDelta.STATE | IModelDelta.UNINSTALL); } private void fireDelta(IDebugTarget target, int flags) { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetProxy.java index e5531c2b7..56fd179d9 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DebugTargetProxy.java @@ -80,10 +80,10 @@ public class DebugTargetProxy extends EventHandlerModelProxy { IStackFrame frame = thread.getTopStackFrame(); if (frame != null) { ModelDelta delta = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); - ModelDelta node = delta.addNode(target.getLaunch(), IModelDelta.NO_CHANGE); - node = node.addNode(target, IModelDelta.NO_CHANGE); - node = node.addNode(thread, IModelDelta.NO_CHANGE | IModelDelta.EXPAND); - node = node.addNode(frame, IModelDelta.NO_CHANGE | IModelDelta.SELECT); + ModelDelta node = delta.addNode(target.getLaunch(), -1, IModelDelta.NO_CHANGE, target.getLaunch().getChildren().length); + node = node.addNode(target, 0, IModelDelta.NO_CHANGE, threads.length); + node = node.addNode(thread, i, IModelDelta.NO_CHANGE | IModelDelta.EXPAND, thread.getStackFrames().length); + node = node.addNode(frame, 0, IModelDelta.NO_CHANGE | IModelDelta.SELECT, 0); fireModelChanged(delta); return; } @@ -91,8 +91,8 @@ public class DebugTargetProxy extends EventHandlerModelProxy { } // expand the target if no suspended thread ModelDelta delta = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); - ModelDelta node = delta.addNode(target.getLaunch(), IModelDelta.NO_CHANGE); - node = node.addNode(target, IModelDelta.EXPAND | IModelDelta.SELECT); + ModelDelta node = delta.addNode(target.getLaunch(), -1, IModelDelta.NO_CHANGE, target.getLaunch().getChildren().length); + node = node.addNode(target, 0, IModelDelta.EXPAND | IModelDelta.SELECT, threads.length); fireModelChanged(delta); } catch (DebugException e) { } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultModelProxyFactory.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultModelProxyFactory.java index 18fb82fc8..c339e2ac7 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultModelProxyFactory.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultModelProxyFactory.java @@ -49,7 +49,8 @@ public class DefaultModelProxyFactory implements IModelProxyFactoryAdapter { if (IDebugUIConstants.ID_EXPRESSION_VIEW.equals(id)) { if (element instanceof IExpressionManager) { return new ExpressionManagerModelProxy(); - } if (element instanceof IWatchExpression) { + } + if (element instanceof IWatchExpression) { IWorkbenchPart part = context.getPart(); if (part == null) { return null; diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultVariableViewModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultVariableViewModelProxy.java index dc449df61..58642c150 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultVariableViewModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultVariableViewModelProxy.java @@ -59,5 +59,5 @@ public class DefaultVariableViewModelProxy extends EventHandlerModelProxy { } return false; } - + } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionEventHandler.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionEventHandler.java index 877bb50a3..e1bd8cabd 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionEventHandler.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionEventHandler.java @@ -48,7 +48,14 @@ public class ExpressionEventHandler extends DebugEventHandler { } } if (expression != null) { - delta.addNode(expression, IModelDelta.CONTENT | IModelDelta.STATE); + int flags = IModelDelta.NO_CHANGE; + if ((event.getDetail() & DebugEvent.STATE) != 0) { + flags = flags | IModelDelta.STATE; + } + if ((event.getDetail() & DebugEvent.CONTENT) != 0) { + flags = flags | IModelDelta.CONTENT; + } + delta.addNode(expression, flags); fireDelta(delta); } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionManagerModelProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionManagerModelProxy.java index c27c4a88c..aac1572b8 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionManagerModelProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ExpressionManagerModelProxy.java @@ -12,6 +12,7 @@ package org.eclipse.debug.internal.ui.viewers.update; import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.IExpressionManager; import org.eclipse.debug.core.IExpressionsListener; import org.eclipse.debug.core.model.IExpression; import org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy; @@ -27,7 +28,21 @@ public class ExpressionManagerModelProxy extends AbstractModelProxy implements I */ public void init(IPresentationContext context) { super.init(context); - DebugPlugin.getDefault().getExpressionManager().addExpressionListener(this); + getExpressionManager().addExpressionListener(this); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.AbstractModelProxy#installed() + */ + public void installed() { + updateExpressions(getExpressionManager().getExpressions(), IModelDelta.INSTALL); + } + + /** + * @return + */ + protected IExpressionManager getExpressionManager() { + return DebugPlugin.getDefault().getExpressionManager(); } /* (non-Javadoc) @@ -35,21 +50,21 @@ public class ExpressionManagerModelProxy extends AbstractModelProxy implements I */ public synchronized void dispose() { super.dispose(); - DebugPlugin.getDefault().getExpressionManager().removeExpressionListener(this); + getExpressionManager().removeExpressionListener(this); } /* (non-Javadoc) * @see org.eclipse.debug.core.IExpressionsListener#expressionsAdded(org.eclipse.debug.core.model.IExpression[]) */ public void expressionsAdded(IExpression[] expressions) { - updateExpressions(expressions, IModelDelta.ADDED); + updateExpressions(expressions, IModelDelta.ADDED | IModelDelta.INSTALL); } /* (non-Javadoc) * @see org.eclipse.debug.core.IExpressionsListener#expressionsRemoved(org.eclipse.debug.core.model.IExpression[]) */ public void expressionsRemoved(IExpression[] expressions) { - updateExpressions(expressions, IModelDelta.REMOVED); + updateExpressions(expressions, IModelDelta.REMOVED | IModelDelta.UNINSTALL); } /* (non-Javadoc) @@ -60,7 +75,7 @@ public class ExpressionManagerModelProxy extends AbstractModelProxy implements I } private void updateExpressions(IExpression[] expressions, int flags) { - ModelDelta delta = new ModelDelta(DebugPlugin.getDefault() .getExpressionManager(), IModelDelta.NO_CHANGE); + ModelDelta delta = new ModelDelta(getExpressionManager(), IModelDelta.NO_CHANGE); for (int i = 0; i < expressions.length; i++) { IExpression expression = expressions[i]; delta.addNode(expression, flags); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/LaunchManagerProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/LaunchManagerProxy.java index ed2aaf2ed..b7e332e60 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/LaunchManagerProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/LaunchManagerProxy.java @@ -10,6 +10,11 @@ *******************************************************************************/ package org.eclipse.debug.internal.ui.viewers.update; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchManager; @@ -22,6 +27,11 @@ import org.eclipse.debug.internal.ui.viewers.provisional.ModelDelta; public class LaunchManagerProxy extends AbstractModelProxy implements ILaunchesListener2 { private ILaunchManager fLaunchManager; + /** + * Map of each launch to its previous children. When a child is added, + * its model proxy is installed. + */ + private Map fPrevChildren = new HashMap(); /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.viewers.AbstractModelProxy#init(org.eclipse.debug.internal.ui.viewers.IPresentationContext) @@ -38,9 +48,7 @@ public class LaunchManagerProxy extends AbstractModelProxy implements ILaunchesL public void installed() { // expand existing launches ILaunch[] launches = fLaunchManager.getLaunches(); - if (launches.length > 0) { - fireDelta(launches, IModelDelta.EXPAND); - } + launchesAdded(launches); } /* (non-Javadoc) @@ -64,6 +72,10 @@ public class LaunchManagerProxy extends AbstractModelProxy implements ILaunchesL */ public void launchesRemoved(ILaunch[] launches) { fireDelta(launches, IModelDelta.REMOVED); + // clear the children cache + for (int i = 0; i < launches.length; i++) { + fPrevChildren.remove(launches[i]); + } } /* (non-Javadoc) @@ -71,6 +83,12 @@ public class LaunchManagerProxy extends AbstractModelProxy implements ILaunchesL */ public void launchesAdded(ILaunch[] launches) { fireDelta(launches, IModelDelta.ADDED | IModelDelta.EXPAND); + // install model proxies + for (int i = 0; i < launches.length; i++) { + ILaunch launch = launches[i]; + fPrevChildren.put(launch, new HashSet()); + } + installModelProxies(launches); } /* (non-Javadoc) @@ -78,6 +96,34 @@ public class LaunchManagerProxy extends AbstractModelProxy implements ILaunchesL */ public void launchesChanged(ILaunch[] launches) { fireDelta(launches, IModelDelta.STATE | IModelDelta.CONTENT); + // install model proxies for new children + installModelProxies(launches); + } + + /** + * Installs model proxies for any new children in the given launches. + * + * @param launches + */ + protected void installModelProxies(ILaunch[] launches) { + boolean changes = false; + ModelDelta root = new ModelDelta(fLaunchManager, IModelDelta.NO_CHANGE); + for (int i = 0; i < launches.length; i++) { + ILaunch launch = launches[i]; + ModelDelta launchDelta = root.addNode(launch, IModelDelta.NO_CHANGE); + Object[] children = launch.getChildren(); + Set set = (Set) fPrevChildren.get(launch); + for (int j = 0; j < children.length; j++) { + Object child = children[j]; + if (set.add(child)) { + changes = true; + launchDelta.addNode(child, IModelDelta.INSTALL); + } + } + } + if (changes) { + fireModelChanged(root); + } } protected void fireDelta(ILaunch[] launches, int launchFlags) { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ProcessProxy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ProcessProxy.java index 2157ad040..7139168fe 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ProcessProxy.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/ProcessProxy.java @@ -28,29 +28,31 @@ public class ProcessProxy extends EventHandlerModelProxy { } protected void handleChange(DebugEvent event) { + fireDelta(IModelDelta.STATE); + } + + protected void handleCreate(DebugEvent event) { + // do nothing - Launch change notification handles this + } + + protected void handleTerminate(DebugEvent event) { + fireDelta(IModelDelta.STATE | IModelDelta.UNINSTALL); + } + + private void fireDelta(int flags) { ModelDelta delta = null; synchronized (ProcessProxy.this) { if (!isDisposed()) { delta = new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); ModelDelta node = delta; node = node.addNode(fProcess.getLaunch(), IModelDelta.NO_CHANGE); - node.addNode(fProcess, IModelDelta.STATE); + node.addNode(fProcess, flags); } } if (delta != null && !isDisposed()) { fireModelChanged(delta); - } + } } - - protected void handleCreate(DebugEvent event) { - // do nothing - Launch change notification handles this - } - - protected void handleTerminate(DebugEvent event) { - handleChange(event); - } - - }; 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 4b19e034d..5f1b5e6cb 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 @@ -18,6 +18,8 @@ import java.util.Set; import org.eclipse.debug.core.DebugEvent; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.DebugPlugin; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.ILaunchManager; import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IStackFrame; import org.eclipse.debug.core.model.IThread; @@ -157,12 +159,36 @@ public class ThreadEventHandler extends DebugEventHandler { } protected ModelDelta buildRootDelta() { - return new ModelDelta(DebugPlugin.getDefault().getLaunchManager(), IModelDelta.NO_CHANGE); + return new ModelDelta(getLaunchManager(), IModelDelta.NO_CHANGE); + } + + /** + * Returns the launch manager. + * + * @return the launch manager + */ + protected ILaunchManager getLaunchManager() { + return DebugPlugin.getDefault().getLaunchManager(); } + /** + * Adds nodes into the delta up to but not including the given thread. + * + * @param delta root delta for the view (includes viewer input) + * @param thread thread for which path is requested + * @return + */ protected ModelDelta addPathToThread(ModelDelta delta, IThread thread) { - delta = delta.addNode(thread.getLaunch(), IModelDelta.NO_CHANGE); - return delta.addNode(thread.getDebugTarget(), IModelDelta.NO_CHANGE); + ILaunch launch = thread.getLaunch(); + Object[] children = launch.getChildren(); + delta = delta.addNode(launch, indexOf(getLaunchManager().getLaunches(), launch), IModelDelta.NO_CHANGE, children.length); + IDebugTarget debugTarget = thread.getDebugTarget(); + int numThreads = -1; + try { + numThreads = debugTarget.getThreads().length; + } catch (DebugException e) { + } + return delta.addNode(debugTarget, indexOf(children, debugTarget), IModelDelta.NO_CHANGE, numThreads); } private void fireDeltaAndClearTopFrame(IThread thread, int flags) { @@ -187,20 +213,22 @@ public class ThreadEventHandler extends DebugEventHandler { frame = thread.getTopStackFrame(); } catch (DebugException e) { } + int threadIndex = indexOf(thread); + int childCount = childCount(thread); if (isEqual(frame, prev)) { if (frame == null) { if (thread.isSuspended()) { // no frames, but suspended - update & select - node = node.addNode(thread, flags | IModelDelta.STATE | IModelDelta.SELECT); + node = node.addNode(thread, threadIndex, flags | IModelDelta.STATE | IModelDelta.SELECT, childCount); } } else { - node = node.addNode(thread, flags); + node = node.addNode(thread, threadIndex, flags, childCount); } } else { - node = node.addNode(thread, flags | IModelDelta.CONTENT); + node = node.addNode(thread, threadIndex, flags | IModelDelta.CONTENT, childCount); } if (frame != null) { - node.addNode(frame, IModelDelta.STATE | IModelDelta.SELECT); + node.addNode(frame, indexOf(frame), IModelDelta.STATE | IModelDelta.SELECT, childCount(frame)); } synchronized (this) { if (!isDisposed()) { @@ -210,6 +238,54 @@ public class ThreadEventHandler extends DebugEventHandler { fireDelta(delta); } + /** + * Returns the index of the given thread, relative to its parent in the view. + * + * @param thread thread + * @return index of the thread, relative to its parent + */ + protected int indexOf(IThread thread) { + try { + return indexOf(thread.getDebugTarget().getThreads(), thread); + } catch (DebugException e) { + } + return -1; + } + + /** + * Returns the index of the given frame, relative to its parent in the view. + * + * @param frame frame + * @return index of the frame, relative to its thread + */ + protected int indexOf(IStackFrame frame) { + return 0; + } + + /** + * Returns the number of children the given thread has in the view. + * + * @param thread thread + * @return number of children + */ + protected int childCount(IThread thread) { + try { + return thread.getStackFrames().length; + } catch (DebugException e) { + } + return -1; + } + + /** + * Returns the number of children the given frame has in the view. + * + * @param frame frame + * @return child count + */ + protected int childCount(IStackFrame frame) { + return 0; + } + private void fireDeltaUpdatingThread(IThread thread, int flags) { ModelDelta delta = buildRootDelta(); ModelDelta node = addPathToThread(delta, thread); diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/expression/ExpressionView.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/expression/ExpressionView.java index ce977d9b1..733458279 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/expression/ExpressionView.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/expression/ExpressionView.java @@ -15,11 +15,9 @@ import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.model.IDebugElement; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; -import org.eclipse.debug.internal.ui.views.AbstractViewerState; import org.eclipse.debug.internal.ui.views.variables.AvailableLogicalStructuresAction; import org.eclipse.debug.internal.ui.views.variables.VariablesView; import org.eclipse.debug.internal.ui.views.variables.VariablesViewMessages; -import org.eclipse.debug.internal.ui.views.variables.VariablesViewer; import org.eclipse.debug.ui.IDebugUIConstants; import org.eclipse.jface.action.IAction; import org.eclipse.jface.action.IMenuManager; @@ -29,7 +27,6 @@ import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.swt.SWT; -import org.eclipse.swt.widgets.Composite; import org.eclipse.ui.IWorkbenchActionConstants; import org.eclipse.ui.IWorkbenchPart; @@ -39,8 +36,6 @@ import org.eclipse.ui.IWorkbenchPart; */ public class ExpressionView extends VariablesView { - private AbstractViewerState fState; - /** * @see AbstractDebugView#getHelpContextId() */ @@ -76,7 +71,8 @@ public class ExpressionView extends VariablesView { menu.add(new Separator(IDebugUIConstants.EMPTY_EXPRESSION_GROUP)); menu.add(new Separator(IDebugUIConstants.EXPRESSION_GROUP)); - menu.add(getAction(FIND_ELEMENT)); + // TODO: + //menu.add(getAction(FIND_ELEMENT)); menu.add(getAction("ChangeVariableValue")); //$NON-NLS-1$ IAction action = new AvailableLogicalStructuresAction(this); if (action.isEnabled()) { @@ -128,53 +124,18 @@ public class ExpressionView extends VariablesView { } /* (non-Javadoc) - * @see org.eclipse.debug.ui.AbstractDebugView#createActions() - */ - protected void createActions() { - super.createActions(); - setInitialContent(); - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#restoreState() - */ - protected void restoreState() { - if (fState != null) { - fState.restoreState(getVariablesViewer()); - } - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#dispose() - */ - public void dispose() { - super.dispose(); - fState = null; - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#becomesHidden() - */ - protected void becomesHidden() { - fState = getViewerState(); - super.becomesHidden(); - getViewer().setInput(null); - } - - /* (non-Javadoc) * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#becomesVisible() */ protected void becomesVisible() { - super.becomesVisible(); setInitialContent(); - restoreState(); + super.becomesVisible(); } /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#createVariablesViewer(org.eclipse.swt.widgets.Composite) + * @see org.eclipse.debug.internal.ui.views.variables.VariablesView#getViewerStyle() */ - protected VariablesViewer createVariablesViewer(Composite parent) { - return new VariablesViewer(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL, this); + protected int getViewerStyle() { + return SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL; } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DebugElementAdapterFactory.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DebugElementAdapterFactory.java index ecd5be667..c1a0c4e24 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DebugElementAdapterFactory.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/DebugElementAdapterFactory.java @@ -44,6 +44,21 @@ import org.eclipse.debug.internal.ui.elements.adapters.ThreadContentAdapter; import org.eclipse.debug.internal.ui.elements.adapters.VariableColumnFactoryAdapter; import org.eclipse.debug.internal.ui.elements.adapters.VariableContentAdapter; import org.eclipse.debug.internal.ui.elements.adapters.VariableLabelAdapter; +import org.eclipse.debug.internal.ui.model.IElementContentProvider; +import org.eclipse.debug.internal.ui.model.IElementLabelProvider; +import org.eclipse.debug.internal.ui.model.IElementMementoProvider; +import org.eclipse.debug.internal.ui.model.elements.DebugElementLabelProvider; +import org.eclipse.debug.internal.ui.model.elements.DebugTargetContentProvider; +import org.eclipse.debug.internal.ui.model.elements.ExpressionContentProvider; +import org.eclipse.debug.internal.ui.model.elements.ExpressionLabelProvider; +import org.eclipse.debug.internal.ui.model.elements.ExpressionManagerContentProvider; +import org.eclipse.debug.internal.ui.model.elements.LaunchContentProvider; +import org.eclipse.debug.internal.ui.model.elements.LaunchManagerContentProvider; +import org.eclipse.debug.internal.ui.model.elements.StackFrameContentProvider; +import org.eclipse.debug.internal.ui.model.elements.VariablesViewElementMementoProvider; +import org.eclipse.debug.internal.ui.model.elements.ThreadContentProvider; +import org.eclipse.debug.internal.ui.model.elements.VariableContentProvider; +import org.eclipse.debug.internal.ui.model.elements.VariableLabelProvider; import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousContentAdapter; import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousLabelAdapter; import org.eclipse.debug.internal.ui.viewers.provisional.IColumnEditorFactoryAdapter; @@ -72,6 +87,10 @@ public class DebugElementAdapterFactory implements IAdapterFactory { private static IAsynchronousLabelAdapter fgMemoryBlockLabelAdapter = new MemoryBlockLabelAdapter(); private static IAsynchronousLabelAdapter fgTableRenderingLineLabelAdapter = new MemorySegmentLabelAdapter(); + private static IElementLabelProvider fgLPDebugElement = new DebugElementLabelProvider(); + private static IElementLabelProvider fgLPVariable = new VariableLabelProvider(); + private static IElementLabelProvider fgLPExpression = new ExpressionLabelProvider(); + private static IAsynchronousContentAdapter fgAsyncLaunchManager = new LauchManagerContentAdapter(); private static IAsynchronousContentAdapter fgAsyncLaunch = new LaunchContentAdapter(); private static IAsynchronousContentAdapter fgAsyncTarget = new DebugTargetContentAdapter(); @@ -85,6 +104,17 @@ public class DebugElementAdapterFactory implements IAdapterFactory { private static IAsynchronousContentAdapter fgAsyncMemoryRetrieval = new MemoryRetrievalContentAdapter(); private static IAsynchronousContentAdapter fgAsyncMemoryBlock = new MemoryBlockContentAdapter(); + private static IElementContentProvider fgCPLaunchManger = new LaunchManagerContentProvider(); + private static IElementContentProvider fgCPLaunch = new LaunchContentProvider(); + private static IElementContentProvider fgCPTarget = new DebugTargetContentProvider(); + private static IElementContentProvider fgCPThread = new ThreadContentProvider(); + private static IElementContentProvider fgCPFrame = new StackFrameContentProvider(); + private static IElementContentProvider fgCPVariable = new VariableContentProvider(); + private static IElementContentProvider fgCPExpressionManager = new ExpressionManagerContentProvider(); + private static IElementContentProvider fgCPExpression = new ExpressionContentProvider(); + + private static IElementMementoProvider fgMPFrame = new VariablesViewElementMementoProvider(); + private static IColumnPresentationFactoryAdapter fgVariableColumnFactory = new VariableColumnFactoryAdapter(); @@ -135,6 +165,33 @@ public class DebugElementAdapterFactory implements IAdapterFactory { } } + if (adapterType.equals(IElementContentProvider.class)) { + if (adaptableObject instanceof ILaunchManager) { + return fgCPLaunchManger; + } + if (adaptableObject instanceof ILaunch) { + return fgCPLaunch; + } + if (adaptableObject instanceof IDebugTarget) { + return fgCPTarget; + } + if (adaptableObject instanceof IThread) { + return fgCPThread; + } + if (adaptableObject instanceof IStackFrame) { + return fgCPFrame; + } + if (adaptableObject instanceof IVariable) { + return fgCPVariable; + } + if (adaptableObject instanceof IExpressionManager) { + return fgCPExpressionManager; + } + if (adaptableObject instanceof IExpression) { + return fgCPExpression; + } + } + if (adapterType.equals(IAsynchronousLabelAdapter.class)) { if (adaptableObject instanceof IExpression) { return fgExpressionLabelAdapter; @@ -153,6 +210,16 @@ public class DebugElementAdapterFactory implements IAdapterFactory { return fgDebugLabelAdapter; } + if (adapterType.equals(IElementLabelProvider.class)) { + if (adaptableObject instanceof IVariable) { + return fgLPVariable; + } + if (adaptableObject instanceof IExpression) { + return fgLPExpression; + } + return fgLPDebugElement; + } + if (adapterType.equals(IModelProxyFactoryAdapter.class)) { if (adaptableObject instanceof IDebugTarget || adaptableObject instanceof IProcess || adaptableObject instanceof ILaunchManager || @@ -184,7 +251,13 @@ public class DebugElementAdapterFactory implements IAdapterFactory { if (adaptableObject instanceof IVariable) { return fgVariableColumnFactory; } - } + } + + if (adapterType.equals(IElementMementoProvider.class)) { + if (adaptableObject instanceof IStackFrame) { + return fgMPFrame; + } + } return null; } @@ -193,7 +266,8 @@ public class DebugElementAdapterFactory implements IAdapterFactory { */ public Class[] getAdapterList() { return new Class[] {IWorkbenchAdapter.class, IWorkbenchAdapter2.class, IDeferredWorkbenchAdapter.class, IAsynchronousLabelAdapter.class, IAsynchronousContentAdapter.class, - IModelProxyFactoryAdapter.class, ISourceDisplayAdapter.class, IModelSelectionPolicyFactoryAdapter.class, IColumnPresentationFactoryAdapter.class, IColumnEditorFactoryAdapter.class}; + IModelProxyFactoryAdapter.class, ISourceDisplayAdapter.class, IModelSelectionPolicyFactoryAdapter.class, IColumnPresentationFactoryAdapter.class, IColumnEditorFactoryAdapter.class, + IElementContentProvider.class, IElementLabelProvider.class, IElementMementoProvider.class}; } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java index 5755ebe7d..e235e1a9b 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java @@ -32,7 +32,6 @@ import org.eclipse.debug.internal.ui.DelegatingModelPresentation; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.actions.AddToFavoritesAction; import org.eclipse.debug.internal.ui.actions.EditLaunchConfigurationAction; -import org.eclipse.debug.internal.ui.actions.FindElementAction; import org.eclipse.debug.internal.ui.actions.context.AbstractDebugContextAction; import org.eclipse.debug.internal.ui.actions.context.DisconnectAction; import org.eclipse.debug.internal.ui.actions.context.DropToFrameAction; @@ -48,6 +47,7 @@ import org.eclipse.debug.internal.ui.actions.context.TerminateAndRemoveAction; import org.eclipse.debug.internal.ui.contexts.DebugContextManager; import org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextListener; import org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextProvider; +import org.eclipse.debug.internal.ui.model.viewers.TreeModelViewer; import org.eclipse.debug.internal.ui.sourcelookup.EditSourceLookupPathAction; import org.eclipse.debug.internal.ui.sourcelookup.LookupSourceAction; import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; @@ -244,7 +244,7 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi fAddToFavoritesAction = new AddToFavoritesAction(); fEditSourceAction = new EditSourceLookupPathAction(this); fLookupAction = new LookupSourceAction(this); - setAction(FIND_ACTION, new FindElementAction(this, (AsynchronousTreeViewer) getViewer())); + //setAction(FIND_ACTION, new FindElementAction(this, (AsynchronousTreeViewer) getViewer())); IWorkbenchWindow window = getSite().getWorkbenchWindow(); @@ -287,8 +287,9 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi * @see org.eclipse.debug.ui.AbstractDebugView#createViewer(org.eclipse.swt.widgets.Composite) */ protected Viewer createViewer(Composite parent) { - AsynchronousTreeViewer viewer = new LaunchViewer(parent, this); - viewer.setContext(new PresentationContext(this)); + TreeModelViewer viewer = new TreeModelViewer(parent, + SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.BORDER | SWT.VIRTUAL, + new PresentationContext(this)); viewer.addSelectionChangedListener(this); viewer.getControl().addKeyListener(new KeyAdapter() { diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/MemoryViewTreeViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/MemoryViewTreeViewer.java index 6001b2e59..a855596d1 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/MemoryViewTreeViewer.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/memory/MemoryViewTreeViewer.java @@ -97,7 +97,7 @@ public class MemoryViewTreeViewer extends AsynchronousTreeViewer { if ((flags & IModelDelta.REPLACED) != 0) { } - updateNodes(node.getNodes()); + updateNodes(node.getChildDeltas()); } } } diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/ToggleShowColumnsAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/ToggleShowColumnsAction.java index efae55c12..7e9a1046c 100644 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/ToggleShowColumnsAction.java +++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/ToggleShowColumnsAction.java @@ -13,7 +13,7 @@ package org.eclipse.debug.internal.ui.views.variables; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.IDebugHelpContextIds; import org.eclipse.debug.internal.ui.IInternalDebugUIConstants; -import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; +import org.eclipse.debug.internal.ui.model.viewers.TreeModelViewer; import org.eclipse.debug.ui.DebugUITools; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.IAction; @@ -29,9 +29,9 @@ import org.eclipse.ui.texteditor.IUpdate; */ public class ToggleShowColumnsAction extends Action implements IUpdate { - private AsynchronousTreeViewer fViewer; + private TreeModelViewer fViewer; - public ToggleShowColumnsAction(AsynchronousTreeViewer viewew) { + public ToggleShowColumnsAction(TreeModelViewer viewew) { super(VariablesViewMessages.ToggleShowColumnsAction_0, IAction.AS_CHECK_BOX); fViewer = viewew; setToolTipText(VariablesViewMessages.ToggleShowColumnsAction_1); 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 67dc42609..f266369bd 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 @@ -18,15 +18,11 @@ import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.util.ArrayList; -import java.util.HashMap; import java.util.Iterator; -import java.util.LinkedHashMap; import java.util.List; import java.util.ResourceBundle; import org.eclipse.core.commands.operations.IUndoContext; - -import org.eclipse.core.runtime.Assert; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; @@ -47,18 +43,21 @@ import org.eclipse.debug.internal.ui.LazyModelPresentation; import org.eclipse.debug.internal.ui.VariablesViewModelPresentation; import org.eclipse.debug.internal.ui.actions.CollapseAllAction; import org.eclipse.debug.internal.ui.actions.ConfigureColumnsAction; -import org.eclipse.debug.internal.ui.actions.FindElementAction; import org.eclipse.debug.internal.ui.actions.variables.AssignValueAction; import org.eclipse.debug.internal.ui.actions.variables.ChangeVariableValueAction; import org.eclipse.debug.internal.ui.actions.variables.ShowTypesAction; import org.eclipse.debug.internal.ui.actions.variables.ToggleDetailPaneAction; import org.eclipse.debug.internal.ui.contexts.DebugContextManager; import org.eclipse.debug.internal.ui.contexts.provisional.IDebugContextListener; +import org.eclipse.debug.internal.ui.model.viewers.IViewerUpdateListener; +import org.eclipse.debug.internal.ui.model.viewers.TreeModelViewer; import org.eclipse.debug.internal.ui.preferences.IDebugPreferenceConstants; -import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; import org.eclipse.debug.internal.ui.viewers.PresentationContext; +import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta; +import org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor; import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; -import org.eclipse.debug.internal.ui.views.AbstractViewerState; import org.eclipse.debug.internal.ui.views.IDebugExceptionHandler; import org.eclipse.debug.ui.AbstractDebugView; import org.eclipse.debug.ui.IDebugModelPresentation; @@ -100,6 +99,7 @@ import org.eclipse.jface.viewers.IStructuredSelection; import org.eclipse.jface.viewers.SelectionChangedEvent; import org.eclipse.jface.viewers.StructuredSelection; import org.eclipse.jface.viewers.StructuredViewer; +import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.viewers.Viewer; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; @@ -132,6 +132,7 @@ import org.eclipse.ui.handlers.IHandlerService; import org.eclipse.ui.operations.OperationHistoryActionHandler; import org.eclipse.ui.operations.RedoActionHandler; import org.eclipse.ui.operations.UndoActionHandler; +import org.eclipse.ui.progress.UIJob; import org.eclipse.ui.progress.WorkbenchJob; import org.eclipse.ui.texteditor.FindReplaceAction; import org.eclipse.ui.texteditor.IAbstractTextEditorHelpContextIds; @@ -146,9 +147,9 @@ import com.ibm.icu.text.MessageFormat; * This view shows variables and their values for a particular stack frame */ public class VariablesView extends AbstractDebugView implements IDebugContextListener, - IPropertyChangeListener, - IDebugExceptionHandler, - IPerspectiveListener { + IPropertyChangeListener, IDebugExceptionHandler, + IPerspectiveListener, IModelChangedListener, + IViewerUpdateListener { /** * Internal interface for a cursor listener. I.e. aggregation @@ -157,44 +158,6 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis */ interface ICursorListener extends MouseListener, KeyListener { } - - /** - * Most recently used variant with capped size that only counts - * {@linkplain #put(Object, Object) put} as access. This is implemented by always removing an - * element before it gets put back. - * - * @since 3.2 - */ - private static final class MRUMap extends LinkedHashMap { - private static final long serialVersionUID= 1L; - private final int fMaxSize; - - /** - * Creates a new <code>MRUMap</code> with the given size. - * - * @param maxSize the maximum size of the cache, must be > 0 - */ - public MRUMap(int maxSize) { - Assert.isLegal(maxSize > 0); - fMaxSize= maxSize; - } - - /* - * @see java.util.HashMap#put(java.lang.Object, java.lang.Object) - */ - public Object put(Object key, Object value) { - Object object= remove(key); - super.put(key, value); - return object; - } - - /* - * @see java.util.LinkedHashMap#removeEldestEntry(java.util.Map.Entry) - */ - protected boolean removeEldestEntry(java.util.Map.Entry eldest) { - return size() > fMaxSize; - } - } /** * The selection provider for the variables view changes depending on whether @@ -263,14 +226,22 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis } } public void run() { - getFindAction().run(); + IAction findAction = getFindAction(); + if (findAction != null) { + findAction.run(); + } } /* (non-Javadoc) * @see org.eclipse.ui.texteditor.IUpdate#update() */ public void update() { - setEnabled(getFindAction().isEnabled()); + IAction findAction = getFindAction(); + if (findAction != null) { + setEnabled(findAction.isEnabled()); + } else { + setEnabled(false); + } } } @@ -426,21 +397,6 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis private List fSelectionActions = new ArrayList(3); /** - * An MRU cache of stack frame hash codes to <code>ViewerState</code>s. - * Used to restore the expanded state of the variables view on - * re-selection of the same stack frame. The cache is limited - * to twenty entries. - */ - private HashMap fSelectionStates = new MRUMap(20); - - /** - * The last known viewer state. Used to initialize the expansion/selection - * in the variables view when there is no state to go on for the - * current stack frame being displayed. - */ - private AbstractViewerState fLastState = null; - - /** * Remembers which viewer (tree viewer or details viewer) had focus, so we * can reset the focus properly when re-activated. */ @@ -506,6 +462,54 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis /** Whether logical structures are showing */ private boolean fShowLogical; + + /** + * Visits deltas to determine if details should be displayed + */ + class Visitor implements IModelDeltaVisitor { + /** + * Whether to trigger details display. + * + * @since 3.3 + */ + private boolean fTriggerDetails = false; + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelDeltaVisitor#visit(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta, int) + */ + public boolean visit(IModelDelta delta, int depth) { + if ((delta.getFlags() & IModelDelta.CONTENT) > 0) { + fTriggerDetails = true; + return false; + } + return true; + } + + public void reset() { + fTriggerDetails = false; + } + + public boolean isTriggerDetails() { + return fTriggerDetails; + } + + } + /** + * Delta visitor + */ + private Visitor fVisitor = new Visitor(); + + /** + * Job to update details in the UI thread. + */ + private Job fTriggerDetailsJob = new UIJob("trigger details") { //$NON-NLS-1$ + + public IStatus runInUIThread(IProgressMonitor monitor) { + populateDetailPane(); + return Status.OK_STATUS; + } + + }; + /** * Remove myself as a selection listener * and preference change listener. @@ -518,15 +522,12 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis getSite().getWorkbenchWindow().removePerspectiveListener(this); DebugUIPlugin.getDefault().getPreferenceStore().removePropertyChangeListener(this); JFaceResources.getFontRegistry().removeListener(this); - Viewer viewer = getViewer(); + TreeModelViewer viewer = getVariablesViewer(); if (viewer != null) { getDetailDocument().removeDocumentListener(getDetailDocumentListener()); - if (viewer instanceof AsynchronousTreeViewer) { - AsynchronousTreeViewer asyncTreeViewer = (AsynchronousTreeViewer) viewer; - asyncTreeViewer.dispose(); - } + viewer.removeModelChangedListener(this); + viewer.removeViewerUpdateListener(this); } - fSelectionStates.clear(); super.dispose(); } @@ -549,79 +550,15 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis if (current != null && current.equals(context)) { return; - } - - if (current != null) { - // save state - AbstractViewerState state = getViewerState(); - cacheViewerState(current, state); - fLastState = (AbstractViewerState) state.clone(); - } + } if (context instanceof IDebugElement) { setDebugModel(((IDebugElement)context).getModelIdentifier()); } showViewer(); getViewer().setInput(context); - restoreState(); - } - - /** - * Caches the given viewer state for the given viewer input. - * - * @param input viewer input - * @param state viewer state - */ - protected void cacheViewerState(Object input, AbstractViewerState state) { - // generate a key for the input based on its hash code, we don't - // want to maintain reference real model objects preventing GCs. - fSelectionStates.put(generateKey(input), state); - } - - /** - * Generate a key for an input object. - * - * @param input - * @return key - */ - protected Object generateKey(Object input) { - return new Integer(input.hashCode()); - } - - /** - * Returns the cached viewer state for the given viewer input or - * <code>null</code> if none. - * - * @param input viewer input - * @return viewer state or <code>null</code> - */ - protected AbstractViewerState getCachedViewerState(Object input) { - return (AbstractViewerState) fSelectionStates.get(generateKey(input)); } - - /** - * Restores the state of the viewer - */ - protected void restoreState() { - VariablesViewer viewer = (VariablesViewer) getViewer(); - if (viewer != null) { - Object context = viewer.getInput(); - if (context != null) { - AbstractViewerState state = getCachedViewerState(context); - if (state == null) { - // attempt to restore selection/expansion based on last - // frame - if (fLastState != null) { - state = fLastState; - } - } - if (state != null) { - state.restoreState(viewer); - } - } - } - } - + /** * Configures the details viewer for the debug model * currently being displayed @@ -671,8 +608,7 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis * @see org.eclipse.debug.ui.AbstractDebugView#createViewer(Composite) */ public Viewer createViewer(Composite parent) { - VariablesViewer variablesViewer = (VariablesViewer) createTreeViewer(parent); - variablesViewer.setContext(new PresentationContext(this)); + TreeModelViewer variablesViewer = createTreeViewer(parent); variablesViewer.getPresentationContext().addPropertyChangeListener( new IPropertyChangeListener() { public void propertyChange(PropertyChangeEvent event) { @@ -699,6 +635,8 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis if (memento != null) { variablesViewer.initState(memento); } + variablesViewer.addModelChangedListener(this); + variablesViewer.addViewerUpdateListener(this); return variablesViewer; } @@ -796,7 +734,7 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis /** * Create and return the main tree viewer that displays variable. */ - protected Viewer createTreeViewer(Composite parent) { + protected TreeModelViewer createTreeViewer(Composite parent) { fModelPresentation = new VariablesViewModelPresentation(); DebugUIPlugin.getDefault().getPreferenceStore().addPropertyChangeListener(this); JFaceResources.getFontRegistry().addListener(this); @@ -805,7 +743,8 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis fSashForm = new SashForm(parent, SWT.NONE); // add tree viewer - final VariablesViewer variablesViewer = createVariablesViewer(fSashForm); + int style = getViewerStyle(); + final TreeModelViewer variablesViewer = new TreeModelViewer(fSashForm, style, new PresentationContext(this)); variablesViewer.getControl().addFocusListener(new FocusAdapter() { /* (non-Javadoc) * @see org.eclipse.swt.events.FocusListener#focusGained(FocusEvent) @@ -828,13 +767,12 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis } /** - * Creates and returns a variables viewer in the given composite. + * Returns the style bits for the viewer. * - * @param parent - * @return variables viewer + * @return SWT style */ - protected VariablesViewer createVariablesViewer(Composite parent) { - return new VariablesViewer(parent, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION, this); + protected int getViewerStyle() { + return SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL | SWT.VIRTUAL | SWT.FULL_SELECTION; } /** @@ -1006,7 +944,7 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis action = new ToggleLogicalStructureAction(this); setAction("ToggleContentProviders", action); //$NON-NLS-1$ - action = new CollapseAllAction((AsynchronousTreeViewer)getViewer()); + action = new CollapseAllAction((TreeViewer)getViewer()); setAction("CollapseAll", action); //$NON-NLS-1$ action = new ChangeVariableValueAction(this); @@ -1045,8 +983,9 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis textAction.setActionDefinitionId(IWorkbenchActionDefinitionIds.PASTE); setAction(ActionFactory.PASTE.getId(), textAction); - action= new FindElementAction(this, getVariablesViewer()); - setAction(FIND_ELEMENT, action); + //TODO: + //action= new FindElementAction(this, getVariablesViewer()); + //setAction(FIND_ELEMENT, action); // TODO: Still using "old" resource access ResourceBundle bundle= ResourceBundle.getBundle("org.eclipse.debug.internal.ui.views.variables.VariablesViewResourceBundleMessages"); //$NON-NLS-1$ @@ -1119,7 +1058,7 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis return null; } - private void createOrientationActions(VariablesViewer viewer) { + private void createOrientationActions(TreeModelViewer viewer) { IActionBars actionBars = getViewSite().getActionBars(); IMenuManager viewMenu = actionBars.getMenuManager(); @@ -1184,7 +1123,8 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis menu.add(new Separator(IDebugUIConstants.EMPTY_VARIABLE_GROUP)); menu.add(new Separator(IDebugUIConstants.VARIABLE_GROUP)); - menu.add(getAction(FIND_ELEMENT)); + // TODO: + //menu.add(getAction(FIND_ELEMENT)); menu.add(getAction("ChangeVariableValue")); //$NON-NLS-1$ IAction action = new AvailableLogicalStructuresAction(this); if (action.isEnabled()) { @@ -1270,9 +1210,12 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis if (fDetailsJob != null) { fDetailsJob.cancel(); } - fDetailsJob = new DetailJob(selection); - setDetails(""); //$NON-NLS-1$ - fDetailsJob.schedule(); + if (selection.isEmpty()) { + setDetails(""); //$NON-NLS-1$ + } else { + fDetailsJob = new DetailJob(selection); + fDetailsJob.schedule(); + } } } @@ -1595,18 +1538,9 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis ISelection selection = DebugContextManager.getDefault().getActiveContext(getSite().getWorkbenchWindow()); contextActivated(selection, null); } - - /** - * Returns the memento of the expanded and selected items in the viewer. - * - * @return the memento of the expanded and selected items in the viewer - */ - protected AbstractViewerState getViewerState() { - return new ViewerState(getVariablesViewer()); - } - protected VariablesViewer getVariablesViewer() { - return (VariablesViewer) getViewer(); + protected TreeModelViewer getVariablesViewer() { + return (TreeModelViewer) getViewer(); } /** @@ -1697,5 +1631,40 @@ public class VariablesView extends AbstractDebugView implements IDebugContextLis setLastSashWeights(DEFAULT_SASH_WEIGHTS); fSashForm.setWeights(DEFAULT_SASH_WEIGHTS); } + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta) + */ + public void modelChanged(IModelDelta delta) { + fVisitor.reset(); + delta.accept(fVisitor); + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IViewerUpdateListener#updateComplete(org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor) + */ + public void updateComplete(IAsynchronousRequestMonitor update) { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IViewerUpdateListener#updateStarted(org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor) + */ + public void updateStarted(IAsynchronousRequestMonitor update) { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IViewerUpdateListener#viewerUpdatesBegin() + */ + public void viewerUpdatesBegin() { + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.viewers.IViewerUpdateListener#viewerUpdatesComplete() + */ + public void viewerUpdatesComplete() { + if (fVisitor.isTriggerDetails()) { + fTriggerDetailsJob.schedule(); + } } } 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 deleted file mode 100644 index 2a192de87..000000000 --- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/variables/VariablesViewer.java +++ /dev/null @@ -1,56 +0,0 @@ -/******************************************************************************* - * Copyright (c) 2005, 2006 IBM Corporation and others. - * All rights reserved. This program and the accompanying materials - * are made available under the terms of the Eclipse Public License v1.0 - * which accompanies this distribution, and is available at - * http://www.eclipse.org/legal/epl-v10.html - * - * Contributors: - * IBM Corporation - initial API and implementation - *******************************************************************************/ -package org.eclipse.debug.internal.ui.views.variables; - -import org.eclipse.core.runtime.IProgressMonitor; -import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.Status; -import org.eclipse.debug.internal.ui.viewers.AsynchronousTreeViewer; -import org.eclipse.debug.internal.ui.viewers.provisional.IAsynchronousRequestMonitor; -import org.eclipse.swt.widgets.Composite; -import org.eclipse.ui.progress.UIJob; - -/** - * @since 3.2 - * - */ -public class VariablesViewer extends AsynchronousTreeViewer{ - - private VariablesView fView; - - private UIJob fRestoreJob = new UIJob("restore viewer state") { //$NON-NLS-1$ - public IStatus runInUIThread(IProgressMonitor monitor) { - fView.restoreState(); - return Status.OK_STATUS; - } - }; - - public VariablesViewer(Composite parent, int style, VariablesView view) { - super(parent, style); - fView = view; - fRestoreJob.setSystem(true); - } - - protected void updateComplete(IAsynchronousRequestMonitor update) { - if (fView != null && !hasPendingUpdates()) { - fRestoreJob.schedule(); - fView.populateDetailPane(); - } - } - - /* (non-Javadoc) - * @see org.eclipse.debug.internal.ui.treeviewer.AsynchronousTreeViewer#handlePresentationFailure(org.eclipse.debug.internal.ui.treeviewer.IPresentationRequestMonitor, org.eclipse.core.runtime.IStatus) - */ - protected void handlePresentationFailure(IAsynchronousRequestMonitor update, IStatus status) { - fView.showMessage(status.getMessage()); - } - -} 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 308b5b90f..2a7b2944b 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 @@ -11,9 +11,7 @@ package org.eclipse.debug.ui; -import java.util.Iterator; import java.util.List; -import java.util.Map; import org.eclipse.core.runtime.CoreException; import org.eclipse.debug.core.DebugException; @@ -23,16 +21,15 @@ import org.eclipse.debug.core.model.IValue; import org.eclipse.debug.core.model.IVariable; import org.eclipse.debug.internal.ui.DebugUIPlugin; import org.eclipse.debug.internal.ui.VariablesViewModelPresentation; +import org.eclipse.debug.internal.ui.model.elements.ElementContentProvider; +import org.eclipse.debug.internal.ui.model.viewers.TreeModelViewer; import org.eclipse.debug.internal.ui.viewers.PresentationContext; -import org.eclipse.debug.internal.ui.viewers.provisional.AsynchronousContentAdapter; import org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext; import org.eclipse.debug.internal.ui.views.DebugUIViewsMessages; import org.eclipse.debug.internal.ui.views.variables.IndexedVariablePartition; import org.eclipse.debug.internal.ui.views.variables.VariablesView; -import org.eclipse.debug.internal.ui.views.variables.VariablesViewer; import org.eclipse.jface.viewers.StructuredViewer; import org.eclipse.jface.viewers.TreePath; -import org.eclipse.jface.viewers.TreeSelection; import org.eclipse.jface.viewers.ViewerFilter; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; @@ -72,7 +69,7 @@ public class InspectPopupDialog extends DebugPopup { private static final int MIN_HEIGHT = 200; - private VariablesViewer fVariablesViewer; + private TreeModelViewer fViewer; private IDebugModelPresentation fModelPresentation; @@ -112,23 +109,18 @@ public class InspectPopupDialog extends DebugPopup { fSashForm.setLayoutData(new GridData(GridData.FILL_BOTH)); VariablesView view = getViewToEmulate(); - - fVariablesViewer = new VariablesViewer(fSashForm, SWT.NO_TRIM | SWT.VIRTUAL, null); - IPresentationContext context; if (view == null) { context = new PresentationContext(IDebugUIConstants.ID_VARIABLE_VIEW); } else { - context = new PresentationContext(view); + context = ((TreeModelViewer)view.getViewer()).getPresentationContext(); } - fVariablesViewer.setContext(context); + fViewer = new TreeModelViewer(fSashForm, SWT.NO_TRIM | SWT.VIRTUAL, context); fModelPresentation = new VariablesViewModelPresentation(); - fVariablesViewer.setLabelProvider(fModelPresentation); - fValueDisplay = new StyledText(fSashForm, SWT.NO_TRIM | SWT.WRAP | SWT.V_SCROLL); fValueDisplay.setEditable(false); - fTree = fVariablesViewer.getTree(); + fTree = fViewer.getTree(); fTree.addSelectionListener(new SelectionListener() { public void widgetSelected(SelectionEvent e) { try { @@ -165,41 +157,44 @@ public class InspectPopupDialog extends DebugPopup { // sashForm.setWeights(getInitialSashWeights()); fSashForm.setWeights(DEFAULT_SASH_WEIGHTS); - fVariablesViewer.getContentProvider(); + fViewer.getContentProvider(); if (view != null) { StructuredViewer structuredViewer = (StructuredViewer) view.getViewer(); if (structuredViewer != null) { ViewerFilter[] filters = structuredViewer.getFilters(); for (int i = 0; i < filters.length; i++) { - fVariablesViewer.addFilter(filters[i]); + fViewer.addFilter(filters[i]); } } - - Map map = view.getPresentationAttributes(fExpression.getModelIdentifier()); - Iterator iterator = map.keySet().iterator(); - while (iterator.hasNext()) { - String key = (String) iterator.next(); - fModelPresentation.setAttribute(key, map.get(key)); - } } TreeRoot treeRoot = new TreeRoot(); - fVariablesViewer.setInput(treeRoot); - fVariablesViewer.expand(new TreeSelection(new TreePath(new Object[] {treeRoot, fExpression}))); + fViewer.setInput(treeRoot); + fViewer.expandToLevel(new TreePath(new Object[] {fExpression}), 1); return fTree; } - private class TreeRoot extends AsynchronousContentAdapter { - protected Object[] getChildren(Object parent, IPresentationContext context) throws CoreException { - return new Object[] { fExpression }; - } - protected boolean hasChildren(Object element, IPresentationContext context) throws CoreException { - return true; - } - protected boolean supportsPartId(String id) { - return true; - } + private class TreeRoot extends ElementContentProvider { + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildCount(java.lang.Object, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected int getChildCount(Object element, IPresentationContext context) throws CoreException { + return 1; + } + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#getChildren(java.lang.Object, int, int, org.eclipse.debug.internal.ui.viewers.provisional.IPresentationContext) + */ + protected Object[] getChildren(Object parent, int index, int length, IPresentationContext context) throws CoreException { + return new Object[] { fExpression }; + } + + /* (non-Javadoc) + * @see org.eclipse.debug.internal.ui.model.elements.ElementContentProvider#supportsContextId(java.lang.String) + */ + protected boolean supportsContextId(String id) { + return true; + } } private void updateValueDisplay(IValue val) { @@ -242,9 +237,6 @@ public class InspectPopupDialog extends DebugPopup { * @see org.eclipse.debug.ui.DebugPopup#close() */ public boolean close() { - if(fVariablesViewer != null) { - fVariablesViewer.dispose(); - } if(fModelPresentation != null) { fModelPresentation.dispose(); } |