Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPawel Piech2009-01-23 18:29:07 -0500
committerPawel Piech2009-01-23 18:29:07 -0500
commit56f6a790b2e35bdbcdfdb40a8d91eaececae74c1 (patch)
tree67ea4f7360c2264f3026bcda0bc5b1ca6b07f7f0
parent8802b5d2fb4d5fcbc50daa5bc206fc5148a66392 (diff)
downloadeclipse.platform.debug-56f6a790b2e35bdbcdfdb40a8d91eaececae74c1.tar.gz
eclipse.platform.debug-56f6a790b2e35bdbcdfdb40a8d91eaececae74c1.tar.xz
eclipse.platform.debug-56f6a790b2e35bdbcdfdb40a8d91eaececae74c1.zip
Bug 252677 - Add a Debug view breadcrumb to show and select the active debug context.
-rw-r--r--org.eclipse.debug.ui/.options2
-rw-r--r--org.eclipse.debug.ui/META-INF/MANIFEST.MF4
-rw-r--r--org.eclipse.debug.ui/plugin.xml2
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/AbstractBreadcrumb.java339
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItem.java226
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDetails.java425
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java561
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.java35
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.properties13
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbViewer.java817
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/IBreadcrumbDropDownSite.java40
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/TreeViewerDropDown.java300
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java8
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java15
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java10
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java232
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java54
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java177
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java388
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java1373
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java112
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java168
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java261
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java33
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java37
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java5
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java387
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java209
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IPresentationContext.java3
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java29
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java174
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultSelectionPolicy.java18
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchView.java172
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java406
-rw-r--r--org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java53
-rwxr-xr-xorg.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.java25
-rwxr-xr-xorg.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.properties13
37 files changed, 6682 insertions, 444 deletions
diff --git a/org.eclipse.debug.ui/.options b/org.eclipse.debug.ui/.options
index 2bf4af3b5..03fe0ac8c 100644
--- a/org.eclipse.debug.ui/.options
+++ b/org.eclipse.debug.ui/.options
@@ -7,3 +7,5 @@ org.eclipse.debug.ui/debug/viewers/updateSequence = false
org.eclipse.debug.ui/debug/contextlaunching = false
org.eclipse.debug.ui/debug/launchhistory = false
org.eclipse.debug.ui/debug/viewers/stateSaveRestore = false
+org.eclipse.debug.ui/debug/viewers/presentationId =
+org.eclipse.debug.ui/debug/breadcrumb = false
diff --git a/org.eclipse.debug.ui/META-INF/MANIFEST.MF b/org.eclipse.debug.ui/META-INF/MANIFEST.MF
index 51924f8d1..94252cc02 100644
--- a/org.eclipse.debug.ui/META-INF/MANIFEST.MF
+++ b/org.eclipse.debug.ui/META-INF/MANIFEST.MF
@@ -28,6 +28,7 @@ Export-Package: org.eclipse.debug.internal.ui;x-internal:=true,
org.eclipse.debug.internal.ui.sourcelookup.browsers;x-internal:=true,
org.eclipse.debug.internal.ui.stringsubstitution;x-internal:=true,
org.eclipse.debug.internal.ui.viewers;x-internal:=true,
+ org.eclipse.debug.internal.ui.viewers.breadcrumb;x-internal:=true,
org.eclipse.debug.internal.ui.viewers.model;x-internal:=true,
org.eclipse.debug.internal.ui.viewers.model.provisional;x-friends:="org.eclipse.debug.examples.ui,org.eclipse.jdt.debug.ui",
org.eclipse.debug.internal.ui.viewers.provisional;x-internal:=true,
@@ -60,7 +61,8 @@ Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.4.0,4.0.0)",
org.eclipse.ui.ide;bundle-version="[3.3.0,4.0.0)",
org.eclipse.ui.editors;bundle-version="[3.3.0,4.0.0)",
org.eclipse.core.runtime;bundle-version="[3.3.0,4.0.0)",
- org.eclipse.core.filesystem;bundle-version="[1.1.0,2.0.0)"
+ org.eclipse.core.filesystem;bundle-version="[1.1.0,2.0.0)",
+ org.eclipse.ui.forms;bundle-version="3.3.100"
Bundle-ActivationPolicy: lazy
Bundle-ClassPath: .
Import-Package: com.ibm.icu.text
diff --git a/org.eclipse.debug.ui/plugin.xml b/org.eclipse.debug.ui/plugin.xml
index 4480d4dfb..37425b58b 100644
--- a/org.eclipse.debug.ui/plugin.xml
+++ b/org.eclipse.debug.ui/plugin.xml
@@ -1236,7 +1236,7 @@
icon="$nl$/icons/full/elcl16/copy_edit_co.gif"
definitionId="org.eclipse.ui.edit.copy"
helpContextId="copy_to_clipboard_action_context"
- class="org.eclipse.debug.internal.ui.viewers.model.VirtualCopyToClipboardActionDelegate"
+ class="org.eclipse.debug.internal.ui.views.launch.LaunchViewCopyToClipboardActionDelegate"
menubarPath="editGroup"
id="org.eclipse.debug.ui.debugview.popupMenu.copyToClipboard">
</action>
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/AbstractBreadcrumb.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/AbstractBreadcrumb.java
new file mode 100644
index 000000000..2ba1d890e
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/AbstractBreadcrumb.java
@@ -0,0 +1,339 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.jface.resource.JFaceResources;
+import org.eclipse.jface.util.IPropertyChangeListener;
+import org.eclipse.jface.util.PropertyChangeEvent;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IDoubleClickListener;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+
+
+/**
+ * Breadcrumb base class. It creates the breadcrumb viewer and manages
+ * its activation.
+ * <p>
+ * Clients must implement the abstract methods.
+ * </p>
+ *
+ * @since 3.5
+ */
+public abstract class AbstractBreadcrumb {
+
+ private static final String ACTIVE_TAB_BG_END= "org.eclipse.ui.workbench.ACTIVE_TAB_BG_END"; //$NON-NLS-1$
+
+ private BreadcrumbViewer fBreadcrumbViewer;
+
+ private boolean fHasFocus;
+
+ private Composite fComposite;
+
+ private Listener fDisplayFocusListener;
+ private Listener fDisplayKeyListener;
+
+ private IPropertyChangeListener fPropertyChangeListener;
+
+ public AbstractBreadcrumb() {
+ }
+
+ /**
+ * The active element of the editor.
+ *
+ * @return the active element of the editor, or <b>null</b> if none
+ */
+ protected abstract Object getCurrentInput();
+
+ /**
+ * Create and configure the viewer used to display the parent chain.
+ *
+ * @param parent the parent composite
+ * @return the viewer
+ */
+ protected abstract BreadcrumbViewer createViewer(Composite parent);
+
+ /**
+ * Open the element in a new editor if possible.
+ *
+ * @param selection element the element to open
+ * @return true if the element could be opened
+ */
+ protected abstract boolean open(ISelection selection);
+
+ /**
+ * The breadcrumb has been activated. Implementors must retarget the editor actions to the
+ * breadcrumb aware actions.
+ */
+ protected abstract void activateBreadcrumb();
+
+ /**
+ * The breadcrumb has been deactivated. Implementors must retarget the breadcrumb actions to the
+ * editor actions.
+ */
+ protected abstract void deactivateBreadcrumb();
+
+ /**
+ * Returns the selection provider for this breadcrumb.
+ *
+ * @return the selection provider for this breadcrumb
+ */
+ public ISelectionProvider getSelectionProvider() {
+ return fBreadcrumbViewer;
+ }
+
+ /**
+ * Set the input of the breadcrumb to the given element
+ *
+ * @param element the input element can be <code>null</code>
+ */
+ public void setInput(Object element) {
+ if (element == null || fBreadcrumbViewer == null || fBreadcrumbViewer.getControl().isDisposed())
+ return;
+
+ Object input= fBreadcrumbViewer.getInput();
+ if (input == element || element.equals(input))
+ return;
+
+ fBreadcrumbViewer.setInput(element);
+ }
+
+ protected void refresh() {
+ if (!fBreadcrumbViewer.getControl().isDisposed()) {
+ fBreadcrumbViewer.refresh();
+ }
+ }
+
+ /**
+ * Activates the breadcrumb. This sets the keyboard focus
+ * inside this breadcrumb and retargets the editor
+ * actions.
+ */
+ public void activate() {
+ if (fBreadcrumbViewer.getSelection().isEmpty())
+ fBreadcrumbViewer.setSelection(new StructuredSelection(fBreadcrumbViewer.getInput()));
+ fBreadcrumbViewer.setFocus();
+ }
+
+ /**
+ * A breadcrumb is active if it either has the focus or another workbench part has the focus and
+ * the breadcrumb had the focus before the other workbench part was made active.
+ *
+ * @return <code>true</code> if this breadcrumb is active
+ */
+ public boolean isActive() {
+ return true;
+ }
+
+ /**
+ * Create breadcrumb content.
+ *
+ * @param parent the parent of the content
+ * @return the control containing the created content
+ */
+ public Control createContent(Composite parent) {
+ Assert.isTrue(fComposite == null, "Content must only be created once."); //$NON-NLS-1$
+
+ boolean rtl= (parent.getShell().getStyle() & SWT.RIGHT_TO_LEFT) != 0;
+ //boolean rtl = true;
+
+ fComposite= new Composite(parent, rtl ? SWT.RIGHT_TO_LEFT : SWT.NONE);
+ GridData data= new GridData(SWT.FILL, SWT.TOP, true, false);
+ fComposite.setLayoutData(data);
+ GridLayout gridLayout= new GridLayout(1, false);
+ gridLayout.marginWidth= 0;
+ gridLayout.marginHeight= 0;
+ gridLayout.verticalSpacing= 0;
+ gridLayout.horizontalSpacing= 0;
+ fComposite.setLayout(gridLayout);
+
+ fDisplayFocusListener= new Listener() {
+ public void handleEvent(Event event) {
+ if (fComposite.isDisposed()) return;
+
+ if (isBreadcrumbEvent(event)) {
+ if (fHasFocus)
+ return;
+
+ focusGained();
+ } else {
+ if (!fHasFocus)
+ return;
+
+ focusLost();
+ }
+ }
+ };
+ Display.getCurrent().addFilter(SWT.FocusIn, fDisplayFocusListener);
+
+ fBreadcrumbViewer= createViewer(fComposite);
+
+ fBreadcrumbViewer.addDoubleClickListener(new IDoubleClickListener() {
+ public void doubleClick(DoubleClickEvent event) {
+ Object element= ((IStructuredSelection) event.getSelection()).getFirstElement();
+ if (element == null)
+ return;
+
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbViewer.doFindItem(element);
+ if (item == null)
+ return;
+ item.openDropDownMenu();
+ }
+ });
+
+ fBreadcrumbViewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ doOpen(event.getSelection());
+ }
+ });
+
+ fPropertyChangeListener= new IPropertyChangeListener() {
+ public void propertyChange(PropertyChangeEvent event) {
+ if (ACTIVE_TAB_BG_END.equals(event.getProperty())) {
+ if (fComposite.isFocusControl()) {
+ fComposite.setBackground(JFaceResources.getColorRegistry().get(ACTIVE_TAB_BG_END));
+ }
+ }
+ }
+ };
+ JFaceResources.getColorRegistry().addListener(fPropertyChangeListener);
+
+ return fComposite;
+ }
+
+ /**
+ * Dispose all resources hold by this breadcrumb.
+ */
+ public void dispose() {
+ if (fPropertyChangeListener != null) {
+ JFaceResources.getColorRegistry().removeListener(fPropertyChangeListener);
+ }
+ if (fDisplayFocusListener != null) {
+ Display.getDefault().removeFilter(SWT.FocusIn, fDisplayFocusListener);
+ }
+ deinstallDisplayListeners();
+ }
+
+ /**
+ * Either reveal the selection in the editor or open the selection in a new editor. If both fail
+ * open the child pop up of the selected element.
+ *
+ * @param selection the selection to open
+ */
+ private void doOpen(ISelection selection) {
+ if (open(selection)) {
+ fBreadcrumbViewer.setInput(getCurrentInput());
+ }
+ }
+
+ /**
+ * Focus has been transfered into the breadcrumb.
+ */
+ private void focusGained() {
+ if (fHasFocus)
+ focusLost();
+
+ fComposite.setBackground(JFaceResources.getColorRegistry().get(ACTIVE_TAB_BG_END));
+ fHasFocus= true;
+
+ installDisplayListeners();
+
+ activateBreadcrumb();
+ }
+
+ /**
+ * Focus has been revoked from the breadcrumb.
+ */
+ private void focusLost() {
+ fComposite.setBackground(null);
+ fHasFocus= false;
+
+ deinstallDisplayListeners();
+
+ deactivateBreadcrumb();
+ }
+
+ /**
+ * Installs all display listeners.
+ */
+ private void installDisplayListeners() {
+ //Sanity check
+ deinstallDisplayListeners();
+
+ fDisplayKeyListener= new Listener() {
+ public void handleEvent(Event event) {
+ if (event.keyCode != SWT.ESC)
+ return;
+
+ if (!isBreadcrumbEvent(event))
+ return;
+ }
+ };
+ Display.getDefault().addFilter(SWT.KeyDown, fDisplayKeyListener);
+ }
+
+ /**
+ * Removes all previously installed display listeners.
+ */
+ private void deinstallDisplayListeners() {
+ if (fDisplayKeyListener != null) {
+ Display.getDefault().removeFilter(SWT.KeyDown, fDisplayKeyListener);
+ fDisplayKeyListener= null;
+ }
+ }
+
+ /**
+ * Tells whether the given event was issued inside the breadcrumb viewer's control.
+ *
+ * @param event the event to inspect
+ * @return <code>true</code> if event was generated by a breadcrumb child
+ */
+ private boolean isBreadcrumbEvent(Event event) {
+ if (fBreadcrumbViewer == null)
+ return false;
+
+ Widget item= event.widget;
+ if (!(item instanceof Control))
+ return false;
+
+ Shell dropDownShell= fBreadcrumbViewer.getDropDownShell();
+ if (dropDownShell != null && isChild((Control) item, dropDownShell))
+ return true;
+
+ return isChild((Control) item, fBreadcrumbViewer.getControl());
+ }
+
+ private boolean isChild(Control child, Control parent) {
+ if (child == null)
+ return false;
+
+ if (child == parent)
+ return true;
+
+ return isChild(child.getParent(), parent);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItem.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItem.java
new file mode 100644
index 000000000..d70e7aa92
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItem.java
@@ -0,0 +1,226 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * An item in a breadcrumb viewer.
+ * <p>
+ * The item shows a label and an image. It also has the ability to expand, that is to open a drop
+ * down menu.
+ * </p>
+ * <p>
+ * The drop down allows to select any child of the items input element. The item shows the label and
+ * icon of its data element, if any.
+ * </p>
+ *
+ * @since 3.5
+ */
+class BreadcrumbItem extends Item {
+
+ private TreePath fPath;
+
+ private final BreadcrumbViewer fParent;
+ private Composite fContainer;
+
+ private BreadcrumbItemDropDown fExpandBlock;
+ private BreadcrumbItemDetails fDetailsBlock;
+
+ private boolean fIsLast;
+
+ /**
+ * A new breadcrumb item which is shown inside the given viewer.
+ *
+ * @param viewer the items viewer
+ * @param parent the container containing the item
+ */
+ public BreadcrumbItem(BreadcrumbViewer viewer, Composite parent) {
+ super(parent, SWT.NONE);
+
+ fParent= viewer;
+
+ fContainer= new Composite(parent, SWT.NONE);
+ fContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, false, false));
+ GridLayout layout= new GridLayout(2, false);
+ layout.marginBottom= 1;
+ layout.marginHeight= 0;
+ layout.marginWidth= 0;
+ layout.horizontalSpacing= 0;
+ fContainer.setLayout(layout);
+
+ fExpandBlock= new BreadcrumbItemDropDown(this, fContainer);
+ fDetailsBlock= new BreadcrumbItemDetails(this, fContainer);
+ }
+
+ /**
+ * Returns this items viewer.
+ *
+ * @return the viewer showing this item
+ */
+ public BreadcrumbViewer getViewer() {
+ return fParent;
+ }
+
+ /*
+ * @see org.eclipse.swt.widgets.Widget#dispose()
+ */
+ public void dispose() {
+ fContainer.dispose();
+ super.dispose();
+ }
+
+ public TreePath getPath() {
+ return fPath;
+ }
+
+ public void setPath(TreePath path) {
+ fPath = path;
+ }
+
+ /**
+ * Should this item show a text label.
+ *
+ * @param enabled true if it should
+ */
+ void setShowText(boolean enabled) {
+ fDetailsBlock.setTextVisible(enabled);
+ }
+
+ /**
+ * Does this item show a text label?
+ *
+ * @return true if it does.
+ */
+ boolean isShowText() {
+ return fDetailsBlock.isTextVisible();
+ }
+
+ /**
+ * Returns the width of this item.
+ *
+ * @return the width of this item
+ */
+ int getWidth() {
+ return fDetailsBlock.getWidth() + fExpandBlock.getWidth() + 2;
+ }
+
+ /**
+ * Sets whether this item has to be marked as
+ * selected or not.
+ *
+ * @param selected true if marked as selected
+ */
+ void setSelected(boolean selected) {
+ fDetailsBlock.setSelected(selected);
+ }
+
+ /**
+ * Sets whether this item has the keyboard focus.
+ *
+ * @param state <code>true</code> if it has focus, <code>false</code> otherwise
+ */
+ void setFocus(boolean state) {
+ fDetailsBlock.setFocus(state);
+ }
+
+ /**
+ * Returns whether this item has the keyboard focus.
+ *
+ * @return <code>true</code> if this item has the keyboard focus
+ */
+ boolean hasFocus() {
+ return fDetailsBlock.hasFocus();
+ }
+
+ /**
+ * Set whether this is the last item in the breadcrumb item chain or not.
+ *
+ * @param isLast <code>true</code> if this is the last item, <code>false</code> otherwise
+ */
+ void setIsLastItem(boolean isLast) {
+ fIsLast= isLast;
+
+ GridData data= (GridData) fContainer.getLayoutData();
+ data.grabExcessHorizontalSpace= isLast;
+ }
+
+ /**
+ * Expand this item, shows the drop down menu.
+ */
+ void openDropDownMenu() {
+ fExpandBlock.showMenu();
+ }
+
+ /**
+ * @return true if this item is expanded
+ */
+ boolean isMenuShown() {
+ return fExpandBlock.isMenuShown();
+ }
+
+ /**
+ * Returns the drop down shell.
+ *
+ * @return the shell of the drop down if shown, <code>null</code> otherwise
+ */
+ Shell getDropDownShell() {
+ return fExpandBlock.getDropDownShell();
+ }
+
+ /**
+ * Returns the bounds of this item.
+ *
+ * @return the bounds of this item
+ */
+ public Rectangle getBounds() {
+ return fContainer.getBounds();
+ }
+
+ /**
+ * Set the tool tip of the item to the given text.
+ *
+ * @param text the tool tip for the item
+ */
+ public void setToolTip(String text) {
+ fDetailsBlock.setToolTip(text);
+ }
+
+ /*
+ * @see org.eclipse.swt.widgets.Item#setText(java.lang.String)
+ */
+ public void setText(String string) {
+ super.setText(string);
+ fDetailsBlock.setText(string);
+
+ //more or less space might be required for the label
+ if (fIsLast)
+ fContainer.layout(true, true);
+ }
+
+ /*
+ * @see org.eclipse.swt.widgets.Item#setImage(org.eclipse.swt.graphics.Image)
+ */
+ public void setImage(Image image) {
+ super.setImage(image);
+ fDetailsBlock.setImage(image);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDetails.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDetails.java
new file mode 100644
index 000000000..7627f1670
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDetails.java
@@ -0,0 +1,425 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.events.FocusEvent;
+import org.eclipse.swt.events.FocusListener;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.PaintEvent;
+import org.eclipse.swt.events.PaintListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Shell;
+
+
+/**
+ * The label and icon part of the breadcrumb item.
+ *
+ * @since 3.5
+ */
+class BreadcrumbItemDetails {
+
+ private final Label fElementImage;
+ private final Label fElementText;
+ private final Composite fDetailComposite;
+ private final BreadcrumbItem fParent;
+ private final Composite fTextComposite;
+ private final Composite fImageComposite;
+
+ private boolean fTextVisible;
+ private boolean fSelected;
+ private boolean fHasFocus;
+
+
+ public BreadcrumbItemDetails(BreadcrumbItem parent, Composite parentContainer) {
+ fParent= parent;
+ fTextVisible= true;
+
+ fDetailComposite= new Composite(parentContainer, SWT.NONE);
+ fDetailComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ GridLayout layout= new GridLayout(2, false);
+ layout.marginHeight= 0;
+ layout.marginWidth= 0;
+ layout.horizontalSpacing= 0;
+ fDetailComposite.setLayout(layout);
+ addElementListener(fDetailComposite);
+
+ fImageComposite= new Composite(fDetailComposite, SWT.NONE);
+ fImageComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ layout= new GridLayout(1, false);
+ layout.marginHeight= 1;
+ layout.marginWidth= 2;
+ fImageComposite.setLayout(layout);
+ fImageComposite.addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ if (fHasFocus && !isTextVisible()) {
+ e.gc.drawFocus(e.x, e.y, e.width, e.height);
+ }
+ }
+ });
+ installFocusComposite(fImageComposite);
+ addElementListener(fImageComposite);
+
+ fElementImage= new Label(fImageComposite, SWT.NONE);
+ GridData layoutData= new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
+ fElementImage.setLayoutData(layoutData);
+ addElementListener(fElementImage);
+
+ fTextComposite= new Composite(fDetailComposite, SWT.NONE);
+ fTextComposite.setLayoutData(new GridData(SWT.BEGINNING, SWT.CENTER, false, false));
+ layout= new GridLayout(1, false);
+ layout.marginHeight= 2;
+ layout.marginWidth= 2;
+ fTextComposite.setLayout(layout);
+ addElementListener(fTextComposite);
+ fTextComposite.addPaintListener(new PaintListener() {
+ public void paintControl(PaintEvent e) {
+ if (fHasFocus && isTextVisible()) {
+ e.gc.drawFocus(e.x, e.y, e.width, e.height);
+ }
+ }
+ });
+ installFocusComposite(fTextComposite);
+ addElementListener(fTextComposite);
+
+ fElementText= new Label(fTextComposite, SWT.NONE);
+
+ layoutData= new GridData(SWT.BEGINNING, SWT.CENTER, false, false);
+ fElementText.setLayoutData(layoutData);
+ addElementListener(fElementText);
+
+ fTextComposite.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ e.result= fElementText.getText();
+ }
+ });
+ fImageComposite.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ e.result= fElementText.getText();
+ }
+ });
+
+ fDetailComposite.setTabList(new Control[] { fTextComposite });
+ }
+
+ /**
+ * Returns whether this element has the keyboard focus.
+ *
+ * @return true if this element has the keyboard focus.
+ */
+ public boolean hasFocus() {
+ return fHasFocus;
+ }
+
+ /**
+ * Sets the tool tip to the given text.
+ *
+ * @param text the tool tip
+ */
+ public void setToolTip(String text) {
+ if (isTextVisible()) {
+ fElementText.getParent().setToolTipText(text);
+ fElementText.setToolTipText(text);
+
+ fElementImage.setToolTipText(text);
+ } else {
+ fElementText.getParent().setToolTipText(null);
+ fElementText.setToolTipText(null);
+
+ fElementImage.setToolTipText(text);
+ }
+ }
+
+ /**
+ * Sets the image to the given image.
+ *
+ * @param image the image to use
+ */
+ public void setImage(Image image) {
+ if (image != fElementImage.getImage()) {
+ fElementImage.setImage(image);
+ }
+ }
+
+ /**
+ * Sets the text to the given text.
+ *
+ * @param text the text to use
+ */
+ public void setText(String text) {
+ if (text == null) {
+ text= ""; //$NON-NLS-1$
+ }
+ if (!text.equals(fElementText.getText())) {
+ fElementText.setText(text);
+ }
+ }
+
+ /**
+ * Returns the width of this element.
+ *
+ * @return current width of this element
+ */
+ public int getWidth() {
+ int result= 2;
+
+ if (fElementImage.getImage() != null)
+ result+= fElementImage.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+
+ if (fTextVisible && fElementText.getText().length() > 0)
+ result+= fElementText.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+
+ return result;
+ }
+
+ public void setTextVisible(boolean enabled) {
+ if (fTextVisible == enabled)
+ return;
+
+ fTextVisible= enabled;
+
+ GridData data= (GridData) fTextComposite.getLayoutData();
+ data.exclude= !enabled;
+ fTextComposite.setVisible(enabled);
+
+ if (fTextVisible) {
+ fDetailComposite.setTabList(new Control[] { fTextComposite });
+ } else {
+ fDetailComposite.setTabList(new Control[] { fImageComposite });
+ }
+
+ if (fHasFocus) {
+ if (isTextVisible()) {
+ fTextComposite.setFocus();
+ } else {
+ fImageComposite.setFocus();
+ }
+ }
+ updateSelection();
+ }
+
+ /**
+ * Tells whether this item shows a text or only an image.
+ *
+ * @return <code>true</code> if it shows a text and an image, false if it only shows the image
+ */
+ public boolean isTextVisible() {
+ return fTextVisible;
+ }
+
+ public void setSelected(boolean selected) {
+ if (selected == fSelected)
+ return;
+
+ fSelected= selected;
+ if (!fSelected)
+ fHasFocus= false;
+
+ updateSelection();
+ }
+
+ public void setFocus(boolean enabled) {
+ if (enabled == fHasFocus)
+ return;
+
+ fHasFocus= enabled;
+ if (fHasFocus) {
+ if (isTextVisible()) {
+ fTextComposite.setFocus();
+ } else {
+ fImageComposite.setFocus();
+ }
+ }
+ updateSelection();
+ }
+
+ private void updateSelection() {
+ Color background;
+ Color foreground;
+
+ if (fSelected && fHasFocus) {
+ background= Display.getDefault().getSystemColor(SWT.COLOR_LIST_SELECTION);
+ foreground= Display.getDefault().getSystemColor(SWT.COLOR_LIST_SELECTION_TEXT);
+ } else {
+ foreground= null;
+ background= null;
+ }
+
+ if (isTextVisible()) {
+ fTextComposite.setBackground(background);
+ fElementText.setBackground(background);
+ fElementText.setForeground(foreground);
+
+ fImageComposite.setBackground(null);
+ fElementImage.setBackground(null);
+ } else {
+ fImageComposite.setBackground(background);
+ fElementImage.setBackground(background);
+
+ fTextComposite.setBackground(null);
+ fElementText.setBackground(null);
+ fElementText.setForeground(null);
+ }
+
+ fTextComposite.redraw();
+ fImageComposite.redraw();
+ }
+
+ /**
+ * Install focus and key listeners to the given composite.
+ *
+ * @param composite the composite which may get focus
+ */
+ private void installFocusComposite(Composite composite) {
+ composite.addTraverseListener(new TraverseListener() {
+ public void keyTraversed(TraverseEvent e) {
+ if (e.detail == SWT.TRAVERSE_TAB_NEXT || e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {
+ int index= fParent.getViewer().getIndexOfItem(fParent);
+ if (e.detail == SWT.TRAVERSE_TAB_NEXT) {
+ index++;
+ } else {
+ index--;
+ }
+
+ if (index > 0 && index < fParent.getViewer().getItemCount()) {
+ fParent.getViewer().selectItem(fParent.getViewer().getItem(index));
+ }
+
+ e.doit= true;
+ }
+ }
+ });
+ composite.addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) {
+ BreadcrumbViewer viewer= fParent.getViewer();
+
+ switch (e.keyCode) {
+ case SWT.ARROW_LEFT:
+ if (fSelected) {
+ viewer.doTraverse(false);
+ e.doit= false;
+ } else {
+ viewer.selectItem(fParent);
+ }
+ break;
+ case SWT.ARROW_RIGHT:
+ if (fSelected) {
+ viewer.doTraverse(true);
+ e.doit= false;
+ } else {
+ viewer.selectItem(fParent);
+ }
+ break;
+ case SWT.ARROW_DOWN:
+ case SWT.ARROW_UP:
+ case SWT.KEYPAD_ADD:
+ if (!fSelected) {
+ viewer.selectItem(fParent);
+ }
+ openDropDown();
+ e.doit= false;
+ break;
+ case SWT.CR:
+ if (!fSelected) {
+ viewer.selectItem(fParent);
+ }
+ viewer.fireOpen();
+ break;
+ default:
+ if (e.character == ' ') {
+ if (!fSelected) {
+ viewer.selectItem(fParent);
+ }
+ openDropDown();
+ e.doit= false;
+ }
+ break;
+ }
+ }
+
+ private void openDropDown() {
+ Shell shell = fParent.getDropDownShell();
+ if (shell == null) {
+ fParent.openDropDownMenu();
+ shell = fParent.getDropDownShell();
+ }
+ shell.setFocus();
+ }
+
+ public void keyReleased(KeyEvent e) {
+ }
+ });
+
+ composite.addFocusListener(new FocusListener() {
+ public void focusGained(FocusEvent e) {
+ if (!fHasFocus) {
+ fHasFocus= true;
+ updateSelection();
+ }
+ }
+
+ public void focusLost(FocusEvent e) {
+ if (fHasFocus) {
+ fHasFocus= false;
+ updateSelection();
+ }
+ }
+ });
+ }
+
+ /**
+ * Add mouse listeners to the given control.
+ *
+ * @param control the control to which may be clicked
+ */
+ private void addElementListener(Control control) {
+ control.addMouseListener(new MouseListener() {
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+
+ public void mouseDown(MouseEvent e) {
+ BreadcrumbViewer viewer= fParent.getViewer();
+ Shell shell= fParent.getDropDownShell();
+ viewer.selectItem(fParent);
+ if (shell == null && e.button == 1 && e.stateMask == 0) {
+ fParent.getViewer().fireDoubleClick();
+ }
+ }
+
+ public void mouseUp(MouseEvent e) {
+ }
+ });
+ control.addMenuDetectListener(new MenuDetectListener() {
+ public void menuDetected(MenuDetectEvent e) {
+ BreadcrumbViewer viewer= fParent.getViewer();
+ viewer.selectItem(fParent);
+ fParent.getViewer().fireMenuDetect(e);
+ }
+ });
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java
new file mode 100644
index 000000000..37d07ceec
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbItemDropDown.java
@@ -0,0 +1,561 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.debug.internal.ui.DebugUIPlugin;
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.ToolBarManager;
+import org.eclipse.jface.resource.CompositeImageDescriptor;
+import org.eclipse.jface.util.Geometry;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.accessibility.AccessibleAdapter;
+import org.eclipse.swt.accessibility.AccessibleEvent;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MouseAdapter;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.ShellEvent;
+import org.eclipse.swt.events.ShellListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.ImageData;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.graphics.Rectangle;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Monitor;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.ToolBar;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.forms.FormColors;
+
+
+/**
+ * The part of the breadcrumb item with the drop down menu.
+ *
+ * @since 3.5
+ */
+class BreadcrumbItemDropDown implements IBreadcrumbDropDownSite {
+
+ /**
+ * Tells whether this class is in debug mode.
+ */
+ private static boolean DEBUG= DebugUIPlugin.DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.debug.ui/debug/breadcrumb")); //$NON-NLS-1$//$NON-NLS-2$
+
+ private static final boolean IS_MAC_WORKAROUND= "carbon".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+ /**
+ * An arrow image descriptor. The images color is related to the list
+ * fore- and background color. This makes the arrow visible even in high contrast
+ * mode. If <code>ltr</code> is true the arrow points to the right, otherwise it
+ * points to the left.
+ */
+ private final class AccessibelArrowImage extends CompositeImageDescriptor {
+
+ private final static int ARROW_SIZE= 5;
+
+ private final boolean fLTR;
+
+ public AccessibelArrowImage(boolean ltr) {
+ fLTR= ltr;
+ }
+
+ /*
+ * @see org.eclipse.jface.resource.CompositeImageDescriptor#drawCompositeImage(int, int)
+ */
+ protected void drawCompositeImage(int width, int height) {
+ Display display= fParentComposite.getDisplay();
+
+ Image image= new Image(display, ARROW_SIZE, ARROW_SIZE * 2);
+
+ GC gc= new GC(image);
+
+ Color triangle= createColor(SWT.COLOR_LIST_FOREGROUND, SWT.COLOR_LIST_BACKGROUND, 20, display);
+ Color aliasing= createColor(SWT.COLOR_LIST_FOREGROUND, SWT.COLOR_LIST_BACKGROUND, 30, display);
+ gc.setBackground(triangle);
+
+ if (fLTR) {
+ gc.fillPolygon(new int[] { mirror(0), 0, mirror(ARROW_SIZE), ARROW_SIZE, mirror(0), ARROW_SIZE * 2 });
+ } else {
+ gc.fillPolygon(new int[] { ARROW_SIZE, 0, 0, ARROW_SIZE, ARROW_SIZE, ARROW_SIZE * 2 });
+ }
+
+ gc.setForeground(aliasing);
+ gc.drawLine(mirror(0), 1, mirror(ARROW_SIZE - 1), ARROW_SIZE);
+ gc.drawLine(mirror(ARROW_SIZE - 1), ARROW_SIZE, mirror(0), ARROW_SIZE * 2 - 1);
+
+ gc.dispose();
+ triangle.dispose();
+ aliasing.dispose();
+
+ ImageData imageData= image.getImageData();
+ for (int y= 1; y < ARROW_SIZE; y++) {
+ for (int x= 0; x < y; x++) {
+ imageData.setAlpha(mirror(x), y, 255);
+ }
+ }
+ for (int y= 0; y < ARROW_SIZE; y++) {
+ for (int x= 0; x <= y; x++) {
+ imageData.setAlpha(mirror(x), ARROW_SIZE * 2 - y - 1, 255);
+ }
+ }
+
+ int offset= fLTR ? 0 : -1;
+ drawImage(imageData, (width / 2) - (ARROW_SIZE / 2) + offset, (height / 2) - ARROW_SIZE - 1);
+
+ image.dispose();
+ }
+
+ private int mirror(int x) {
+ if (fLTR)
+ return x;
+
+ return ARROW_SIZE - x - 1;
+ }
+
+ /*
+ * @see org.eclipse.jface.resource.CompositeImageDescriptor#getSize()
+ */
+ protected Point getSize() {
+ return new Point(10, 16);
+ }
+
+ private Color createColor(int color1, int color2, int ratio, Display display) {
+ RGB rgb1= display.getSystemColor(color1).getRGB();
+ RGB rgb2= display.getSystemColor(color2).getRGB();
+
+ RGB blend= FormColors.blend(rgb2, rgb1, ratio);
+
+ return new Color(display, blend);
+ }
+ }
+
+ private static final int DROP_DOWN_HIGHT= 300;
+ private static final int DROP_DOWN_WIDTH= 500;
+
+ private final BreadcrumbItem fParent;
+ private final Composite fParentComposite;
+ private final ToolBar fToolBar;
+
+ private boolean fMenuIsShown;
+ private boolean fEnabled;
+ private Shell fShell;
+
+ public BreadcrumbItemDropDown(BreadcrumbItem parent, Composite composite) {
+ fParent= parent;
+ fParentComposite= composite;
+ fMenuIsShown= false;
+ fEnabled= true;
+
+ fToolBar= new ToolBar(composite, SWT.FLAT);
+ fToolBar.setLayoutData(new GridData(SWT.END, SWT.CENTER, false, false));
+ fToolBar.getAccessible().addAccessibleListener(new AccessibleAdapter() {
+ public void getName(AccessibleEvent e) {
+ e.result= BreadcrumbMessages.BreadcrumbItemDropDown_showDropDownMenu_action_toolTip;
+ }
+ });
+ ToolBarManager manager= new ToolBarManager(fToolBar);
+
+ final Action showDropDownMenuAction= new Action(null, SWT.NONE) {
+ public void run() {
+ Shell shell= fParent.getDropDownShell();
+ if (shell != null)
+ return;
+
+ shell= fParent.getViewer().getDropDownShell();
+ if (shell != null)
+ shell.close();
+
+ showMenu();
+
+ fShell.setFocus();
+ }
+ };
+
+ showDropDownMenuAction.setImageDescriptor(new AccessibelArrowImage(isLeft()));
+ showDropDownMenuAction.setToolTipText(BreadcrumbMessages.BreadcrumbItemDropDown_showDropDownMenu_action_toolTip);
+ manager.add(showDropDownMenuAction);
+
+ manager.update(true);
+ if (IS_MAC_WORKAROUND) {
+ manager.getControl().addMouseListener(new MouseAdapter() {
+ // see also BreadcrumbItemDetails#addElementListener(Control)
+ public void mouseDown(MouseEvent e) {
+ showDropDownMenuAction.run();
+ }
+ });
+ }
+ }
+
+ /**
+ * Return the width of this element.
+ *
+ * @return the width of this element
+ */
+ public int getWidth() {
+ return fToolBar.computeSize(SWT.DEFAULT, SWT.DEFAULT).x;
+ }
+
+ /**
+ * Set whether the drop down menu is available.
+ *
+ * @param enabled true if available
+ */
+ public void setEnabled(boolean enabled) {
+ fEnabled= enabled;
+
+ fToolBar.setVisible(enabled);
+ }
+
+ /**
+ * Tells whether the menu is shown.
+ *
+ * @return true if the menu is open
+ */
+ public boolean isMenuShown() {
+ return fMenuIsShown;
+ }
+
+ /**
+ * Returns the shell used for the drop down menu if it is shown.
+ *
+ * @return the drop down shell or <code>null</code>
+ */
+ public Shell getDropDownShell() {
+ if (!isMenuShown())
+ return null;
+
+ return fShell;
+ }
+
+ /**
+ * Opens the drop down menu.
+ */
+ public void showMenu() {
+ if (DEBUG)
+ System.out.println("BreadcrumbItemDropDown.showMenu()"); //$NON-NLS-1$
+
+ if (!fEnabled || fMenuIsShown)
+ return;
+
+ fMenuIsShown= true;
+
+ fShell= new Shell(fToolBar.getShell(), SWT.RESIZE | SWT.TOOL | SWT.ON_TOP);
+ if (DEBUG)
+ System.out.println(" creating new shell"); //$NON-NLS-1$
+
+ GridLayout layout= new GridLayout(1, false);
+ layout.marginHeight= 0;
+ layout.marginWidth= 0;
+ fShell.setLayout(layout);
+
+ Composite composite= new Composite(fShell, SWT.NONE);
+ composite.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+ GridLayout gridLayout= new GridLayout(1, false);
+ gridLayout.marginHeight= 0;
+ gridLayout.marginWidth= 0;
+ composite.setLayout(gridLayout);
+
+ TreePath path= fParent.getPath();
+
+ Control control = fParent.getViewer().createDropDown(composite, this, path);
+
+ control.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true));
+
+ setShellBounds(fShell);
+ fShell.setVisible(true);
+ installCloser(fShell);
+ }
+
+ /**
+ * The closer closes the given shell when the focus is lost.
+ *
+ * @param shell the shell to install the closer to
+ */
+ private void installCloser(final Shell shell) {
+ final Listener focusListener= new Listener() {
+ public void handleEvent(Event event) {
+ Widget focusElement= event.widget;
+ boolean isFocusBreadcrumbTreeFocusWidget= focusElement == shell || focusElement instanceof Control && ((Control)focusElement).getShell() == shell;
+
+ switch (event.type) {
+ case SWT.FocusIn:
+ if (DEBUG)
+ System.out.println("focusIn - is breadcrumb tree: " + isFocusBreadcrumbTreeFocusWidget); //$NON-NLS-1$
+
+ if (!isFocusBreadcrumbTreeFocusWidget) {
+ if (DEBUG)
+ System.out.println("==> closing shell since focus in other widget"); //$NON-NLS-1$
+ shell.close();
+ }
+ break;
+
+ case SWT.FocusOut:
+ if (DEBUG)
+ System.out.println("focusOut - is breadcrumb tree: " + isFocusBreadcrumbTreeFocusWidget); //$NON-NLS-1$
+
+ if (event.display.getActiveShell() != shell) {
+ if (DEBUG)
+ System.out.println("==> closing shell since event.display.getActiveShell() != shell"); //$NON-NLS-1$
+ shell.close();
+ }
+ break;
+
+ default:
+ Assert.isTrue(false);
+ }
+ }
+ };
+
+ final Display display= shell.getDisplay();
+ display.addFilter(SWT.FocusIn, focusListener);
+ display.addFilter(SWT.FocusOut, focusListener);
+
+ final ControlListener controlListener= new ControlListener() {
+ public void controlMoved(ControlEvent e) {
+ if (!shell.isDisposed()) {
+ shell.close();
+ }
+ }
+
+ public void controlResized(ControlEvent e) {
+ if (!shell.isDisposed()) {
+ shell.close();
+ }
+ }
+ };
+ fToolBar.getShell().addControlListener(controlListener);
+
+ shell.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (DEBUG)
+ System.out.println("==> shell disposed"); //$NON-NLS-1$
+
+ display.removeFilter(SWT.FocusIn, focusListener);
+ display.removeFilter(SWT.FocusOut, focusListener);
+
+ if (!fToolBar.isDisposed()) {
+ fToolBar.getShell().removeControlListener(controlListener);
+ }
+ }
+ });
+ shell.addShellListener(new ShellListener() {
+ public void shellActivated(ShellEvent e) {
+ }
+
+ public void shellClosed(ShellEvent e) {
+ if (DEBUG)
+ System.out.println("==> shellClosed"); //$NON-NLS-1$
+
+ if (!fMenuIsShown)
+ return;
+
+ fMenuIsShown= false;
+ }
+
+ public void shellDeactivated(ShellEvent e) {
+ }
+
+ public void shellDeiconified(ShellEvent e) {
+ }
+
+ public void shellIconified(ShellEvent e) {
+ }
+ });
+ }
+
+ /**
+ * Calculates a useful size for the given shell.
+ *
+ * @param shell the shell to calculate the size for.
+ */
+ private void setShellBounds(Shell shell) {
+
+ Rectangle rect= fParentComposite.getBounds();
+ Rectangle toolbarBounds= fToolBar.getBounds();
+
+ shell.pack();
+ Point size= shell.getSize();
+ int height= Math.min(size.y, DROP_DOWN_HIGHT);
+ // TODO: Because of bug 258196 the drop down does not resize correctly
+ // on GTK. As a workaround temporarily increase the initial width of
+ // the drop down to 500.
+ //int width= Math.max(Math.min(size.x, DROP_DOWN_WIDTH), 250);
+ int width= Math.max(Math.min(size.x, DROP_DOWN_WIDTH), 500);
+
+ int imageBoundsX= 0;
+ if (fParent.getImage() != null) {
+ imageBoundsX= fParent.getImage().getImageData().width;
+ }
+
+ Rectangle trim= fShell.computeTrim(0, 0, width, height);
+ int x= toolbarBounds.x + toolbarBounds.width + 2 + trim.x - imageBoundsX;
+ if (!isLeft())
+ x+= width;
+
+ int y = rect.y;
+ if (isTop())
+ y+= rect.height;
+ else
+ y-= height;
+
+ Point pt= new Point(x, y);
+ pt= fParentComposite.toDisplay(pt);
+
+ Rectangle monitor= getClosestMonitor(shell.getDisplay(), pt).getClientArea();
+ int overlap= (pt.x + width) - (monitor.x + monitor.width);
+ if (overlap > 0)
+ pt.x-= overlap;
+ if (pt.x < monitor.x)
+ pt.x= monitor.x;
+
+ shell.setLocation(pt);
+ shell.setSize(width, height);
+ }
+
+ /**
+ * Returns the monitor whose client area contains the given point. If no monitor contains the
+ * point, returns the monitor that is closest to the point.
+ * <p>
+ * Copied from <code>org.eclipse.jface.window.Window.getClosestMonitor(Display, Point)</code>
+ * </p>
+ *
+ * @param display the display showing the monitors
+ * @param point point to find (display coordinates)
+ * @return the monitor closest to the given point
+ */
+ private static Monitor getClosestMonitor(Display display, Point point) {
+ int closest= Integer.MAX_VALUE;
+
+ Monitor[] monitors= display.getMonitors();
+ Monitor result= monitors[0];
+
+ for (int i= 0; i < monitors.length; i++) {
+ Monitor current= monitors[i];
+
+ Rectangle clientArea= current.getClientArea();
+
+ if (clientArea.contains(point))
+ return current;
+
+ int distance= Geometry.distanceSquared(Geometry.centerPoint(clientArea), point);
+ if (distance < closest) {
+ closest= distance;
+ result= current;
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Set the size of the given shell such that more content can be shown. The shell size does not
+ * exceed {@link #DROP_DOWN_HIGHT} and {@link #DROP_DOWN_WIDTH}.
+ *
+ * @param shell the shell to resize
+ */
+ private void resizeShell(final Shell shell) {
+ Point size= shell.getSize();
+ int currentWidth= size.x;
+ int currentHeight= size.y;
+
+ if (currentHeight >= DROP_DOWN_HIGHT && currentWidth >= DROP_DOWN_WIDTH)
+ return;
+
+ Point preferedSize= shell.computeSize(SWT.DEFAULT, SWT.DEFAULT, true);
+
+ int newWidth;
+ if (currentWidth >= DROP_DOWN_WIDTH) {
+ newWidth= currentWidth;
+ } else {
+ newWidth= Math.min(Math.max(preferedSize.x, currentWidth), DROP_DOWN_WIDTH);
+ }
+ int newHeight;
+ if (currentHeight >= DROP_DOWN_HIGHT) {
+ newHeight= currentHeight;
+ } else {
+ newHeight= Math.min(Math.max(preferedSize.y, currentHeight), DROP_DOWN_HIGHT);
+ }
+
+ if (newHeight != currentHeight || newWidth != currentWidth) {
+ shell.setRedraw(false);
+ try {
+ shell.setSize(newWidth, newHeight);
+
+ Point location = shell.getLocation();
+ Point newLocation = location;
+ if (!isLeft()) {
+ newLocation = new Point(newLocation.x - (newWidth - currentWidth), newLocation.y);
+ }
+ if (!isTop()) {
+ newLocation = new Point(newLocation.x, newLocation.y - (newHeight - currentHeight));
+ }
+ if (!location.equals(newLocation)) {
+ shell.setLocation(newLocation.x, newLocation.y);
+ }
+ } finally {
+ shell.setRedraw(true);
+ }
+ }
+ }
+
+ /**
+ * Tells whether this the breadcrumb is in LTR mode or RTL mode. Or whether the breadcrumb
+ * is on the right-side status coolbar, which has the same effect on layout.
+ *
+ * @return <code>true</code> if the breadcrumb in left-to-right mode, <code>false</code>
+ * otherwise
+ */
+ private boolean isLeft() {
+ return (fParentComposite.getStyle() & SWT.RIGHT_TO_LEFT) == 0 &&
+ (fParent.getViewer().getStyle() & SWT.RIGHT) == 0;
+ }
+
+ /**
+ * Tells whether this the breadcrumb is in LTR mode or RTL mode. Or whether the breadcrumb
+ * is on the right-side status coolbar, which has the same effect on layout.
+ *
+ * @return <code>true</code> if the breadcrumb in left-to-right mode, <code>false</code>
+ * otherwise
+ */
+ private boolean isTop() {
+ return (fParent.getViewer().getStyle() & SWT.BOTTOM) == 0;
+ }
+
+ public void close() {
+ fShell.close();
+ }
+
+ public void notifySelection(ISelection selection) {
+ fParent.getViewer().fireMenuSelection(selection);
+ }
+
+ public void updateSize() {
+ if (!fShell.isDisposed()) {
+ resizeShell(fShell);
+ }
+ }
+}
+
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.java
new file mode 100644
index 000000000..e9f6e88bc
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.java
@@ -0,0 +1,35 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.osgi.util.NLS;
+
+
+/**
+ * Helper class to get NLSed messages.
+ *
+ * @since 3.5
+ */
+public class BreadcrumbMessages extends NLS {
+
+ private static final String BUNDLE_NAME= BreadcrumbMessages.class.getName();
+
+ public static String BreadcrumbItemDropDown_showDropDownMenu_action_toolTip;
+
+ static {
+ // initialize resource bundle
+ NLS.initializeMessages(BUNDLE_NAME, BreadcrumbMessages.class);
+ }
+
+ private BreadcrumbMessages() {
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.properties
new file mode 100644
index 000000000..a584ca2de
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbMessages.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2008, 2009 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
+# Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+###############################################################################
+
+BreadcrumbItemDropDown_showDropDownMenu_action_toolTip=Show Children
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbViewer.java
new file mode 100644
index 000000000..94fcc61eb
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/BreadcrumbViewer.java
@@ -0,0 +1,817 @@
+/*******************************************************************************
+ * Copyright (c) 2008, 2009 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
+ * Pawel Piech (Wind River) - adapted breadcrumb for use in Debug view (Bug 252677)
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.runtime.Assert;
+import org.eclipse.core.runtime.ListenerList;
+import org.eclipse.jface.viewers.DoubleClickEvent;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreePathContentProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredViewer;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.events.DisposeListener;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.events.TraverseEvent;
+import org.eclipse.swt.events.TraverseListener;
+import org.eclipse.swt.graphics.Color;
+import org.eclipse.swt.graphics.GC;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.graphics.RGB;
+import org.eclipse.swt.layout.GridData;
+import org.eclipse.swt.layout.GridLayout;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.swt.widgets.Event;
+import org.eclipse.swt.widgets.Listener;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.swt.widgets.Widget;
+import org.eclipse.ui.forms.FormColors;
+
+
+/**
+ * A breadcrumb viewer shows a the parent chain of its input element in a list. Each breadcrumb item
+ * of that list can be expanded and a sibling of the element presented by the breadcrumb item can be
+ * selected.
+ * <p>
+ * Content providers for breadcrumb viewers must implement the <code>ITreePathContentProvider</code>
+ * interface.
+ * </p>
+ * <p>
+ * Label providers for breadcrumb viewers must implement the <code>ITreePathLabelProvider</code> interface.
+ * </p>
+ *
+ * @since 3.5
+ */
+public abstract class BreadcrumbViewer extends StructuredViewer {
+
+ private static final boolean IS_GTK= "gtk".equals(SWT.getPlatform()); //$NON-NLS-1$
+
+ private final int fStyle;
+ private final Composite fContainer;
+ private final ArrayList fBreadcrumbItems;
+ private final ListenerList fMenuListeners;
+
+ private Image fGradientBackground;
+ private BreadcrumbItem fSelectedItem;
+
+ /**
+ * Create a new <code>BreadcrumbViewer</code>.
+ * <p>
+ * Style is one of:
+ * <ul>
+ * <li>SWT.NONE</li>
+ * <li>SWT.VERTICAL</li>
+ * <li>SWT.HORIZONTAL</li>
+ * <li>SWT.BOTTOM</li>
+ * <li>SWT.RIGHT</li>
+ * </ul>
+ *
+ * @param parent the container for the viewer
+ * @param style the style flag used for this viewer
+ */
+ public BreadcrumbViewer(Composite parent, int style) {
+ fStyle = style;
+ fBreadcrumbItems= new ArrayList();
+ fMenuListeners= new ListenerList();
+
+ fContainer= new Composite(parent, SWT.NONE);
+ GridData layoutData= new GridData(SWT.FILL, SWT.TOP, true, false);
+ fContainer.setLayoutData(layoutData);
+ fContainer.addTraverseListener(new TraverseListener() {
+ public void keyTraversed(TraverseEvent e) {
+ e.doit= true;
+ }
+ });
+ fContainer.setBackgroundMode(SWT.INHERIT_DEFAULT);
+
+ fContainer.addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event event) {
+ int height= fContainer.getClientArea().height;
+
+ if (fGradientBackground == null || fGradientBackground.getBounds().height != height) {
+ Image image= createGradientImage(height, event.display);
+ fContainer.setBackgroundImage(image);
+
+ if (fGradientBackground != null)
+ fGradientBackground.dispose();
+ fGradientBackground= image;
+ }
+ }
+ });
+
+
+ fContainer.addDisposeListener(new DisposeListener() {
+ public void widgetDisposed(DisposeEvent e) {
+ if (fGradientBackground != null) {
+ fGradientBackground.dispose();
+ fGradientBackground = null;
+ }
+ }
+ });
+
+ hookControl(fContainer);
+
+ int columns= 1000;
+ if ((SWT.VERTICAL & style) != 0) {
+ columns= 2;
+ }
+
+ GridLayout gridLayout= new GridLayout(columns, false);
+ gridLayout.marginWidth= 0;
+ gridLayout.marginHeight= 0;
+ gridLayout.verticalSpacing= 0;
+ gridLayout.horizontalSpacing= 0;
+ fContainer.setLayout(gridLayout);
+
+ fContainer.addListener(SWT.Resize, new Listener() {
+ public void handleEvent(Event event) {
+ refresh();
+ }
+ });
+ }
+
+ int getStyle() {
+ return fStyle;
+ }
+
+ /**
+ * Configure the given drop down viewer. The given input is used for the viewers input. Clients
+ * must at least set the label and the content provider for the viewer.
+ *
+ * @param viewer the viewer to configure
+ * @param input the input for the viewer
+ */
+ protected abstract Control createDropDown(Composite parent, IBreadcrumbDropDownSite site, TreePath path);
+
+ /*
+ * @see org.eclipse.jface.viewers.Viewer#getControl()
+ */
+ public Control getControl() {
+ return fContainer;
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#reveal(java.lang.Object)
+ */
+ public void reveal(Object element) {
+ //all elements are always visible
+ }
+
+ /**
+ * Transfers the keyboard focus into the viewer.
+ */
+ public void setFocus() {
+ fContainer.setFocus();
+
+ if (fSelectedItem != null) {
+ fSelectedItem.setFocus(true);
+ } else {
+ if (fBreadcrumbItems.size() == 0)
+ return;
+
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.get(fBreadcrumbItems.size() - 1);
+ item.setFocus(true);
+ }
+ }
+
+ /**
+ * @return true if any of the items in the viewer is expanded
+ */
+ public boolean isDropDownOpen() {
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ if (item.isMenuShown())
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * The shell used for the shown drop down or <code>null</code>
+ * if no drop down is shown at the moment.
+ *
+ * @return the drop downs shell or <code>null</code>
+ */
+ public Shell getDropDownShell() {
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ if (item.isMenuShown())
+ return item.getDropDownShell();
+ }
+
+ return null;
+ }
+
+ /**
+ * Add the given listener to the set of listeners which will be informed
+ * when a context menu is requested for a breadcrumb item.
+ *
+ * @param listener the listener to add
+ */
+ public void addMenuDetectListener(MenuDetectListener listener) {
+ fMenuListeners.add(listener);
+ }
+
+ /**
+ * Remove the given listener from the set of menu detect listeners.
+ * Does nothing if the listener is not element of the set.
+ *
+ * @param listener the listener to remove
+ */
+ public void removeMenuDetectListener(MenuDetectListener listener) {
+ fMenuListeners.remove(listener);
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#assertContentProviderType(org.eclipse.jface.viewers.IContentProvider)
+ */
+ protected void assertContentProviderType(IContentProvider provider) {
+ super.assertContentProviderType(provider);
+ Assert.isTrue(provider instanceof ITreePathContentProvider);
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.Viewer#inputChanged(java.lang.Object, java.lang.Object)
+ */
+ protected void inputChanged(Object fInput, Object oldInput) {
+ if (fContainer.isDisposed())
+ return;
+
+ disableRedraw();
+ try {
+ if (fBreadcrumbItems.size() > 0) {
+ BreadcrumbItem last= (BreadcrumbItem) fBreadcrumbItems.get(fBreadcrumbItems.size() - 1);
+ last.setIsLastItem(false);
+ }
+
+ int lastIndex= buildItemChain(fInput);
+
+ BreadcrumbItem last = null;
+ if (lastIndex > 0) {
+ last= (BreadcrumbItem) fBreadcrumbItems.get(lastIndex - 1);
+ last.setIsLastItem(true);
+ }
+
+ while (lastIndex < fBreadcrumbItems.size()) {
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.remove(fBreadcrumbItems.size() - 1);
+ if (item.hasFocus() && last != null) {
+ last.setFocus(true);
+ }
+ if (item == fSelectedItem) {
+ selectItem(null);
+ }
+ if (item.getData() != null)
+ unmapElement(item.getData());
+ item.dispose();
+ }
+
+ updateSize();
+ fContainer.layout(true, true);
+ } finally {
+ enableRedraw();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#doFindInputItem(java.lang.Object)
+ */
+ protected Widget doFindInputItem(Object element) {
+ if (element == null)
+ return null;
+
+ if (element == getInput() || element.equals(getInput()))
+ return doFindItem(element);
+
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#doFindItem(java.lang.Object)
+ */
+ protected Widget doFindItem(Object element) {
+ if (element == null)
+ return null;
+
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ if (item.getData() == element || element.equals(item.getData()))
+ return item;
+ }
+
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#doUpdateItem(org.eclipse.swt.widgets.Widget, java.lang.Object, boolean)
+ */
+ protected void doUpdateItem(Widget widget, Object element, boolean fullMap) {
+ if (widget instanceof BreadcrumbItem) {
+ final BreadcrumbItem item= (BreadcrumbItem) widget;
+
+ // remember element we are showing
+ if (fullMap) {
+ associate(element, item);
+ } else {
+ Object data= item.getData();
+ if (data != null) {
+ unmapElement(data, item);
+ }
+ item.setData(element);
+ mapElement(element, item);
+ }
+
+ refreshItem(item);
+ }
+ }
+
+ /**
+ * This implementation of getSelection() returns an instance of
+ * ITreeSelection.
+ */
+ public ISelection getSelection() {
+ Control control = getControl();
+ if (control == null || control.isDisposed()) {
+ return TreeSelection.EMPTY;
+ }
+ if (fSelectedItem != null) {
+ TreePath path = getTreePathFromItem(fSelectedItem);
+ if (path != null) {
+ return new TreeSelection(new TreePath[] { path });
+ }
+ }
+ return TreeSelection.EMPTY;
+ }
+
+ protected TreePath getTreePathFromItem(BreadcrumbItem item) {
+ List elements = new ArrayList(fBreadcrumbItems.size());
+ for (int i = 0; i < fBreadcrumbItems.size(); i++) {
+ elements.add( ((BreadcrumbItem)fBreadcrumbItems.get(i)).getData() );
+ if (fBreadcrumbItems.get(i).equals(item)) {
+ return new TreePath(elements.toArray());
+ }
+ }
+ return null;
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#getSelectionFromWidget()
+ */
+ protected List getSelectionFromWidget() {
+ if (fSelectedItem == null)
+ return Collections.EMPTY_LIST;
+
+ if (fSelectedItem.getData() == null)
+ return Collections.EMPTY_LIST;
+
+ ArrayList result= new ArrayList();
+ result.add(fSelectedItem.getData());
+ return result;
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#internalRefresh(java.lang.Object)
+ */
+ protected void internalRefresh(Object element) {
+
+ disableRedraw();
+ try {
+ BreadcrumbItem item= (BreadcrumbItem) doFindItem(element);
+ if (item == null || element != null && element.equals(getInput())) {
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem item1= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ refreshItem(item1);
+ }
+ } else {
+ refreshItem(item);
+ }
+ updateSize();
+ fContainer.layout(true, true);
+ } finally {
+ enableRedraw();
+ }
+ }
+
+ /*
+ * @see org.eclipse.jface.viewers.StructuredViewer#setSelectionToWidget(java.util.List, boolean)
+ */
+ protected void setSelectionToWidget(List l, boolean reveal) {
+ BreadcrumbItem focusItem= null;
+
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem item= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ if (item.hasFocus())
+ focusItem= item;
+
+ item.setSelected(false);
+ }
+
+ if (l == null)
+ return;
+
+ fSelectedItem = null;
+ for (Iterator iterator= l.iterator(); iterator.hasNext();) {
+ Object element= iterator.next();
+ BreadcrumbItem item= (BreadcrumbItem) doFindItem(element);
+ if (item != null) {
+ item.setSelected(true);
+ fSelectedItem= item;
+ if (item == focusItem) {
+ item.setFocus(true);
+ }
+ }
+ }
+ }
+
+ /**
+ * Set a single selection to the given item. <code>null</code> to deselect all.
+ *
+ * @param item the item to select or <code>null</code>
+ */
+ void selectItem(BreadcrumbItem item) {
+ if (fSelectedItem != null)
+ fSelectedItem.setSelected(false);
+
+ fSelectedItem= item;
+ setSelectionToWidget(getSelection(), false);
+
+ if (item != null) {
+ setFocus();
+ }/* else {
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem listItem= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ listItem.setFocus(false);
+ }
+ }*/
+
+ fireSelectionChanged(new SelectionChangedEvent(this, getSelection()));
+ }
+
+ /**
+ * Returns the item count.
+ *
+ * @return number of items shown in the viewer
+ */
+ int getItemCount() {
+ return fBreadcrumbItems.size();
+ }
+
+ /**
+ * Returns the item for the given item index.
+ *
+ * @param index the index of the item
+ * @return the item ad the given <code>index</code>
+ */
+ BreadcrumbItem getItem(int index) {
+ return (BreadcrumbItem) fBreadcrumbItems.get(index);
+ }
+
+ /**
+ * Returns the index of the given item.
+ *
+ * @param item the item to search
+ * @return the index of the item or -1 if not found
+ */
+ int getIndexOfItem(BreadcrumbItem item) {
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem pItem= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ if (pItem == item)
+ return i;
+ }
+
+ return -1;
+ }
+
+ /**
+ * Notifies all double click listeners.
+ */
+ void fireDoubleClick() {
+ fireDoubleClick(new DoubleClickEvent(this, getSelection()));
+ }
+
+ /**
+ * Notifies all open listeners.
+ */
+ void fireOpen() {
+ fireOpen(new OpenEvent(this, getSelection()));
+ }
+
+ /**
+ * The given element was selected from a drop down menu.
+ *
+ * @param element the selected element
+ */
+ void fireMenuSelection(ISelection selection) {
+ fireOpen(new OpenEvent(this, selection));
+ }
+
+ /**
+ * A context menu has been requested for the selected breadcrumb item.
+ *
+ * @param event the event issued the menu detection
+ */
+ void fireMenuDetect(MenuDetectEvent event) {
+ Object[] listeners= fMenuListeners.getListeners();
+ for (int i= 0; i < listeners.length; i++) {
+ ((MenuDetectListener)listeners[i]).menuDetected(event);
+ }
+ }
+
+ /**
+ * Set selection to the next or previous element if possible.
+ *
+ * @param next <code>true</code> if the next element should be selected, otherwise the previous
+ * one will be selected
+ */
+ void doTraverse(boolean next) {
+ if (fSelectedItem == null)
+ return;
+
+ int index= fBreadcrumbItems.indexOf(fSelectedItem);
+ if (next) {
+ if (index == fBreadcrumbItems.size() - 1) {
+ BreadcrumbItem current= (BreadcrumbItem) fBreadcrumbItems.get(index);
+
+ current.openDropDownMenu();
+ current.getDropDownShell().setFocus();
+ } else {
+ BreadcrumbItem nextItem= (BreadcrumbItem) fBreadcrumbItems.get(index + 1);
+ selectItem(nextItem);
+ }
+ } else {
+ if (index == 0) {
+ BreadcrumbItem root= (BreadcrumbItem) fBreadcrumbItems.get(index);
+ root.openDropDownMenu();
+ root.getDropDownShell().setFocus();
+ } else {
+ selectItem((BreadcrumbItem) fBreadcrumbItems.get(index - 1));
+ }
+ }
+ }
+
+ /**
+ * Generates the parent chain of the given element.
+ *
+ * @param element element to build the parent chain for
+ * @return the first index of an item in fBreadcrumbItems which is not
+ * part of the chain
+ */
+ private int buildItemChain(Object input) {
+ int index = 0;
+ if (input == null) {
+ return index;
+ }
+
+ ITreePathContentProvider contentProvider= (ITreePathContentProvider) getContentProvider();
+ TreePath path = new TreePath(new Object[0]);
+ //BreadcrumbItem item = getOrCreateItem(index++, path, input);
+ //don't show the models root
+ //item.setVisible(false);
+
+ BreadcrumbItem item = null;
+
+ // Top level elements need to be retrieved using getElements(), rest
+ // using getChildren().
+ Object[] children = contentProvider.getElements(input);
+ Object element = children != null && children.length != 0 ? children[0] : null;
+ while (element != null) {
+ path = path.createChildPath(element);
+
+ // All but last item are hidden if the viewer is in a vertical toolbar.
+ children = contentProvider.getChildren(path);
+ if ((getStyle() & SWT.VERTICAL) == 0 || children == null || children.length == 0) {
+ item = getOrCreateItem(index++, path, element);
+ }
+
+ if (children != null && children.length != 0) {
+ element = children[0];
+ } else {
+ break;
+ }
+
+ }
+ if (item != null) {
+ item.setIsLastItem(true);
+ }
+
+ return index;
+ }
+
+ private void refreshItem(BreadcrumbItem item) {
+ TreePath path = getTreePathFromItem(item);
+
+ ViewerLabel label = new ViewerLabel(item.getText(), item.getImage());
+ ((ITreePathLabelProvider)getLabelProvider()).updateLabel(label, path);
+
+ item.setText(label.getText());
+ item.setImage(label.getImage());
+ item.setToolTip(label.getTooltipText());
+ }
+
+ /**
+ * Creates and returns a new instance of a breadcrumb item.
+ *
+ * @return new instance of a breadcrumb item
+ */
+ private BreadcrumbItem getOrCreateItem(int index, TreePath path, Object element) {
+ BreadcrumbItem item;
+ if (fBreadcrumbItems.size() > index) {
+ item = (BreadcrumbItem)fBreadcrumbItems.get(index);
+ if (item.getData() != null) {
+ unmapElement(item.getData());
+ }
+ } else {
+ item = new BreadcrumbItem(this, fContainer);
+ fBreadcrumbItems.add(item);
+ }
+
+ if (equals(element, item.getData())) {
+ item.setPath(path);
+ update(element, null);
+ } else {
+ item.setData(element);
+ item.setPath(path);
+ refreshItem(item);
+ }
+ mapElement(element, item);
+
+ return item;
+ }
+
+ /**
+ * Update the size of the items such that all items are visible, if possible.
+ *
+ * @return <code>true</code> if any item has changed, <code>false</code> otherwise
+ */
+ private boolean updateSize() {
+ int width= fContainer.getClientArea().width;
+
+ int currentWidth= getCurrentWidth();
+
+ boolean requiresLayout= false;
+
+ if (currentWidth > width) {
+ int index= 0;
+ while (currentWidth > width && index < fBreadcrumbItems.size() - 1) {
+ BreadcrumbItem viewer= (BreadcrumbItem) fBreadcrumbItems.get(index);
+ if (viewer.isShowText()) {
+ viewer.setShowText(false);
+ currentWidth= getCurrentWidth();
+ requiresLayout= true;
+ }
+
+ index++;
+ }
+
+ } else if (currentWidth < width) {
+
+ int index= fBreadcrumbItems.size() - 1;
+ while (currentWidth < width && index >= 0) {
+
+ BreadcrumbItem viewer= (BreadcrumbItem) fBreadcrumbItems.get(index);
+ if (!viewer.isShowText()) {
+ viewer.setShowText(true);
+ currentWidth= getCurrentWidth();
+ if (currentWidth > width) {
+ viewer.setShowText(false);
+ index= 0;
+ } else {
+ requiresLayout= true;
+ }
+ }
+
+ index--;
+ }
+ }
+
+ return requiresLayout;
+ }
+
+ /**
+ * Returns the current width of all items in the list.
+ *
+ * @return the width of all items in the list
+ */
+ private int getCurrentWidth() {
+ int result= 0;
+ for (int i= 0, size= fBreadcrumbItems.size(); i < size; i++) {
+ BreadcrumbItem viewer= (BreadcrumbItem) fBreadcrumbItems.get(i);
+ result+= viewer.getWidth();
+ }
+
+ return result;
+ }
+
+ /**
+ * Enables redrawing of the breadcrumb.
+ */
+ private void enableRedraw() {
+ if (IS_GTK) //flickers on GTK
+ return;
+
+ fContainer.setRedraw(true);
+ }
+
+ /**
+ * Disables redrawing of the breadcrumb.
+ *
+ * <p>
+ * <strong>A call to this method must be followed by a call to {@link #enableRedraw()}</strong>
+ * </p>
+ */
+ private void disableRedraw() {
+ if (IS_GTK) //flickers on GTK
+ return;
+
+ fContainer.setRedraw(false);
+ }
+
+ /**
+ * The image to use for the breadcrumb background as specified in
+ * https://bugs.eclipse.org/bugs/show_bug.cgi?id=221477
+ *
+ * @param height the height of the image to create
+ * @param display the current display
+ * @return the image for the breadcrumb background
+ */
+ private Image createGradientImage(int height, Display display) {
+ int width= 50;
+
+ Image result= new Image(display, width, height);
+
+ GC gc= new GC(result);
+
+ Color colorC= createColor(SWT.COLOR_WIDGET_BACKGROUND, SWT.COLOR_LIST_BACKGROUND, 35, display);
+ Color colorD= createColor(SWT.COLOR_WIDGET_BACKGROUND, SWT.COLOR_LIST_BACKGROUND, 45, display);
+ Color colorE= createColor(SWT.COLOR_WIDGET_BACKGROUND, SWT.COLOR_LIST_BACKGROUND, 80, display);
+ Color colorF= createColor(SWT.COLOR_WIDGET_BACKGROUND, SWT.COLOR_LIST_BACKGROUND, 70, display);
+ Color colorG= createColor(SWT.COLOR_WIDGET_BACKGROUND, SWT.COLOR_WHITE, 45, display);
+ Color colorH= createColor(SWT.COLOR_WIDGET_NORMAL_SHADOW, SWT.COLOR_LIST_BACKGROUND, 35, display);
+
+ try {
+ drawLine(width, 0, colorC, gc);
+ drawLine(width, 1, colorC, gc);
+
+ gc.setForeground(colorD);
+ gc.setBackground(colorE);
+ gc.fillGradientRectangle(0, 2, width, 2 + 8, true);
+
+ gc.setBackground(colorE);
+ gc.fillRectangle(0, 2 + 9, width, height - 4);
+
+ drawLine(width, height - 3, colorF, gc);
+ drawLine(width, height - 2, colorG, gc);
+ drawLine(width, height - 1, colorH, gc);
+
+ } finally {
+ gc.dispose();
+
+ colorC.dispose();
+ colorD.dispose();
+ colorE.dispose();
+ colorF.dispose();
+ colorG.dispose();
+ colorH.dispose();
+ }
+
+ return result;
+ }
+
+ private void drawLine(int width, int position, Color color, GC gc) {
+ gc.setForeground(color);
+ gc.drawLine(0, position, width, position);
+ }
+
+ private Color createColor(int color1, int color2, int ratio, Display display) {
+ RGB rgb1= display.getSystemColor(color1).getRGB();
+ RGB rgb2= display.getSystemColor(color2).getRGB();
+
+ RGB blend= FormColors.blend(rgb2, rgb1, ratio);
+
+ return new Color(display, blend);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/IBreadcrumbDropDownSite.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/IBreadcrumbDropDownSite.java
new file mode 100644
index 000000000..adea89dfd
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/IBreadcrumbDropDownSite.java
@@ -0,0 +1,40 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import org.eclipse.jface.viewers.ISelection;
+
+/**
+ * Interface allowing breadcrumb drop-down implementors to communicate with their
+ * containing breadcrumb.
+ *
+ * @since 3.5
+ */
+public interface IBreadcrumbDropDownSite {
+
+ /**
+ * Notifies the breadcrumb that the given selection was made in the drop-down
+ * viewer.
+ * @param selection Selection to set to breadcrumb.
+ */
+ public void notifySelection(ISelection selection);
+
+ /**
+ * Notifies the breadcrumb that the drop-down viewer should be closed.
+ */
+ public void close();
+
+ /**
+ * Notifies the breadcrumb that the drop-down viewer's contents have
+ * changed and viewer shell should be adjusted for the new size.
+ */
+ public void updateSize();
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/TreeViewerDropDown.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/TreeViewerDropDown.java
new file mode 100644
index 000000000..617e52f2a
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/breadcrumb/TreeViewerDropDown.java
@@ -0,0 +1,300 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.breadcrumb;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Platform;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
+import org.eclipse.debug.internal.ui.DebugUIPlugin;
+import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jface.viewers.IOpenListener;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.ITreeViewerListener;
+import org.eclipse.jface.viewers.OpenEvent;
+import org.eclipse.jface.viewers.TreeExpansionEvent;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.KeyEvent;
+import org.eclipse.swt.events.KeyListener;
+import org.eclipse.swt.events.MouseEvent;
+import org.eclipse.swt.events.MouseListener;
+import org.eclipse.swt.events.MouseMoveListener;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Item;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * A breadcrumb drop-down which shows a tree viewer. It implements mouse and
+ * key listeners to handle selection and expansion behavior of the viewer.
+ * This class needs to be extended to implement
+ * {@link #createTreeViewer(Composite, int, TreePath)} to instantiate the
+ * concrete {@link TreeViewer} object.
+ *
+ * @since 3.5
+ */
+public abstract class TreeViewerDropDown {
+
+ /**
+ * Tells whether this class is in debug mode.
+ */
+ private static boolean DEBUG= DebugUIPlugin.DEBUG && "true".equalsIgnoreCase(Platform.getDebugOption("org.eclipse.debug.ui/debug/breadcrumb")); //$NON-NLS-1$//$NON-NLS-2$
+
+ /**
+ * Delay to control scrolling when the mouse pointer reaches the edge of
+ * the tree viewer.
+ */
+ private static long MOUSE_MOVE_SCROLL_DELAY = 500;
+
+ /**
+ * The breadcrumb site in which the viewer is created.
+ */
+ private IBreadcrumbDropDownSite fDropDownSite;
+
+ /**
+ * The tree viewer.
+ */
+ private TreeViewer fDropDownViewer;
+
+ /**
+ * Creates the viewer and installs the listeners.
+ *
+ * @param composite Parent control of the viewer.
+ * @param site Breadcrumb site for the viewer.
+ * @param path Path to the element for which the drop-down is being opened.
+ * @return The control created for the viewer.
+ */
+ public Control createDropDown(Composite composite, IBreadcrumbDropDownSite site, TreePath path) {
+
+ fDropDownSite = site;
+ fDropDownViewer= createTreeViewer(composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL, path);
+
+ fDropDownViewer.addOpenListener(new IOpenListener() {
+ public void open(OpenEvent event) {
+ if (DEBUG)
+ System.out.println("BreadcrumbItemDropDown.showMenu()$treeViewer>open"); //$NON-NLS-1$
+
+ openElement(event.getSelection());
+ }
+ });
+
+ final Tree tree = fDropDownViewer.getTree();
+
+ tree.addMouseListener(new MouseListener() {
+ public void mouseUp(MouseEvent e) {
+ if (DEBUG)
+ System.out.println("BreadcrumbItemDropDown.showMenu()$treeViewer>mouseUp"); //$NON-NLS-1$
+
+ if (e.button != 1)
+ return;
+
+ if ((OpenStrategy.getOpenMethod() & OpenStrategy.SINGLE_CLICK) != 0)
+ return;
+
+ TreeItem item= tree.getItem(new Point(e.x, e.y));
+ if (item == null)
+ return;
+
+ List pathElements = new LinkedList();
+ while(item != null) {
+ Object data = item.getData();
+ if (data == null) return;
+ pathElements.add(0, data);
+ item = item.getParentItem();
+ }
+
+ openElement(new TreeSelection(new TreePath(pathElements.toArray())));
+ }
+
+ public void mouseDown(MouseEvent e) {
+ }
+
+ public void mouseDoubleClick(MouseEvent e) {
+ }
+ });
+
+ tree.addMouseMoveListener(new MouseMoveListener() {
+ TreeItem fLastItem= null;
+ long fLastScrollTime = 0;
+
+ public void mouseMove(MouseEvent e) {
+ if (tree.equals(e.getSource())) {
+ Object o= tree.getItem(new Point(e.x, e.y));
+ if (o instanceof TreeItem) {
+ TreeItem currentItem= (TreeItem) o;
+ if (!o.equals(fLastItem)) {
+ fLastItem= (TreeItem) o;
+ tree.setSelection(new TreeItem[] { fLastItem });
+ } else if (System.currentTimeMillis() > (fLastScrollTime + MOUSE_MOVE_SCROLL_DELAY)) {
+ if (e.y < tree.getItemHeight() / 4)
+ {
+ // Scroll up
+ if (currentItem.getParentItem() == null) {
+ int index= tree.indexOf((TreeItem) o);
+ if (index < 1)
+ return;
+
+ fLastItem= tree.getItem(index - 1);
+ tree.setSelection(new TreeItem[] { fLastItem });
+ } else {
+ Point p= tree.toDisplay(e.x, e.y);
+ Item item= fDropDownViewer.scrollUp(p.x, p.y);
+ fLastScrollTime = System.currentTimeMillis();
+ if (item instanceof TreeItem) {
+ fLastItem= (TreeItem) item;
+ tree.setSelection(new TreeItem[] { fLastItem });
+ }
+ }
+ } else if (e.y > tree.getBounds().height - tree.getItemHeight() / 4) {
+ // Scroll down
+ if (currentItem.getParentItem() == null) {
+ int index= tree.indexOf((TreeItem) o);
+ if (index >= tree.getItemCount() - 1)
+ return;
+
+ fLastItem= tree.getItem(index + 1);
+ tree.setSelection(new TreeItem[] { fLastItem });
+ } else {
+ Point p= tree.toDisplay(e.x, e.y);
+ Item item= fDropDownViewer.scrollDown(p.x, p.y);
+ fLastScrollTime = System.currentTimeMillis();
+ if (item instanceof TreeItem) {
+ fLastItem= (TreeItem) item;
+ tree.setSelection(new TreeItem[] { fLastItem });
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ });
+
+ tree.addKeyListener(new KeyListener() {
+ public void keyPressed(KeyEvent e) {
+ if (e.keyCode == SWT.ARROW_UP) {
+ TreeItem[] selection= tree.getSelection();
+ if (selection.length != 1)
+ return;
+
+ int selectionIndex= tree.indexOf(selection[0]);
+ if (selectionIndex != 0)
+ return;
+
+ fDropDownSite.close();
+ }
+ }
+
+ public void keyReleased(KeyEvent e) {
+ }
+ });
+
+ fDropDownViewer.addTreeListener(new ITreeViewerListener() {
+ public void treeCollapsed(TreeExpansionEvent event) {
+ }
+
+ public void treeExpanded(TreeExpansionEvent event) {
+ tree.setRedraw(false);
+ new UIJob(tree.getDisplay(), IInternalDebugCoreConstants.EMPTY_STRING) {
+ { setSystem(true); }
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (!tree.isDisposed()) {
+ try {
+ fDropDownSite.updateSize();
+ } finally {
+ tree.setRedraw(true);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+
+ });
+
+ return tree;
+ }
+
+ /**
+ * Creates and returns the tree viewer.
+ *
+ * @param composite Parent control of the viewer.
+ * @param style Style flags to use in creating the tree viewer.
+ * @param path Path to the element for which the drop-down is being opened.
+ * @return The newly created tree viewer.
+ */
+ protected abstract TreeViewer createTreeViewer(Composite composite, int style, TreePath path);
+
+ /**
+ * Called when the given element was selected in the viewer. It causes the
+ * breadcrumb viewer to fire an opened event. If the viewer loses focus
+ * as a result of the open operation, then the drop-down is closed.
+ * Otherwise the selected element is expanded.
+ *
+ * @param selection The selection to open.
+ */
+ protected void openElement(ISelection selection) {
+ if (selection == null || !(selection instanceof ITreeSelection) || selection.isEmpty())
+ return;
+
+ // This might or might not open an editor
+ fDropDownSite.notifySelection(selection);
+
+ Tree tree = fDropDownViewer.getTree();
+
+ boolean treeHasFocus= !tree.isDisposed() && tree.isFocusControl();
+
+ if (DEBUG) {
+ System.out.println(" isDisposed: " + tree.isDisposed()); //$NON-NLS-1$
+ System.out.println(" shell hasFocus: " + (!tree.isDisposed() && tree.isFocusControl())); //$NON-NLS-1$
+ System.out.println(" tree hasFocus: " + treeHasFocus); //$NON-NLS-1$
+ }
+
+ if (tree.isDisposed())
+ return;
+
+ if (!treeHasFocus) {
+ fDropDownSite.close();
+ return;
+ }
+
+ toggleExpansionState( ((ITreeSelection)selection).getPaths()[0]);
+ }
+
+ private void toggleExpansionState(TreePath path) {
+ Tree tree= fDropDownViewer.getTree();
+ if (fDropDownViewer.getExpandedState(path))
+ fDropDownViewer.collapseToLevel(path, 1);
+ else {
+ tree.setRedraw(false);
+ try {
+ fDropDownViewer.expandToLevel(path, 1);
+ fDropDownSite.updateSize();
+ } finally {
+ tree.setRedraw(true);
+ }
+ }
+ }
+
+
+
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
index 3879f93c3..eb4da116d 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenCountUpdate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River Systems - Fix for viewer state save/restore [188704]
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -18,7 +19,6 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpd
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.jface.viewers.TreeViewer;
/**
* @since 3.3
@@ -48,10 +48,10 @@ class ChildrenCountUpdate extends ViewerUpdateMonitor implements IChildrenCountU
getContentProvider().setModelChildCount(elementPath, fCount);
viewCount = getContentProvider().modelToViewChildCount(elementPath, fCount);
}
- if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) {
+ if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("setChildCount(" + getElement() + ", modelCount: " + fCount + " viewCount: " + viewCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
- ((TreeViewer)(getContentProvider().getViewer())).setChildCount(elementPath, viewCount);
+ getContentProvider().getViewer().setChildCount(elementPath, viewCount);
getContentProvider().doRestore(getElementPath(), -1, true, true);
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java
index 2d9f89842..8fc6419a9 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ChildrenUpdate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River Systems - Fix for viewer state save/restore [188704]
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -48,7 +49,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda
TreeModelContentProvider provider = (TreeModelContentProvider) getContentProvider();
TreePath elementPath = getElementPath();
if (fElements != null) {
- InternalTreeModelViewer viewer = (InternalTreeModelViewer) provider.getViewer();
+ ITreeModelContentProviderTarget viewer = provider.getViewer();
for (int i = 0; i < fElements.length; i++) {
int modelIndex = fIndex + i;
Object element = fElements[i];
@@ -56,7 +57,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda
int viewIndex = provider.modelToViewIndex(elementPath, modelIndex);
if (provider.shouldFilter(elementPath, element)) {
if (provider.addFilteredIndex(elementPath, modelIndex, element)) {
- if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) {
+ if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("REMOVE(" + getElement() + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
viewer.remove(elementPath, viewIndex);
@@ -70,7 +71,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda
}
viewer.insert(elementPath, element, insertIndex);
} else {
- if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) {
+ if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("replace(" + getElement() + ", modelIndex: " + modelIndex + " viewIndex: " + viewIndex + ", " + element + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$
}
viewer.replace(elementPath, viewIndex, element);
@@ -114,7 +115,7 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda
fIndex = Math.min(fIndex, otherStart);
end = Math.max(end, otherEnd);
fLength = end - fIndex;
- if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) {
+ if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("coalesced: " + this.toString()); //$NON-NLS-1$
}
return true;
@@ -151,8 +152,8 @@ public class ChildrenUpdate extends ViewerUpdateMonitor implements IChildrenUpda
buf.append(getElement());
buf.append(" {"); //$NON-NLS-1$
buf.append(getOffset());
- buf.append(',');
- buf.append(getLength());
+ buf.append("->"); //$NON-NLS-1$
+ buf.append(getOffset() + getLength());
buf.append("}"); //$NON-NLS-1$
return buf.toString();
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java
index 9f5110b9f..be08635b3 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/HasChildrenUpdate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River Systems - Fix for viewer state save/restore [188704]
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -18,7 +19,6 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentPr
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.jface.viewers.TreePath;
-import org.eclipse.jface.viewers.TreeViewer;
/**
* @since 3.3
@@ -45,12 +45,12 @@ class HasChildrenUpdate extends ViewerUpdateMonitor implements IHasChildrenUpdat
if (!fHasChildren) {
contentProvider.clearFilters(elementPath);
}
- if (ModelContentProvider.DEBUG_CONTENT_PROVIDER) {
+ if (ModelContentProvider.DEBUG_CONTENT_PROVIDER && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("setHasChildren(" + getElement() + " >> " + fHasChildren); //$NON-NLS-1$ //$NON-NLS-2$
}
- ((TreeViewer)(contentProvider.getViewer())).setHasChildren(elementPath, fHasChildren);
+ contentProvider.getViewer().setHasChildren(elementPath, fHasChildren);
if (fHasChildren) {
- ((InternalTreeModelViewer)contentProvider.getViewer()).autoExpand(elementPath);
+ contentProvider.getViewer().autoExpand(elementPath);
}
if (elementPath.getSegmentCount() > 0) {
getContentProvider().doRestore(getElementPath(), -1, true, false);
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java
new file mode 100644
index 000000000..701ebca0d
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelContentProviderTarget.java
@@ -0,0 +1,232 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ViewerFilter;
+
+/**
+ * This interface must be implemented by the viewer which uses the
+ * {@link TreeModelContentProvider} content provider. It allows the content
+ * provider to update the viewer with information retrieved from the
+ * content, proxy, memento, and other element-based providers.
+ *
+ * @since 3.5
+ */
+public interface ITreeModelContentProviderTarget extends ITreeModelViewer {
+
+ /**
+ * Returns this viewer's filters.
+ *
+ * @return an array of viewer filters
+ * @see StructuredViewer#setFilters(ViewerFilter[])
+ */
+ public ViewerFilter[] getFilters();
+
+ /**
+ * Reveals the given element in the viewer.
+ * @param path Path to the element's parent.
+ * @param index Index of the element to be revealed.
+ */
+ public void reveal(TreePath path, int index);
+
+ /**
+ * Triggers an update of the given element's state. If multiple instances
+ * of the given element are found in the tree, they will all be updated.
+ *
+ * @param element Element to update.
+ */
+ public void update(Object element);
+
+ /**
+ * Triggers an update of the given element and its children. If
+ * multiple instances of the given element are found in the tree,
+ * they will all be updated.
+ *
+ * @param element Element to update.
+ */
+ public void refresh(Object element);
+
+ /**
+ * Triggers a full update of all the elements in the tree.
+ *
+ * @param element Element to update.
+ */
+ public void refresh();
+
+ /**
+ * Sets the given object to be the element at the given index of the given parent.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param parentOrTreePath Parent object, or a tree path of the parent element.
+ * @param index Index at which to set the new element.
+ * @param element Element object.
+ */
+ public void replace(Object parentOrTreePath, final int index, Object element);
+
+ /**
+ * Set the number of children of the given element or tree path. To set the
+ * number of children of the invisible root of the tree, you can pass the
+ * input object or an empty tree path.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementOrTreePath The element, or tree path.
+ * @param count
+ */
+ public void setChildCount(final Object elementOrTreePath, final int count);
+
+ /**
+ * Inform the viewer about whether the given element or tree path has
+ * children. Avoid calling this method if the number of children has
+ * already been set.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementOrTreePath
+ * the element, or tree path
+ * @param hasChildren
+ */
+ public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren);
+
+ /**
+ * Performs auto expand on an element at the specified path if the auto expand
+ * level dictates the element should be expanded.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementPath tree path to element to consider for expansion
+ */
+ public void autoExpand(TreePath elementPath);
+
+ /**
+ * Sets whether the node corresponding to the given element or tree path is
+ * expanded or collapsed.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementOrTreePath
+ * the element, or the tree path to the element
+ * @param expanded
+ * <code>true</code> if the node is expanded, and
+ * <code>false</code> if collapsed
+ */
+ public void setExpandedState(Object elementOrTreePath, boolean expanded);
+
+ /**
+ * Expands all ancestors of the given element or tree path so that the given
+ * element becomes visible in this viewer's tree control, and then expands
+ * the subtree rooted at the given element to the given level.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementOrTreePath
+ * the element
+ * @param level
+ * non-negative level, or <code>ALL_LEVELS</code> to expand all
+ * levels of the tree
+ */
+ public void expandToLevel(Object elementOrTreePath, int level);
+
+
+
+ /**
+ * Removes the given element from the viewer. The selection is updated if
+ * necessary.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param elementsOrTreePaths
+ * the element, or the tree path to the element
+ */
+ public void remove(Object elementOrTreePath);
+
+ /**
+ * Removes the element at the specified index of the parent. The selection is updated if required.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param parentOrTreePath the parent element, the input element, or a tree path to the parent element
+ * @param index child index
+ */
+ public void remove(Object parentOrTreePath, final int index);
+
+ /**
+ * Inserts the given element as a new child element of the given parent
+ * element at the given position. If this viewer has a sorter, the position
+ * is ignored and the element is inserted at the correct position in the
+ * sort order.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param parentElementOrTreePath
+ * the parent element, or the tree path to the parent
+ * @param element
+ * the element
+ * @param position
+ * a 0-based position relative to the model, or -1 to indicate
+ * the last position
+ */
+ public void insert(Object parentOrTreePath, Object element, int position);
+
+ /**
+ * Returns whether the candidate selection should override the current
+ * selection.
+ */
+ public boolean overrideSelection(ISelection current, ISelection candidate);
+
+ /**
+ * Returns whether the node corresponding to the given element or tree path
+ * is expanded or collapsed.
+ *
+ * @param elementOrTreePath
+ * the element
+ * @return <code>true</code> if the node is expanded, and
+ * <code>false</code> if collapsed
+ */
+ public boolean getExpandedState(Object elementOrTreePath);
+
+ /**
+ * Returns the child count of the element at the given path.
+ */
+ public int getChildCount(TreePath path);
+
+ /**
+ * Returns the element which is a child of the element at the
+ * given path, with the given index.
+ */
+ public Object getChildElement(TreePath path, int index);
+
+ /**
+ * Returns the tree path of the element that is at the top of the
+ * viewer.
+ */
+ public TreePath getTopElementPath();
+
+ /**
+ * Finds the index of the given element with a parent of given path.
+ *
+ * @return The element's index, or -1 if not found.
+ */
+ public int findElementIndex(TreePath parentPath, Object element);
+
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java
new file mode 100644
index 000000000..add8a89be
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelLabelProviderTarget.java
@@ -0,0 +1,54 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.swt.graphics.FontData;
+import org.eclipse.swt.graphics.RGB;
+
+/**
+ * This interface must be implemented by the viewer which uses the
+ * {@link TreeModelLabelProvider} label provider. It allows the label
+ * provider to update the viewer with information retrieved from the
+ * element-based label providers.
+ *
+ * @since 3.5
+ */
+public interface ITreeModelLabelProviderTarget extends ITreeModelViewer {
+
+ /**
+ * Sets the element's display information.
+ * <p>
+ * This method should only be called by the viewer framework.
+ * </p>
+ *
+ * @param path Element path.
+ * @param numColumns Number of columns in the data.
+ * @param labels Array of labels. The array cannot to be
+ * <code>null</code>, but values within the array may be.
+ * @param images Array of image descriptors, may be <code>null</code>.
+ * @param fontDatas Array of fond data objects, may be <code>null</code>.
+ * @param foregrounds Array of RGB values for foreground colors, may be
+ * <code>null</code>.
+ * @param backgrounds Array of RGB values for background colors, may be
+ * <code>null</code>.
+ */
+ public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images, FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds);
+
+ /**
+ * 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();
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java
new file mode 100644
index 000000000..48917bf6c
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ITreeModelViewer.java
@@ -0,0 +1,177 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionProvider;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.swt.widgets.Display;
+
+/**
+ * Interface of an tree model viewer. It declares the common methods for the
+ * JFace-based {@link TreeModelViewer} and the UI-less
+ * {@link VirtualTreeModelViewer}.
+ *
+ * @since 3.5
+ */
+public interface ITreeModelViewer extends ISelectionProvider {
+
+ /**
+ * Constant indicating that all levels of the tree should be expanded or
+ * collapsed.
+ *
+ * @see #setAutoExpandLevel(int)
+ * @see #getAutoExpandLevel()
+ */
+ public static final int ALL_LEVELS = -1;
+
+ /**
+ * Returns the Display object that this viewer is in. The
+ * display object can be used by clients to access the display thread
+ * to call the viewer methods.
+ */
+ public Display getDisplay();
+
+ /**
+ * Returns this viewer's presentation context.
+ *
+ * @return presentation context
+ */
+ public IPresentationContext getPresentationContext();
+
+ /**
+ * Returns the current input of this viewer, or <code>null</code>
+ * if none. The viewer's input provides the "model" for the viewer's
+ * content.
+ */
+ public Object getInput();
+
+ /**
+ * Sets the input of this viewer. Setting the input resets the
+ * viewer's contents and triggers an update starting at the input
+ * element.
+ * @param object Input element, or <code>null</code> if none.
+ */
+ public void setInput(Object object);
+
+ /**
+ * Returns the current selection in viewer.
+ */
+ public ISelection getSelection();
+
+ /**
+ * Sets a new selection for this viewer and optionally makes it visible.
+ *
+ * @param selection the new selection
+ * @param reveal <code>true</code> if the selection is to be made
+ * visible, and <code>false</code> otherwise
+ */
+ public void setSelection(ISelection selection, boolean reveal);
+
+ /**
+ * Returns the auto-expand level.
+ *
+ * @return non-negative level, or <code>ALL_LEVELS</code> if all levels of
+ * the tree are expanded automatically
+ * @see #setAutoExpandLevel
+ */
+ public int getAutoExpandLevel();
+
+ /**
+ * Sets the auto-expand level to be used when the input of the viewer is set
+ * using {@link #setInput(Object)}. The value 0 means that there is no
+ * auto-expand; 1 means that the invisible root element is expanded (since
+ * most concrete implementations do not show the root element, there is usually
+ * no practical difference between using the values 0 and 1); 2 means that
+ * top-level elements are expanded, but not their children; 3 means that
+ * top-level elements are expanded, and their children, but not
+ * grandchildren; and so on.
+ * <p>
+ * The value <code>ALL_LEVELS</code> means that all subtrees should be
+ * expanded.
+ * </p>
+ *
+ * @param level
+ * non-negative level, or <code>ALL_LEVELS</code> to expand all
+ * levels of the tree
+ */
+ public void setAutoExpandLevel(int level);
+
+ /**
+ * Returns the label data for the given element and for the given column,
+ * Returns <code>null</code> if the given element is not found or is not
+ * materialized in the virtual viewer. Clients may listen to label update
+ * events to be notified when element labels are updated.
+ *
+ * @param path Path of the element.
+ * @param columnIdx ID of the column for which to return the label data.
+ * @return Label object containing the label information. Can be
+ * <code>null</code> if the given element is not found or is not
+ * materialized in the virtual viewer.
+ */
+ public ViewerLabel getElementLabel(TreePath path, String columnId);
+
+ /**
+ * Registers the specified listener for view update notifications.
+ */
+ public void addViewerUpdateListener(IViewerUpdateListener listener);
+
+ /**
+ * Removes the specified listener from update notifications.
+ */
+ public void removeViewerUpdateListener(IViewerUpdateListener listener);
+
+ /**
+ * Registers the specified listener for view label update notifications.
+ */
+ public void addLabelUpdateListener(ILabelUpdateListener listener);
+
+ /**
+ * Removes the specified listener from view label update notifications.
+ */
+ public void removeLabelUpdateListener(ILabelUpdateListener listener);
+
+ /**
+ * Registers the given listener for model delta notification.
+ * This listener is called immediately after the viewer processes
+ * the delta.
+ */
+ public void addModelChangedListener(IModelChangedListener listener);
+
+ /**
+ * Removes the given listener from model delta notification.
+ */
+ public void removeModelChangedListener(IModelChangedListener listener);
+
+ /**
+ * Writes state information into a delta for the sub-tree at the given
+ * path. It adds delta nodes and IModelDelta.EXPAND and IModelDelta.SELECT
+ * as it parses the sub-tree.
+ * @param path Path where to start saving the state.
+ * @param delta The delta where the state is to be saved.
+ */
+ public void saveElementState(TreePath path, ModelDelta delta);
+
+ /**
+ * Causes the viewer to process the given delta as if it came from a
+ * model proxy. This method is intended to be used to restore state
+ * saved using {@link #saveElementState(TreePath, ModelDelta)}.
+ *
+ * @param delta Delta to process.
+ */
+ public void updateViewer(IModelDelta delta);
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
index 2cf5a345e..6a3ff19da 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalTreeModelViewer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -7,17 +7,22 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
import java.util.ArrayList;
+import java.util.Collection;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.Map.Entry;
import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.core.commands.Request;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
@@ -27,20 +32,25 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementEditor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.CellEditor;
+import org.eclipse.jface.viewers.IBaseLabelProvider;
+import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.viewers.ICellModifier;
import org.eclipse.jface.viewers.IContentProvider;
import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
import org.eclipse.jface.viewers.ISelection;
-import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.ViewerLabel;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.ControlEvent;
import org.eclipse.swt.events.ControlListener;
@@ -69,7 +79,9 @@ import org.eclipse.ui.IMemento;
*
* @since 3.3
*/
-public class InternalTreeModelViewer extends TreeViewer {
+public class InternalTreeModelViewer extends TreeViewer
+ implements ITreeModelViewer, ITreeModelContentProviderTarget, ITreeModelLabelProviderTarget
+{
private IPresentationContext fContext;
@@ -542,6 +554,14 @@ public class InternalTreeModelViewer extends TreeViewer {
* @return column labels
*/
public String[] getLabel() {
+ if (fLabel == null) {
+ String[] visibleColumns = getVisibleColumns();
+ String[] label = new String[visibleColumns == null ? 1 : visibleColumns.length];
+ for (int i = 0; i < label.length; i++) {
+ label[i] = fItem.getText(i);
+ }
+ return label;
+ }
return fLabel.fText;
}
@@ -982,6 +1002,10 @@ public class InternalTreeModelViewer extends TreeViewer {
fContext = context;
setContentProvider(createContentProvider());
setLabelProvider(createLabelProvider());
+
+ if ((style & SWT.POP_UP) != 0) {
+ ((ModelContentProvider)getContentProvider()).setSuppressModelControlDeltas(true);
+ }
}
/**
@@ -1023,7 +1047,7 @@ public class InternalTreeModelViewer extends TreeViewer {
* @param item
*/
private void preserveItem(TreeItem item) {
- Object[] labels = (Object[]) item.getData(LabelUpdate.PREV_LABEL_KEY);
+ Object[] labels = (Object[]) item.getData(PREV_LABEL_KEY);
if (labels != null) {
for (int i = 0; i < labels.length; i++) {
if (labels[i] != null) {
@@ -1031,25 +1055,25 @@ public class InternalTreeModelViewer extends TreeViewer {
}
}
}
- Object[] images = (Object[]) item.getData(LabelUpdate.PREV_IMAGE_KEY);
+ Object[] images = (Object[]) item.getData(PREV_IMAGE_KEY);
if (images != null) {
for (int i = 0; i < images.length; i++) {
item.setImage(i, (Image) images[i]);
}
}
- Object[] fonts = (Object[]) item.getData(LabelUpdate.PREV_FONT_KEY);
+ Object[] fonts = (Object[]) item.getData(PREV_FONT_KEY);
if (fonts != null) {
for (int i = 0; i < fonts.length; i++) {
item.setFont(i, (Font) fonts[i]);
}
}
- Object[] foregrounds = (Object[]) item.getData(LabelUpdate.PREV_FOREGROUND_KEY);
+ Object[] foregrounds = (Object[]) item.getData(PREV_FOREGROUND_KEY);
if (foregrounds != null) {
for (int i = 0; i < foregrounds.length; i++) {
item.setForeground(i, (Color) foregrounds[i]);
}
}
- Object[] backgrounds = (Object[]) item.getData(LabelUpdate.PREV_BACKGROUND_KEY);
+ Object[] backgrounds = (Object[]) item.getData(PREV_BACKGROUND_KEY);
if (backgrounds != null) {
for (int i = 0; i < backgrounds.length; i++) {
item.setBackground(i, (Color) backgrounds[i]);
@@ -1066,16 +1090,18 @@ public class InternalTreeModelViewer extends TreeViewer {
protected void handleInvalidSelection(ISelection selection, ISelection newSelection) {
IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(selection, getPresentationContext());
if (selectionPolicy != null) {
- ISelection temp = newSelection;
- newSelection = selectionPolicy.replaceInvalidSelection(selection, newSelection);
- if (temp != newSelection) {
- if (newSelection == null) {
- newSelection = new StructuredSelection();
- }
- // call super.setSelection(...) to avoid asking the selection policy
- // if the selection should be overridden
- super.setSelection(newSelection, false);
- return;
+ while (!selection.equals(newSelection)) {
+ ISelection temp = newSelection;
+ selection = selectionPolicy.replaceInvalidSelection(selection, newSelection);
+ if (selection == null) {
+ selection = TreeSelection.EMPTY;
+ }
+ if (!temp.equals(selection)) {
+ setSelectionToWidget(selection, false);
+ newSelection = getSelection();
+ } else {
+ break;
+ }
}
}
super.handleInvalidSelection(selection, newSelection);
@@ -1615,7 +1641,7 @@ public class InternalTreeModelViewer extends TreeViewer {
* @param curr
* @return
*/
- protected boolean overrideSelection(ISelection current, ISelection candidate) {
+ public boolean overrideSelection(ISelection current, ISelection candidate) {
IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(current, getPresentationContext());
if (selectionPolicy == null) {
return true;
@@ -1710,7 +1736,12 @@ public class InternalTreeModelViewer extends TreeViewer {
return;
}
- ((TreeModelLabelProvider)getLabelProvider()).update(getTreePathFromItem(item), getViewerRowFromItem(treeItem));
+ if ( !((TreeModelLabelProvider)getLabelProvider()).update(getTreePathFromItem(item)) ) {
+ if (element instanceof String) {
+ item.setData(PREV_LABEL_KEY, new String[] { (String)element } );
+ }
+ }
+
// As it is possible for user code to run the event
// loop check here.
@@ -1735,12 +1766,20 @@ public class InternalTreeModelViewer extends TreeViewer {
return new VirtualModel(root, childIndexes);
}
- void addLabelUpdateListener(ILabelUpdateListener listener) {
- ((TreeModelLabelProvider)getLabelProvider()).addLabelUpdateListener(listener);
+ public void addLabelUpdateListener(ILabelUpdateListener listener) {
+ IBaseLabelProvider labelProvider = getLabelProvider();
+ if (labelProvider instanceof TreeModelLabelProvider) {
+ ((TreeModelLabelProvider)labelProvider).addLabelUpdateListener(listener);
+ }
}
- void removeLabelUpdateListener(ILabelUpdateListener listener) {
- ((TreeModelLabelProvider)getLabelProvider()).removeLabelUpdateListener(listener);
+ public void removeLabelUpdateListener(ILabelUpdateListener listener) {
+ if (!getControl().isDisposed()) {
+ IBaseLabelProvider labelProvider = getLabelProvider();
+ if (labelProvider instanceof TreeModelLabelProvider) {
+ ((TreeModelLabelProvider)labelProvider).removeLabelUpdateListener(listener);
+ }
+ }
}
/**
@@ -1750,7 +1789,7 @@ public class InternalTreeModelViewer extends TreeViewer {
* @param path tree path
* @return item or <code>null</code>
*/
- Widget findItem(TreePath path) {
+ public Widget findItem(TreePath path) {
if (path.getSegmentCount() == 0) {
return getTree();
}
@@ -1902,12 +1941,305 @@ public class InternalTreeModelViewer extends TreeViewer {
*
* @param elementPath tree path to element to consider for expansion
*/
- void autoExpand(TreePath elementPath) {
+ public void autoExpand(TreePath elementPath) {
int level = getAutoExpandLevel();
- if (level > 0 || level == ALL_LEVELS) {
- if (level == ALL_LEVELS || level >= elementPath.getSegmentCount()) {
+ if (level > 0 || level == ITreeModelViewer.ALL_LEVELS) {
+ if (level == ITreeModelViewer.ALL_LEVELS || level >= elementPath.getSegmentCount()) {
expandToLevel(elementPath, 1);
}
}
}
+
+ public int findElementIndex(TreePath parentPath, Object element) {
+ Widget parentItem = findItem(parentPath);
+ if (parentItem != null) {
+ Item[] children = getChildren(parentItem);
+ for (int i = 0; i < children.length; i++) {
+ Item item = children[i];
+ Object data = item.getData();
+ if ( (element != null && element.equals(data)) || (element == null && data == null) ) {
+ return i;
+ }
+ }
+ }
+ return -1;
+ }
+
+ public Display getDisplay() {
+ Control control = getControl();
+ if (control != null) {
+ return control.getDisplay();
+ }
+ return null;
+ }
+
+ protected static final String[] STATE_PROPERTIES = new String[]{ IBasicPropertyConstants.P_TEXT, IBasicPropertyConstants.P_IMAGE };
+
+ public void update(Object element) {
+ update(element, STATE_PROPERTIES);
+ }
+
+ /**
+ * Label data cache keys
+ * TODO: workaround for bug 159461
+ */
+ static String PREV_LABEL_KEY = "PREV_LABEL_KEY"; //$NON-NLS-1$
+ static String PREV_IMAGE_KEY = "PREV_IMAGE_KEY"; //$NON-NLS-1$
+ static String PREV_FONT_KEY = "PREV_FONT_KEY"; //$NON-NLS-1$
+ static String PREV_FOREGROUND_KEY = "PREV_FOREGROUND_KEY"; //$NON-NLS-1$
+ static String PREV_BACKGROUND_KEY = "PREV_BACKGROUND_KEY"; //$NON-NLS-1$
+
+ public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] imageDescriptors,
+ FontData[] fontDatas, RGB[] _foregrounds, RGB[] _backgrounds)
+ {
+ Widget widget = findItem(path);
+ String[] columnIds = getVisibleColumns();
+
+ if (widget != null && widget instanceof TreeItem && !widget.isDisposed()) {
+ TreeItem item = (TreeItem)widget;
+ /*Object data = item.getData();
+ int itemCount = item.getItemCount();
+ item.clearAll(false);
+ item.setData(data);
+ item.setItemCount(itemCount);*/
+
+ for (int i=0; i<numColumns; i++){
+ // text might be null if the launch has been terminated
+ item.setText(i,(labels[i] == null ? IInternalDebugCoreConstants.EMPTY_STRING : labels[i]));
+ }
+ item.setData(PREV_LABEL_KEY, labels);
+
+ if (imageDescriptors == null) {
+ for (int i=0; i<numColumns; i++){
+ item.setImage(i,null);
+ }
+ item.setData(PREV_IMAGE_KEY, null);
+ } else {
+ Image[] images = new Image[imageDescriptors.length];
+ for (int i = 0; i < imageDescriptors.length; i++) {
+ images[i] = ((TreeModelLabelProvider)getLabelProvider()).getImage(imageDescriptors[i]);
+ }
+ if (columnIds == null) {
+ item.setImage(images[0]);
+ } else {
+ item.setImage(images);
+ }
+ item.setData(PREV_IMAGE_KEY, images);
+ }
+
+ if (_foregrounds == null) {
+ for (int i=0; i<numColumns; i++){
+ item.setForeground(i,null);
+ }
+ item.setData(PREV_FOREGROUND_KEY, null);
+ } else {
+ Color[] foregrounds = new Color[_foregrounds.length];
+ for (int i = 0; i< foregrounds.length; i++) {
+ foregrounds[i] = ((TreeModelLabelProvider)getLabelProvider()).getColor(_foregrounds[i]);
+ }
+ if (columnIds == null) {
+ item.setForeground(0,foregrounds[0]);
+ } else {
+ for (int i = 0; i< foregrounds.length; i++) {
+ item.setForeground(i, foregrounds[i]);
+ }
+ }
+ item.setData(PREV_FOREGROUND_KEY, foregrounds);
+ }
+
+ if (_backgrounds == null) {
+ for (int i=0; i<numColumns; i++){
+ item.setBackground(i,null);
+ }
+ item.setData(PREV_BACKGROUND_KEY, null);
+ } else {
+ Color[] backgrounds = new Color[_backgrounds.length];
+ for (int i = 0; i< backgrounds.length; i++) {
+ backgrounds[i] = ((TreeModelLabelProvider)getLabelProvider()).getColor(_backgrounds[i]);
+ }
+ if (columnIds == null) {
+ item.setBackground(0,backgrounds[0]);
+ } else {
+ for (int i = 0; i< backgrounds.length; i++) {
+ item.setBackground(i, backgrounds[i]);
+ }
+ }
+ item.setData(PREV_BACKGROUND_KEY, backgrounds);
+ }
+
+ if (fontDatas == null) {
+ for (int i=0; i<numColumns; i++){
+ item.setFont(i,null);
+ }
+ item.setData(PREV_FONT_KEY, null);
+ } else {
+ Font[] fonts = new Font[fontDatas.length];
+ for (int i = 0; i < fontDatas.length; i++) {
+ fonts[i] = ((TreeModelLabelProvider)getLabelProvider()).getFont(fontDatas[i]);
+ }
+ if (columnIds == null) {
+ item.setFont(0,fonts[0]);
+ } else {
+ for (int i = 0; i < fonts.length; i++) {
+ item.setFont(i, fonts[i]);
+ }
+ }
+ item.setData(PREV_FONT_KEY, fonts);
+ }
+ }
+ }
+
+ public ViewerLabel getElementLabel(TreePath path, String columnId) {
+ if (path.getSegmentCount() == 0) {
+ return null;
+ }
+
+ int columnIdx = -1;
+ String[] visibleColumns = getVisibleColumns();
+ if (columnId != null && visibleColumns != null) {
+ int i = 0;
+ for (i = 0; i < visibleColumns.length; i++) {
+ if (columnId.equals(getVisibleColumns()[i])) {
+ columnIdx = i;
+ break;
+ }
+ }
+ if (i == visibleColumns.length) {
+ return null;
+ }
+ } else {
+ columnIdx = 0;
+ }
+ TreeItem item = (TreeItem)findItem(path);
+
+ if (item != null) {
+ ViewerLabel label = new ViewerLabel(item.getText(columnIdx), item.getImage(columnIdx));
+ label.setFont(item.getFont(columnIdx));
+ label.setBackground(item.getBackground(columnIdx));
+ label.setForeground(item.getForeground(columnIdx));
+ return label;
+ }
+ return null;
+ }
+
+ public void reveal(TreePath path, int index) {
+ Widget item = findItem(path);
+ TreeItem[] children = null;
+ if (item instanceof TreeItem) {
+ children = ((TreeItem)item).getItems();
+ } else if (item instanceof Tree) {
+ children = ((Tree)item).getItems();
+ }
+ if (children != null && index < children.length) {
+ getTree().setTopItem(children[index]);
+ }
+ }
+
+ public int getChildCount(TreePath path) {
+ if (path.getSegmentCount() == 0) {
+ return ((Tree)getControl()).getItemCount();
+ } else {
+ Widget[] items = internalFindItems(path);
+ if (items.length > 0) {
+ if (items[0] instanceof TreeItem) {
+ return ((TreeItem)items[0]).getItemCount();
+ }
+ }
+ }
+ return -1;
+ }
+
+ public Object getChildElement(TreePath path, int index) {
+ TreeItem childItem = ((Tree)getControl()).getItem(index);
+ if (path.getSegmentCount() == 0) {
+ Tree tree = (Tree)getControl();
+ try {
+ childItem = tree.getItem(index);
+ } catch (IllegalArgumentException e) {}
+ } else {
+ try {
+ Widget[] items = internalFindItems(path);
+ if (items.length > 0) {
+ if (items[0] instanceof TreeItem) {
+ childItem = ((TreeItem)items[0]).getItem(index);
+ }
+ }
+ } catch (IllegalArgumentException e) {}
+ }
+ if (childItem != null) {
+ return childItem.getData();
+ }
+ return null;
+ }
+
+ public TreePath getTopElementPath() {
+ TreeItem topItem = ((Tree)getControl()).getTopItem();
+ if (topItem != null && topItem.getData() != null) {
+ return getTreePathFromItem(topItem);
+ }
+ return null;
+ }
+
+ public void saveElementState(TreePath path, ModelDelta delta) {
+ Tree tree = (Tree) getControl();
+ TreeItem[] selection = tree.getSelection();
+ Set set = new HashSet();
+ for (int i = 0; i < selection.length; i++) {
+ set.add(selection[i]);
+ }
+
+ TreeItem[] items = null;
+ Widget w = internalGetWidgetToSelect(path);
+ if (w instanceof Tree) {
+ delta.setChildCount(((ModelContentProvider)getContentProvider()).viewToModelCount(path, tree.getItemCount()));
+ delta.setFlags(delta.getFlags() | IModelDelta.EXPAND);
+ items = tree.getItems();
+ } else if (w instanceof TreeItem) {
+ TreeItem item = (TreeItem)w;
+ delta.setChildCount(((ModelContentProvider)getContentProvider()).viewToModelCount(path, item.getItemCount()));
+ if (item.getExpanded()) {
+ delta.setFlags(delta.getFlags() | IModelDelta.EXPAND);
+ }
+ if (set.contains(item)) {
+ delta.setFlags(delta.getFlags() | IModelDelta.SELECT);
+ }
+ items = ((TreeItem)w).getItems();
+ }
+ if (items != null) {
+ for (int i = 0; i < items.length; i++) {
+ doSaveElementState(path, delta, items[i], set, i);
+ }
+ }
+ }
+
+ private void doSaveElementState(TreePath parentPath, ModelDelta delta, TreeItem item, Collection set, int index) {
+ 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;
+ }
+ int modelIndex = ((ModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, index);
+ TreePath elementPath = parentPath.createChildPath(element);
+ int numChildren = ((ModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount());
+ ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren);
+ if (expanded) {
+ TreeItem[] items = item.getItems();
+ for (int i = 0; i < items.length; i++) {
+ doSaveElementState(elementPath, childDelta, items[i], set, i);
+ }
+ }
+ }
+ }
+ }
+
+ public void updateViewer(IModelDelta delta) {
+ ((ModelContentProvider)getContentProvider()).updateNodes(new IModelDelta[] { delta }, true);
+ }
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java
new file mode 100644
index 000000000..b0e836ccf
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/InternalVirtualTreeModelViewer.java
@@ -0,0 +1,1373 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import org.eclipse.debug.internal.ui.viewers.model.VirtualItem.Index;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentation;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IColumnPresentationFactory;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelChangedListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.PresentationContext;
+import org.eclipse.jface.resource.ImageDescriptor;
+import org.eclipse.jface.viewers.IContentProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+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.jface.viewers.ViewerLabel;
+import org.eclipse.swt.SWT;
+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.Control;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IMemento;
+
+/**
+ * A tree model viewer without a UI component.
+ * @since 3.5
+ */
+public class InternalVirtualTreeModelViewer extends Viewer
+ implements VirtualTree.IVirtualItemListener,
+ ITreeModelViewer,
+ ITreeModelContentProviderTarget,
+ ITreeModelLabelProviderTarget
+{
+
+ /**
+ * 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$
+
+ /**
+ * Item's tree path cache
+ */
+ private static final String TREE_PATH_KEY = "TREE_PATH_KEY"; //$NON-NLS-1$
+
+ /**
+ * The display that this virtual tree viewer is associated with. It is used
+ * for access to the UI thread.
+ */
+ private Display fDisplay;
+
+ /**
+ * The object that allows the model to identify what this view
+ * is presenting.
+ */
+ private IPresentationContext fContext;
+
+ /**
+ * Input into the viewer.
+ */
+ private Object fInput;
+
+ /**
+ * The tree of items in this viewer.
+ */
+ private VirtualTree fTree;
+
+ /**
+ * Mapping of data elements in the tree to the items that hold them. The
+ * tree may contain the same element in several places, so the map values
+ * are lists.
+ */
+ private Map fItemsMap = new HashMap();
+
+ /**
+ * Whether to notify the content provider when an element is unmapped.
+ * Used to suppress the notification during an associate operation.
+ */
+ private boolean fNotifyUnmap = true;
+
+ /**
+ * The label provider, must be the tree model provider.
+ */
+ private TreeModelLabelProvider fLabelProvider;
+
+ /**
+ * The content provider must be a tree model provider.
+ */
+ private TreeModelContentProvider fContentProvider;
+
+ /**
+ * Flag indicating whether the viewer is currently executing an operation
+ * at the end of which the selection will be restored.
+ */
+ private boolean fPreservingSelecction;
+
+ /**
+ * Flag indicating that the selection should not be restored at the end
+ * of a preserving-selection operation.
+ */
+ private boolean fRestoreSelection;
+
+ /**
+ * Level to which the tree should automatically expand elements.
+ * <code>-1<code> indicates that all levels shoudl be expanded.
+ */
+ private int fAutoExpandToLevel = 0;
+
+ /**
+ * 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 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();
+
+ public InternalVirtualTreeModelViewer(Display display, int style, IPresentationContext context) {
+ fDisplay = display;
+ fContext = context;
+ fTree = new VirtualTree(style);
+ fTree.addItemListener(this);
+
+ fContentProvider = new TreeModelContentProvider();
+ fLabelProvider = new TreeModelLabelProvider(this);
+
+ if ((style & SWT.POP_UP) != 0) {
+ ((ModelContentProvider)getContentProvider()).setSuppressModelControlDeltas(true);
+ }
+ }
+
+ public Object getInput() {
+ return fInput;
+ }
+
+ public Control getControl() {
+ // The virtual viewer does not have an SWT control associated with it.
+ // Fortunately this method is not used by the base Viewer class.
+ return null;
+ }
+
+ public Display getDisplay() {
+ return fDisplay;
+ }
+
+ public void setInput(Object input) {
+ Object oldInput = fInput;
+ getContentProvider().inputChanged(this, oldInput, input);
+ fInput = input;
+ fTree.setData(fInput);
+ inputChanged(oldInput, fInput);
+ refresh();
+ }
+
+ public void replace(Object parentElementOrTreePath, final int index, Object element) {
+ VirtualItem[] selectedItems = fTree.getSelection();
+ TreeSelection selection = (TreeSelection) getSelection();
+ VirtualItem[] itemsToDisassociate;
+ if (parentElementOrTreePath instanceof TreePath) {
+ TreePath elementPath = ((TreePath) parentElementOrTreePath).createChildPath(element);
+ itemsToDisassociate = findItems(elementPath);
+ } else {
+ itemsToDisassociate = findItems(element);
+ }
+
+ VirtualItem[] parentItems = findItems(parentElementOrTreePath);
+ for (int i = 0; i < parentItems.length; i++) {
+ VirtualItem parentItem = parentItems[i];
+ if (index < parentItem.getItemCount()) {
+ VirtualItem item = parentItem.getItem(new Index(index));
+ selection = adjustSelectionForReplace(selectedItems, selection, item, element, parentItem.getData());
+ // disassociate any different item that represents the
+ // same element under the same parent (the tree)
+ for (int j = 0; j < itemsToDisassociate.length; j++) {
+ VirtualItem itemToDisassociate = itemsToDisassociate[j];
+ if (itemToDisassociate != item && itemsToDisassociate[j].getParent() == parentItem) {
+ disassociate(itemToDisassociate);
+ itemToDisassociate.getParent().clear(itemToDisassociate.getIndex());
+ }
+ }
+ //Object oldData = item.getData();
+ associate(element, item);
+ doUpdate(item);
+ }
+ }
+ // Restore the selection if we are not already in a nested
+ // preservingSelection:
+ if (!fPreservingSelecction) {
+ internalSetSelection(selection, false);
+ // send out notification if old and new differ
+ ISelection newSelection = getSelection();
+ if (!newSelection.equals(selection)) {
+ handleInvalidSelection(selection, newSelection);
+ }
+ }
+ fTree.validate();
+ }
+
+ VirtualTree getTree() {
+ return fTree;
+ }
+
+ public void insert(Object parentOrTreePath, Object element, int position) {
+ if (parentOrTreePath instanceof TreePath) {
+ VirtualItem parentItem = findItem((TreePath) parentOrTreePath);
+ if (parentItem != null) {
+ VirtualItem item = parentItem.addItem(position);
+ item.setData(element);
+
+ }
+ } else {
+ // TODO: Implement insert() for element
+ }
+ fTree.validate();
+ }
+
+ public void remove(final Object parentOrTreePath, final int index) {
+ final List oldSelection = new LinkedList(Arrays
+ .asList(((TreeSelection) getSelection()).getPaths()));
+ preservingSelection(new Runnable() {
+ public void run() {
+ TreePath removedPath = null;
+ VirtualItem[] parentItems = findItems(parentOrTreePath);
+ for (int i = 0; i < parentItems.length; i++) {
+ VirtualItem parentItem = parentItems[i];
+ if (parentItem.isDisposed())
+ continue;
+ if (index < parentItem.getItemCount()) {
+
+ VirtualItem item =parentItem.getItem(new VirtualItem.Index(index));
+ if (item.getData() != null) {
+ removedPath = getTreePathFromItem(item);
+ disassociate(item);
+ }
+ parentItem.remove(item.getIndex());
+ }
+ }
+
+ if (removedPath != null) {
+ boolean removed = false;
+ for (Iterator it = oldSelection.iterator(); it.hasNext();) {
+ TreePath path = (TreePath) it.next();
+ if (path.startsWith(removedPath, null)) {
+ it.remove();
+ removed = true;
+ }
+ }
+ if (removed) {
+ setSelection(
+ new TreeSelection((TreePath[]) oldSelection.toArray(new TreePath[oldSelection.size()])),
+ false);
+ }
+ }
+ }
+ });
+ }
+
+ public void remove(Object elementOrPath) {
+ if (elementOrPath.equals(getInput()) || TreePath.EMPTY.equals(elementOrPath)) {
+ setInput(null);
+ return;
+ }
+
+ VirtualItem[] items = findItems(elementOrPath);
+ if (items.length > 0) {
+ for (int j = 0; j < items.length; j++) {
+ disassociate(items[j]);
+ items[j].getParent().remove(items[j].getIndex());
+ }
+ }
+ }
+
+ private TreeSelection adjustSelectionForReplace(VirtualItem[] selectedItems, TreeSelection selection,
+ VirtualItem item, Object element, Object parentElement)
+ {
+ if (item.getData() != null || selectedItems.length == selection.size() || parentElement == null) {
+ // Don't do anything - we are not seeing an instance of bug 185673
+ return selection;
+ }
+ for (int i = 0; i < selectedItems.length; i++) {
+ if (item == selectedItems[i]) {
+ // The current item was selected, but its data is null.
+ // The data will be replaced by the given element, so to keep
+ // it selected, we have to add it to the selection.
+ TreePath[] originalPaths = selection.getPaths();
+ int length = originalPaths.length;
+ TreePath[] paths = new TreePath[length + 1];
+ System.arraycopy(originalPaths, 0, paths, 0, length);
+ // set the element temporarily so that we can call getTreePathFromItem
+ item.setData(element);
+ paths[length] = getTreePathFromItem(item);
+ item.setData(null);
+ return new TreeSelection(paths, selection.getElementComparer());
+ }
+ }
+ // The item was not selected, return the given selection
+ return selection;
+ }
+
+
+ public void reveal(TreePath path, final int index) {
+ // TODO: implement reveal()
+ }
+
+ public int findElementIndex(TreePath parentPath, Object element) {
+ VirtualItem parentItem = findItem(parentPath);
+ if (parentItem != null) {
+ VirtualItem item = parentItem.findItem(element);
+ if (item != null) {
+ return item.getIndex().intValue();
+ }
+ }
+ return -1;
+ }
+
+ private TreeModelLabelProvider getLabelProvider() {
+ return fLabelProvider;
+ }
+
+ private TreeModelContentProvider getContentProvider() {
+ return fContentProvider;
+ }
+
+ public static int ALL_LEVELS = -1;
+
+ public void refresh() {
+ refresh(fTree);
+ getTree().validate();
+ }
+
+ public void refresh(Object element) {
+ VirtualItem[] items = findItems(element);
+ for (int i = 0; i < items.length; i++) {
+ refresh(items[i]);
+ getTree().validate(items[i]);
+ }
+ }
+
+ private void refresh(VirtualItem item) {
+ item.setNeedsCountUpdate();
+ item.setNeedsLabelUpdate();
+ if (item.getParent() == null) {
+ virtualLazyUpdateChildCount(item);
+ } else if (item.getExpanded()) {
+ virtualLazyUpdateChildCount(item);
+ } else if (item.getData() != null) {
+ virtualLazyUpdateHasChildren(item);
+ }
+
+ VirtualItem[] items = item.getItems();
+ for (int i = 0; i < items.length; i++) {
+ items[i].setNeedsDataUpdate();
+ refresh(items[i]);
+ }
+ }
+
+
+ protected void inputChanged(Object input, Object oldInput) {
+ resetColumns(input);
+ }
+
+ public int getAutoExpandLevel() {
+ return fAutoExpandToLevel;
+ }
+
+ public void setAutoExpandLevel(int level) {
+ fAutoExpandToLevel = level;
+ }
+
+ private VirtualItem findItem(TreePath path) {
+ VirtualItem item = fTree;
+ if (path.getSegmentCount() == 0) {
+ return fTree;
+ }
+
+ for (int i = 0; item != null && i < path.getSegmentCount(); i++) {
+ Object segment = path.getSegment(i);
+ item = item.findItem(segment);
+ }
+ return item;
+ }
+
+ static private final VirtualItem[] EMPTY_ITEMS_ARRAY = new VirtualItem[0];
+
+ private VirtualItem[] findItems(Object elementOrTreePath) {
+ if (elementOrTreePath instanceof TreePath) {
+ VirtualItem item = findItem((TreePath) elementOrTreePath);
+ return item == null ? EMPTY_ITEMS_ARRAY : new VirtualItem[] { item };
+ } else if (getInput().equals(elementOrTreePath)) {
+ return new VirtualItem[] { getTree() };
+ } else {
+ List itemsList = (List) fItemsMap.get(elementOrTreePath);
+ if (itemsList == null) {
+ return EMPTY_ITEMS_ARRAY;
+ } else {
+ return (VirtualItem[]) itemsList.toArray(new VirtualItem[itemsList.size()]);
+ }
+ }
+ }
+
+ public void setElementData(TreePath path, int numColumns, String[] labels, ImageDescriptor[] images,
+ FontData[] fontDatas, RGB[] foregrounds, RGB[] backgrounds) {
+ VirtualItem item = findItem(path);
+ if (item != null) {
+ item.setData(VirtualItem.LABEL_KEY, labels);
+ item.setData(VirtualItem.IMAGE_KEY, images);
+ item.setData(VirtualItem.FOREGROUND_KEY, foregrounds);
+ item.setData(VirtualItem.BACKGROUND_KEY, backgrounds);
+ item.setData(VirtualItem.FONT_KEY, fontDatas);
+ }
+ }
+
+ public void setChildCount(final Object elementOrTreePath, final int count) {
+ preservingSelection(new Runnable() {
+ public void run() {
+ VirtualItem[] items = findItems(elementOrTreePath);
+ for (int i = 0; i < items.length; i++) {
+ VirtualItem[] children = items[i].getItems();
+ for (int j = 0; j < children.length; j++) {
+ if (children[j].getData() != null && children[j].getIndex().intValue() >= count) {
+ disassociate(children[j]);
+ }
+ }
+
+ items[i].setItemCount(count);
+ }
+ }
+ });
+ fTree.validate();
+ }
+
+ public void setHasChildren(final Object elementOrTreePath, final boolean hasChildren) {
+ preservingSelection(new Runnable() {
+ public void run() {
+ VirtualItem[] items = findItems(elementOrTreePath);
+ for (int i = 0; i < items.length; i++) {
+ VirtualItem item = items[i];
+
+ if (!hasChildren) {
+ VirtualItem[] children = item.getItems();
+ for (int j = 0; j < children.length; j++) {
+ if (children[j].getData() != null) {
+ disassociate(children[j]);
+ }
+ }
+ }
+
+ item.setHasItems(hasChildren);
+ if (hasChildren) {
+ if (!item.getExpanded()) {
+ item.setItemCount(-1);
+ } else if (item.needsCountUpdate()) {
+ virtualLazyUpdateChildCount(item);
+ }
+ }
+ }
+ }
+ });
+ }
+
+ private void virtualLazyUpdateHasChildren(VirtualItem item) {
+ TreePath treePath;
+ treePath = getTreePathFromItem(item);
+ getContentProvider().updateHasChildren(treePath);
+ }
+
+ private void virtualLazyUpdateChildCount(VirtualItem item) {
+ item.clearNeedsCountUpdate();
+ getContentProvider().updateChildCount(getTreePathFromItem(item), item.getItemCount());
+ }
+
+ private void virtualLazyUpdateData(VirtualItem item) {
+ item.clearNeedsDataUpdate();
+ getContentProvider().updateElement(getTreePathFromItem(item.getParent()), item.getIndex().intValue());
+ }
+
+ private void virtualLazyUpdateLabel(VirtualItem item) {
+ item.clearNeedsLabelUpdate();
+ if ( !getLabelProvider().update(getTreePathFromItem(item)) ) {
+ if (item.getData() instanceof String) {
+ item.setData(VirtualItem.LABEL_KEY, new String[] { (String)item.getData() } );
+ }
+ }
+ }
+
+ private TreePath getTreePathFromItem(VirtualItem item) {
+ List segments = new LinkedList();
+ while (item.getParent() != null) {
+ segments.add(0, item.getData());
+ item = item.getParent();
+ }
+ return new TreePath(segments.toArray());
+ }
+
+ private void unmapElement(Object element, VirtualItem item) {
+ if (fNotifyUnmap) {
+ // TODO: should we update the filter with the "new non-identical element"?
+ IContentProvider provider = getContentProvider();
+ if (provider instanceof ModelContentProvider) {
+ ((ModelContentProvider) provider).unmapPath((TreePath) item.getData(TREE_PATH_KEY));
+ }
+ }
+
+ List itemsList = (List) fItemsMap.get(element);
+ if (itemsList != null) {
+ itemsList.remove(item);
+ if (itemsList.isEmpty()) {
+ fItemsMap.remove(element);
+ }
+ }
+ }
+
+ private void mapElement(Object element, VirtualItem item) {
+ // Get the items set for given element, if it doesn't exist, create it.
+ // When retrieving the set, also remove it from the map, it will be
+ // re-inserted to make sure that the new instance of element is used
+ // in case the element has changed but the elment is equal to the old
+ // one.
+ List itemsList = (List) fItemsMap.remove(element);
+ if (itemsList == null) {
+ itemsList = new ArrayList(1);
+ }
+
+ if (!itemsList.contains(item)) {
+ itemsList.add(item);
+ }
+
+ // Insert the set back into the map.
+ fItemsMap.put(element, itemsList);
+
+ item.setData(TREE_PATH_KEY, getTreePathFromItem(item));
+ }
+
+ public void revealed(VirtualItem item) {
+ if (item.needsDataUpdate()) {
+ virtualLazyUpdateData(item);
+ } else if (item.getData() != null) {
+ if (item.needsLabelUpdate()) {
+ virtualLazyUpdateLabel(item);
+ }
+ if (item.getExpanded() && item.hasItems() && item.needsCountUpdate()) {
+ virtualLazyUpdateChildCount(item);
+ }
+ }
+ }
+
+ public void disposed(VirtualItem item) {
+ if (!fTree.isDisposed()) {
+ Object data = item.getData();
+ if (data != null) {
+ unmapElement(data, item);
+ }
+ }
+ }
+
+ private void associate(Object element, VirtualItem item) {
+ Object data = item.getData();
+ if (data != null && data != element && data.equals(element)) {
+ // elements are equal but not identical
+ // -> being removed from map, but should not change filters
+ try {
+ fNotifyUnmap = false;
+ doAssociate(element, item);
+ } finally {
+ fNotifyUnmap = true;
+ }
+ } else {
+ doAssociate(element, item);
+ }
+
+ }
+
+ private void doAssociate(Object element, VirtualItem item) {
+ Object data = item.getData();
+ if (data != null && data != element && data.equals(element)) {
+ // workaround for PR 1FV62BT
+ // assumption: elements are equal but not identical
+ // -> remove from map but don't touch children
+ unmapElement(data, item);
+ item.setData(element);
+ mapElement(element, item);
+ } else {
+ // recursively disassociate all
+ if (data != element) {
+ if (data != null) {
+ unmapElement(element, item);
+ disassociate(item);
+ }
+ item.setData(element);
+ }
+ // Always map the element, even if data == element,
+ // since unmapAllElements() can leave the map inconsistent
+ // See bug 2741 for details.
+ mapElement(element, item);
+ }
+ }
+
+ private void disassociate(VirtualItem item) {
+ unmapElement(item.getData(), item);
+
+ // Clear the map before we clear the data
+ item.setData(null);
+
+ // Disassociate the children
+ VirtualItem[] items = item.getItems();
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].getData() != null) {
+ disassociate(items[i]);
+ }
+ }
+ }
+
+ public void setSelection(ISelection selection, boolean reveal) {
+ setSelection(selection, reveal, false);
+ }
+
+ /**
+ * Sets the selection in the viewer to the specified selection.
+ *
+ * @param selection the selection
+ * @param reveal whether to reveal the selection
+ * @param force whether to force the selection (i.e. <code>true</code> to
+ * override the model selection policy)
+ */
+ public void setSelection(ISelection selection, boolean reveal, boolean force) {
+ if (!force && !overrideSelection(getSelection(), selection)) {
+ return;
+ }
+
+ if (!fPreservingSelecction) {
+ internalSetSelection(selection, reveal);
+ fireSelectionChanged(new SelectionChangedEvent(this, selection));
+ } else {
+ fRestoreSelection = false;
+ internalSetSelection(selection, reveal);
+ }
+ }
+
+ private void internalSetSelection(ISelection selection, boolean reveal) {
+ if (selection instanceof ITreeSelection) {
+ TreePath[] paths = ((ITreeSelection) selection).getPaths();
+ List newSelection = new ArrayList(paths.length);
+ for (int i = 0; i < paths.length; ++i) {
+ // Use internalExpand since item may not yet be created. See
+ // 1G6B1AR.
+ VirtualItem item = findItem(paths[i]);
+ if (item != null) {
+ newSelection.add(item);
+ }
+ }
+ fTree.setSelection((VirtualItem[]) newSelection.toArray(new VirtualItem[newSelection.size()]));
+
+ // Although setting the selection in the control should reveal it,
+ // setSelection may be a no-op if the selection is unchanged,
+ // so explicitly reveal items in the selection here.
+ // See bug 100565 for more details.
+ if (reveal && newSelection.size() > 0) {
+ // Iterate backwards so the first item in the list
+ // is the one guaranteed to be visible
+ for (int i = (newSelection.size() - 1); i >= 0; i--) {
+ fTree.showItem((VirtualItem) newSelection.get(i));
+ }
+ }
+ } else {
+ fTree.setSelection(EMPTY_ITEMS_ARRAY);
+ }
+
+ // Make sure that the new selection is properly revealed.
+ fTree.validate();
+ }
+
+ public void update(Object element) {
+ VirtualItem[] items = findItems(element);
+ for (int i = 0; i < items.length; i++) {
+ doUpdate(items[i]);
+ }
+ }
+
+ public void doUpdate(VirtualItem item) {
+ item.setNeedsLabelUpdate();
+ fTree.validate(item);
+ }
+
+ public ISelection getSelection() {
+ if (fTree.isDisposed()) {
+ return TreeSelection.EMPTY;
+ }
+ VirtualItem[] items = fTree.getSelection();
+ ArrayList list = new ArrayList(items.length);
+ for (int i = 0; i < items.length; i++) {
+ if (items[i].getData() != null) {
+ list.add(getTreePathFromItem(items[i]));
+ }
+ }
+ return new TreeSelection((TreePath[]) list.toArray(new TreePath[list.size()]));
+ }
+
+ private void preservingSelection(Runnable updateCode) {
+
+ ISelection oldSelection = null;
+ try {
+ // preserve selection
+ oldSelection = getSelection();
+ fPreservingSelecction = fRestoreSelection = true;
+
+ // perform the update
+ updateCode.run();
+
+ } finally {
+ fPreservingSelecction = false;
+
+ // restore selection
+ if (fRestoreSelection) {
+ internalSetSelection(oldSelection, false);
+ }
+
+ // send out notification if old and new differ
+ ISelection newSelection = getSelection();
+ if (!newSelection.equals(oldSelection)) {
+ handleInvalidSelection(oldSelection, newSelection);
+ }
+ }
+ }
+
+ public void expandToLevel(Object elementOrTreePath, int level) {
+ VirtualItem[] items = findItems(elementOrTreePath);
+ if (items.length > 0) {
+ expandToLevel(items[0], level);
+ }
+ fTree.validate();
+ }
+
+ public void setExpandedState(Object elementOrTreePath, boolean expanded) {
+ VirtualItem[] items = findItems(elementOrTreePath);
+ if (items.length > 0) {
+ items[0].setExpanded(expanded);
+ }
+ }
+
+ public boolean getExpandedState(Object elementOrTreePath) {
+ VirtualItem[] items = findItems(elementOrTreePath);
+ if (items.length > 0) {
+ return items[0].getExpanded();
+ }
+ return false;
+ }
+
+ private void expandToLevel(VirtualItem item, int level) {
+ if (level == ALL_LEVELS || level > 0) {
+ if (!item.hasItems()) {
+ return;
+ }
+
+ if (item.getItemCount() < 0) {
+ virtualLazyUpdateChildCount(item);
+ item.clearNeedsCountUpdate();
+ }
+ item.setExpanded(true);
+
+ if (item.getData() == null) {
+ virtualLazyUpdateData(item);
+ // Cannot expand children if data is null.
+ return;
+ }
+
+ if (level == ALL_LEVELS || level > 1) {
+ VirtualItem[] children = item.getItems();
+ int newLevel = (level == ALL_LEVELS ? ALL_LEVELS
+ : level - 1);
+ for (int i = 0; i < children.length; i++) {
+ expandToLevel(children[i], newLevel);
+ }
+ }
+ }
+ }
+
+ private void handleInvalidSelection(ISelection selection, ISelection newSelection) {
+ IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(selection, getPresentationContext());
+ if (selectionPolicy != null) {
+ while (!selection.equals(newSelection)) {
+ ISelection temp = newSelection;
+ selection = selectionPolicy.replaceInvalidSelection(selection, newSelection);
+ if (selection == null) {
+ selection = TreeSelection.EMPTY;
+ }
+ if (!temp.equals(selection)) {
+ internalSetSelection(selection, false);
+ newSelection = getSelection();
+ } else {
+ break;
+ }
+ }
+ }
+
+ fireSelectionChanged(new SelectionChangedEvent(this, newSelection));
+ }
+
+ /**
+ * Returns whether the candidate selection should override the current
+ * selection.
+ *
+ * @param current
+ * @param curr
+ * @return
+ */
+ public boolean overrideSelection(ISelection current, ISelection candidate) {
+ IModelSelectionPolicy selectionPolicy = ViewerAdapterService.getSelectionPolicy(current, getPresentationContext());
+ if (selectionPolicy == null) {
+ return true;
+ }
+ if (selectionPolicy.contains(candidate, getPresentationContext())) {
+ return selectionPolicy.overrides(current, candidate, getPresentationContext());
+ }
+ return !selectionPolicy.isSticky(current, getPresentationContext());
+ }
+
+ private static ViewerFilter[] EMPTY_FILTER_ARRAY = new ViewerFilter[0];
+
+ public ViewerFilter[] getFilters() {
+ // TODO: Add filter support
+ return EMPTY_FILTER_ARRAY;
+ }
+
+ public void dispose() {
+ if (fColumnPresentation != null) {
+ fColumnPresentation.dispose();
+ }
+ fContext.dispose();
+
+ if (fContentProvider != null) {
+ fContentProvider.dispose();
+ fContentProvider = null;
+ }
+ if (fLabelProvider != null) {
+ fLabelProvider.dispose();
+ fLabelProvider = null;
+ }
+
+ fTree.removeItemListener(this);
+ fTree.dispose();
+ }
+
+ /**
+ * Returns this viewer's presentation context.
+ *
+ * @return presentation context
+ */
+ public IPresentationContext getPresentationContext() {
+ return fContext;
+ }
+
+ /**
+ * Configures the columns for the given viewer input.
+ *
+ * @param input
+ */
+ private void resetColumns(Object input) {
+ if (input != null) {
+ // only change columns if the input is non-null (persist when empty)
+ IColumnPresentationFactory factory = ViewerAdapterService.getColumnPresentationFactory(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();
+ }
+ }
+ }
+ }
+
+ /**
+ * 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();
+ }
+
+ /**
+ * Refreshes the columns in the view, based on the viewer input.
+ */
+ protected 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.
+ *
+ * @param presentation
+ */
+ protected void buildColumns(IColumnPresentation presentation) {
+ PresentationContext presentationContext = (PresentationContext) getPresentationContext();
+ if (presentation != null) {
+ presentationContext.setColumns(getVisibleColumns());
+ } else {
+ presentationContext.setColumns(null);
+ }
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * 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) {
+ if (ids != null && ids.length == 0) {
+ ids = null;
+ }
+ IColumnPresentation presentation = getColumnPresentation();
+ if (presentation != null) {
+ 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();
+ }
+ }
+
+
+
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Save viewer state into the given memento.
+ *
+ * @param memento
+ */
+ public void saveState(IMemento memento) {
+ 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]);
+ }
+ }
+ }
+ // save presentation context properties
+ IPresentationContext context = getPresentationContext();
+ if (context instanceof PresentationContext) {
+ PresentationContext pc = (PresentationContext) context;
+ pc.saveProperites(memento);
+
+ }
+ }
+
+ /**
+ * Initializes viewer state from the memento
+ *
+ * @param memento
+ */
+ public void initState(IMemento memento) {
+ IMemento[] 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);
+ }
+ }
+ // restore presentation context properties
+ // save presentation context properties
+ IPresentationContext context = getPresentationContext();
+ if (context instanceof PresentationContext) {
+ PresentationContext pc = (PresentationContext) context;
+ pc.initProperties(memento);
+ }
+ }
+
+ public void addViewerUpdateListener(IViewerUpdateListener listener) {
+ ((ModelContentProvider)getContentProvider()).addViewerUpdateListener(listener);
+ }
+
+ public void removeViewerUpdateListener(IViewerUpdateListener listener) {
+ ModelContentProvider cp = getContentProvider();
+ if (cp != null) {
+ cp.removeViewerUpdateListener(listener);
+ }
+ }
+
+ public void addModelChangedListener(IModelChangedListener listener) {
+ ((ModelContentProvider)getContentProvider()).addModelChangedListener(listener);
+ }
+
+ public void removeModelChangedListener(IModelChangedListener listener) {
+ ModelContentProvider cp = getContentProvider();
+ if (cp != null) {
+ cp.removeModelChangedListener(listener);
+ }
+ }
+
+
+ public void addLabelUpdateListener(ILabelUpdateListener listener) {
+ getLabelProvider().addLabelUpdateListener(listener);
+ }
+
+ public void removeLabelUpdateListener(ILabelUpdateListener listener) {
+ getLabelProvider().removeLabelUpdateListener(listener);
+ }
+
+ /**
+ * Performs auto expand on an element at the specified path if the auto expand
+ * level dictates the element should be expanded.
+ *
+ * @param elementPath tree path to element to consider for expansion
+ */
+ public void autoExpand(TreePath elementPath) {
+ int level = getAutoExpandLevel();
+ if (level > 0 || level == ITreeModelViewer.ALL_LEVELS) {
+ if (level == ITreeModelViewer.ALL_LEVELS || level >= elementPath.getSegmentCount()) {
+ expandToLevel(elementPath, 1);
+ }
+ }
+ }
+
+ public int getChildCount(TreePath path) {
+ VirtualItem[] items = findItems(path);
+ if (items.length > 0) {
+ return items[0].getItemCount();
+ }
+ return -1;
+ }
+
+ public Object getChildElement(TreePath path, int index) {
+ VirtualItem[] items = findItems(path);
+ if (items.length > 0) {
+ if (index < items[0].getItemCount()) {
+ return items[0].getItem(new VirtualItem.Index(index)).getData();
+ }
+ }
+ return null;
+ }
+
+ public TreePath getTopElementPath() {
+ return null;
+ }
+
+ public void saveElementState(TreePath path, ModelDelta delta) {
+ VirtualTree tree = getTree();
+ VirtualItem[] selection = tree.getSelection();
+ Set set = new HashSet();
+ for (int i = 0; i < selection.length; i++) {
+ set.add(selection[i]);
+ }
+
+ VirtualItem[] items = null;
+ VirtualItem parent = findItem(path);
+
+ if (parent != null) {
+ delta.setChildCount(((ModelContentProvider)getContentProvider()).viewToModelCount(path, parent.getItemCount()));
+ if (parent.getExpanded()) {
+ delta.setFlags(delta.getFlags() | IModelDelta.EXPAND);
+ }
+ if (set.contains(parent)) {
+ delta.setFlags(delta.getFlags() | IModelDelta.SELECT);
+ }
+
+ items = parent.getItems();
+ for (int i = 0; i < items.length; i++) {
+ doSaveElementState(path, delta, items[i], set);
+ }
+ }
+ }
+
+ private void doSaveElementState(TreePath parentPath, ModelDelta delta, VirtualItem item, Collection 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;
+ }
+ int modelIndex = ((ModelContentProvider)getContentProvider()).viewToModelIndex(parentPath, item.getIndex().intValue());
+ TreePath elementPath = parentPath.createChildPath(element);
+ int numChildren = ((ModelContentProvider)getContentProvider()).viewToModelCount(elementPath, item.getItemCount());
+ ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren);
+ if (expanded) {
+ VirtualItem[] items = item.getItems();
+ for (int i = 0; i < items.length; i++) {
+ doSaveElementState(elementPath, childDelta, items[i], set);
+ }
+ }
+ }
+ }
+ }
+
+ public void updateViewer(IModelDelta delta) {
+ ((ModelContentProvider)getContentProvider()).updateNodes(new IModelDelta[] { delta }, true);
+ }
+
+ public ViewerLabel getElementLabel(TreePath path, String columnId) {
+ if (path.getSegmentCount() == 0) {
+ return null;
+ }
+
+ int columnIdx = -1;
+ String[] visibleColumns = getVisibleColumns();
+ if (columnId != null && visibleColumns != null) {
+ int i = 0;
+ for (i = 0; i < visibleColumns.length; i++) {
+ if (columnId.equals(getVisibleColumns()[i])) {
+ columnIdx = i;
+ break;
+ }
+ }
+ if (i == visibleColumns.length) {
+ return null;
+ }
+ } else {
+ columnIdx = 0;
+ }
+ VirtualItem item = findItem(path);
+
+ if (item != null) {
+ ViewerLabel label = new ViewerLabel(getText(item, columnIdx), getImage(item, columnIdx));
+ label.setFont(getFont(item, columnIdx));
+ label.setBackground(getBackground(item, columnIdx));
+ label.setForeground(getForeground(item, columnIdx));
+ return label;
+ }
+ return null;
+ }
+
+ private String getText(VirtualItem item, int columnIdx) {
+ String[] texts = (String[])item.getData(VirtualItem.LABEL_KEY);
+ if (texts != null && texts.length > columnIdx) {
+ return texts[columnIdx];
+ }
+ return null;
+ }
+
+ private Image getImage(VirtualItem item, int columnIdx) {
+ ImageDescriptor[] imageDescriptors = (ImageDescriptor[]) item.getData(VirtualItem.IMAGE_KEY);
+ if (imageDescriptors != null && imageDescriptors.length > columnIdx) {
+ return getLabelProvider().getImage(imageDescriptors[columnIdx]);
+ }
+ return null;
+ }
+
+ private Font getFont(VirtualItem item, int columnIdx) {
+ FontData[] fontDatas = (FontData[]) item.getData(VirtualItem.FONT_KEY);
+ if (fontDatas != null) {
+ return getLabelProvider().getFont(fontDatas[columnIdx]);
+ }
+ return null;
+ }
+
+ public Color getForeground(VirtualItem item, int columnIdx) {
+ RGB[] rgbs = (RGB[]) item.getData(VirtualItem.FOREGROUND_KEY);
+ if (rgbs != null) {
+ return getLabelProvider().getColor(rgbs[columnIdx]);
+ }
+ return null;
+ }
+
+ public Color getBackground(VirtualItem item, int columnIdx) {
+ RGB[] rgbs = (RGB[]) item.getData(VirtualItem.BACKGROUND_KEY);
+ if (rgbs != null) {
+ return getLabelProvider().getColor(rgbs[columnIdx]);
+ }
+ return null;
+ }
+
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java
index ee18400ce..5be643c51 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/LabelUpdate.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2007 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -7,21 +7,17 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
-import org.eclipse.debug.internal.core.IInternalDebugCoreConstants;
import org.eclipse.debug.internal.core.commands.Request;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.TreePath;
-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.TreeItem;
/**
* @since 3.3
@@ -36,22 +32,12 @@ class LabelUpdate extends Request implements ILabelUpdate {
private String[] fLabels;
private FontData[] fFontDatas;
private TreeModelLabelProvider fProvider;
- private TreeItem fItem;
+ private ITreeModelLabelProviderTarget fTreeViewer;
private int fNumColumns;
private IPresentationContext fContext;
private Object fViewerInput;
/**
- * Label data cache keys
- * TODO: workaround for bug 159461
- */
- static String PREV_LABEL_KEY = "PREV_LABEL_KEY"; //$NON-NLS-1$
- static String PREV_IMAGE_KEY = "PREV_IMAGE_KEY"; //$NON-NLS-1$
- static String PREV_FONT_KEY = "PREV_FONT_KEY"; //$NON-NLS-1$
- static String PREV_FOREGROUND_KEY = "PREV_FOREGROUND_KEY"; //$NON-NLS-1$
- static String PREV_BACKGROUND_KEY = "PREV_BACKGROUND_KEY"; //$NON-NLS-1$
-
- /**
* @param viewerInput input at the time the request was made
* @param elementPath element the label is for
* @param item item the label is for
@@ -59,18 +45,18 @@ class LabelUpdate extends Request implements ILabelUpdate {
* @param columnIds column identifiers or <code>null</code>
* @param context presentation context
*/
- public LabelUpdate(Object viewerInput, TreePath elementPath, TreeItem item, TreeModelLabelProvider provider, String[] columnIds, IPresentationContext context) {
+ public LabelUpdate(Object viewerInput, TreePath elementPath, TreeModelLabelProvider provider, ITreeModelLabelProviderTarget treeViewer, String[] columnIds, IPresentationContext context) {
fContext = context;
fViewerInput = viewerInput;
fElementPath = elementPath;
fProvider = provider;
fColumnIds = columnIds;
- fItem = item;
fNumColumns = 1;
if (columnIds != null) {
fNumColumns = columnIds.length;
}
fLabels = new String[fNumColumns];
+ fTreeViewer = treeViewer;
}
/* (non-Javadoc)
@@ -164,93 +150,7 @@ class LabelUpdate extends Request implements ILabelUpdate {
* Applies settings to viewer cell
*/
public void update() {
- // label data is stored to prevent flickering of asynchronous view, see bug 159461
- if (!fItem.isDisposed()) {
-
- for (int i=0; i<fNumColumns; i++){
- // text might be null if the launch has been terminated
- fItem.setText(i,(fLabels[i] == null ? IInternalDebugCoreConstants.EMPTY_STRING : fLabels[i]));
- }
- fItem.setData(PREV_LABEL_KEY, fLabels);
-
- if (fImageDescriptors == null) {
- for (int i=0; i<fNumColumns; i++){
- fItem.setImage(i,null);
- }
- fItem.setData(PREV_IMAGE_KEY, null);
- } else {
- Image[] images = new Image[fImageDescriptors.length];
- for (int i = 0; i < fImageDescriptors.length; i++) {
- images[i] = fProvider.getImage(fImageDescriptors[i]);
- }
- if (fColumnIds == null) {
- fItem.setImage(images[0]);
- } else {
- fItem.setImage(images);
- }
- fItem.setData(PREV_IMAGE_KEY, images);
- }
-
- if (fForegrounds == null) {
- for (int i=0; i<fNumColumns; i++){
- fItem.setForeground(i,null);
- }
- fItem.setData(PREV_FOREGROUND_KEY, null);
- } else {
- Color[] foregrounds = new Color[fForegrounds.length];
- for (int i = 0; i< foregrounds.length; i++) {
- foregrounds[i] = fProvider.getColor(fForegrounds[i]);
- }
- if (fColumnIds == null) {
- fItem.setForeground(0,foregrounds[0]);
- } else {
- for (int i = 0; i< foregrounds.length; i++) {
- fItem.setForeground(i, foregrounds[i]);
- }
- }
- fItem.setData(PREV_FOREGROUND_KEY, foregrounds);
- }
-
- if (fBackgrounds == null) {
- for (int i=0; i<fNumColumns; i++){
- fItem.setBackground(i,null);
- }
- fItem.setData(PREV_BACKGROUND_KEY, null);
- } else {
- Color[] backgrounds = new Color[fBackgrounds.length];
- for (int i = 0; i< backgrounds.length; i++) {
- backgrounds[i] = fProvider.getColor(fBackgrounds[i]);
- }
- if (fColumnIds == null) {
- fItem.setBackground(0,backgrounds[0]);
- } else {
- for (int i = 0; i< backgrounds.length; i++) {
- fItem.setBackground(i, backgrounds[i]);
- }
- }
- fItem.setData(PREV_BACKGROUND_KEY, backgrounds);
- }
-
- if (fFontDatas == null) {
- for (int i=0; i<fNumColumns; i++){
- fItem.setFont(i,null);
- }
- fItem.setData(PREV_FONT_KEY, null);
- } else {
- Font[] fonts = new Font[fFontDatas.length];
- for (int i = 0; i < fFontDatas.length; i++) {
- fonts[i] = fProvider.getFont(fFontDatas[i]);
- }
- if (fColumnIds == null) {
- fItem.setFont(0,fonts[0]);
- } else {
- for (int i = 0; i < fonts.length; i++) {
- fItem.setFont(i, fonts[i]);
- }
- }
- fItem.setData(PREV_FONT_KEY, fonts);
- }
- }
+ fTreeViewer.setElementData(fElementPath, fNumColumns, fLabels, fImageDescriptors, fFontDatas, fForegrounds, fBackgrounds);
fProvider.updateComplete(this);
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java
index 0491a7e6e..fcfabf09c 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ModelContentProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -9,6 +9,7 @@
* IBM Corporation - initial API and implementation
* Pawel Piech - Wind River - Bug 205335: ModelContentProvider does not cancel stale updates when switching viewer input
* Wind River Systems - Fix for viewer state save/restore [188704]
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -49,7 +50,6 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IViewerUpdateListener;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
import org.eclipse.jface.viewers.IContentProvider;
-import org.eclipse.jface.viewers.StructuredViewer;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.jface.viewers.ViewerFilter;
@@ -65,8 +65,14 @@ import org.eclipse.ui.progress.WorkbenchJob;
*/
abstract class ModelContentProvider implements IContentProvider, IModelChangedListener {
- private Viewer fViewer;
+ private ITreeModelContentProviderTarget fViewer;
+ /**
+ * Flag indicating whether the viewer should ignore SELECT, REVEAL,
+ * EXPAND, and COLLAPSE flags of {@link IModelDelta} coming from the model.
+ */
+ private boolean fSuppressModelControlRequests = false;
+
private Map fModelProxies = new HashMap(); // model proxy by element
/**
@@ -197,19 +203,27 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
protected static final TreePath EMPTY_TREE_PATH = new TreePath(new Object[]{});
// debug flags
+ public static String DEBUG_PRESENTATION_ID = null;
public static boolean DEBUG_CONTENT_PROVIDER = false;
public static boolean DEBUG_UPDATE_SEQUENCE = false;
public static boolean DEBUG_STATE_SAVE_RESTORE = false;
-
+ public static boolean DEBUG_DELTAS = false;
+
static {
+ DEBUG_PRESENTATION_ID = Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/presentationId"); //$NON-NLS-1$
+ if (!DebugUIPlugin.DEBUG || "".equals(DEBUG_PRESENTATION_ID)) { //$NON-NLS-1$
+ DEBUG_PRESENTATION_ID = null;
+ }
DEBUG_CONTENT_PROVIDER = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/contentProvider")); //$NON-NLS-1$
DEBUG_UPDATE_SEQUENCE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/updateSequence")); //$NON-NLS-1$
DEBUG_STATE_SAVE_RESTORE = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/stateSaveRestore")); //$NON-NLS-1$
+ DEBUG_DELTAS = DebugUIPlugin.DEBUG && "true".equals( //$NON-NLS-1$
+ Platform.getDebugOption("org.eclipse.debug.ui/debug/viewers/deltas")); //$NON-NLS-1$
}
-
+
/*
* (non-Javadoc)
*
@@ -245,7 +259,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
* java.lang.Object, java.lang.Object)
*/
public synchronized void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- fViewer = viewer;
+ fViewer = (ITreeModelContentProviderTarget)viewer;
if (oldInput != null) {
for (Iterator itr = fCompareRequestsInProgress.values().iterator(); itr.hasNext(); ) {
((ElementCompareRequest)itr.next()).cancel();
@@ -312,7 +326,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
UIJob job = new UIJob("restore state") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
if (!isDisposed() && input.equals(getViewer().getInput())) {
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("RESTORE: " + stateDelta.toString()); //$NON-NLS-1$
}
fViewerStates.remove(keyMementoString);
@@ -441,21 +455,21 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
protected void saveViewerState(Object input) {
IElementMementoProvider stateProvider = ViewerAdapterService.getMementoProvider(input);
if (stateProvider != null) {
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE BEGIN: " + input); //$NON-NLS-1$
}
// build a model delta representing expansion and selection state
final ModelDelta saveDeltaRoot = new ModelDelta(input, IModelDelta.NO_CHANGE);
buildViewerState(saveDeltaRoot);
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE DELTA FROM VIEW: " + saveDeltaRoot); //$NON-NLS-1$
}
if (fPendingState != null) {
// If the restore for the current input was never completed, preserve
// that restore along with the restore that was completed.
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE OUTSTANDING RESTORE: " + fPendingState); //$NON-NLS-1$
}
@@ -492,7 +506,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
saveDeltaNode.setChildCount(pendingDeltaNode.getParentDelta().getChildCount());
copyIntoDelta(pendingDeltaNode, saveDeltaNode);
} else {
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println(" Skipping: " + pendingDeltaNode.getElement()); //$NON-NLS-1$
}
}
@@ -515,7 +529,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
// encode delta with mementos in place of elements, in non-UI thread
encodeDelta(saveDeltaRoot, stateProvider);
} else {
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE CANCELED, NO DATA"); //$NON-NLS-1$
}
}
@@ -621,7 +635,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
} catch (IOException e) {
DebugUIPlugin.log(e);
}
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE COMPLETED: " + rootDelta); //$NON-NLS-1$
}
stateSaveComplete(this);
@@ -634,7 +648,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
req.cancel();
}
requests.clear();
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("SAVE ABORTED: " + rootDelta.getElement()); //$NON-NLS-1$
}
stateSaveComplete(this);
@@ -785,15 +799,12 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
synchronized (ModelContentProvider.this) {
if (!isDisposed()) {
context = getPresentationContext();
- viewer = getViewer();
+ viewer = (Viewer)getViewer();
}
}
if (context != null && !proxy.isDisposed()) {
proxy.init(context);
Object[] mcls = fModelListeners.getListeners();
- for (int i = 0; i < mcls.length; i++) {
- proxy.addModelChangedListener((IModelChangedListener) mcls[i]);
- }
proxy.addModelChangedListener(ModelContentProvider.this);
proxy.installed(viewer);
}
@@ -826,21 +837,58 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
*
* @see org.eclipse.debug.internal.ui.viewers.provisional.IModelChangedListener#modelChanged(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta)
*/
- public void modelChanged(final IModelDelta delta, final IModelProxy proxy) {
- WorkbenchJob job = new WorkbenchJob("process model delta") { //$NON-NLS-1$
- public IStatus runInUIThread(IProgressMonitor monitor) {
- if (!proxy.isDisposed()) {
- updateNodes(new IModelDelta[] { delta });
- }
- return Status.OK_STATUS;
- }
- };
- job.setSystem(true);
- job.schedule();
-
+ public synchronized void modelChanged(final IModelDelta delta, final IModelProxy proxy) {
+ if (fViewer != null && !proxy.isDisposed()) {
+ new WorkbenchJob(fViewer.getDisplay(), "process model delta") { //$NON-NLS-1$
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ if (!proxy.isDisposed()) {
+ if (DEBUG_DELTAS && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
+ DebugUIPlugin.debug("RECEIVED DELTA: " + delta.toString()); //$NON-NLS-1$
+ }
+
+ updateNodes(new IModelDelta[] { delta }, false);
+
+ // Call model listeners after updating the viewer model.
+ Object[] listeners = fModelListeners.getListeners();
+ for (int i = 0; i < listeners.length; i++) {
+ ((IModelChangedListener)listeners[i]).modelChanged(delta, proxy);
+ }
+ }
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
}
- protected void updateNodes(IModelDelta[] nodes) {
+ /**
+ * Turns on the mode which causes the model viewer to ignore SELECT,
+ * EXPAND, and COLLAPSE flags of {@link IModelDelta}.
+ *
+ * @param suppress If <code>true</code> it turns on the suppress mode.
+ */
+ protected void setSuppressModelControlDeltas(boolean suppress) {
+ fSuppressModelControlRequests = suppress;
+ }
+
+ /**
+ * Returns true if the viewer is currently in the mode to ignore SELECT,
+ * REVEAL, EXPAND, and COLLAPSE flags of {@link IModelDelta}.
+ *
+ * @return Returns <code>true</code> if in suppress mode.
+ */
+ protected boolean isSuppressModelControlDeltas() {
+ return fSuppressModelControlRequests;
+ }
+
+ /**
+ * Updates the viewer with the following deltas.
+ *
+ * @param nodes Model deltas to be processed.
+ * @param override If true, it overrides the mode which suppresses
+ * processing of SELECT, REVEAL, EXPAND, COLLAPSE flags of
+ * {@link IModelDelta}.
+ */
+ protected void updateNodes(IModelDelta[] nodes, boolean override) {
for (int i = 0; i < nodes.length; i++) {
IModelDelta node = nodes[i];
int flags = node.getFlags();
@@ -854,15 +902,6 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
if ((flags & IModelDelta.CONTENT) != 0) {
handleContent(node);
}
- if ((flags & IModelDelta.EXPAND) != 0) {
- handleExpand(node);
- }
- if ((flags & IModelDelta.COLLAPSE) != 0) {
- handleCollapse(node);
- }
- if ((flags & IModelDelta.SELECT) != 0) {
- handleSelect(node);
- }
if ((flags & IModelDelta.STATE) != 0) {
handleState(node);
}
@@ -878,10 +917,21 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
if ((flags & IModelDelta.UNINSTALL) != 0) {
handleUninstall(node);
}
- if ((flags & IModelDelta.REVEAL) != 0) {
- handleReveal(node);
- }
- updateNodes(node.getChildDeltas());
+ if (!fSuppressModelControlRequests || override) {
+ if ((flags & IModelDelta.EXPAND) != 0) {
+ handleExpand(node);
+ }
+ if ((flags & IModelDelta.COLLAPSE) != 0) {
+ handleCollapse(node);
+ }
+ if ((flags & IModelDelta.SELECT) != 0) {
+ handleSelect(node);
+ }
+ if ((flags & IModelDelta.REVEAL) != 0) {
+ handleReveal(node);
+ }
+ }
+ updateNodes(node.getChildDeltas(), override);
}
}
@@ -936,7 +986,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
*
* @return viewer
*/
- protected Viewer getViewer() {
+ protected ITreeModelContentProviderTarget getViewer() {
return fViewer;
}
@@ -1046,10 +1096,10 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
* @return whether to filter the element
*/
protected boolean shouldFilter(Object parentElementOrTreePath, Object element) {
- ViewerFilter[] filters = ((StructuredViewer)fViewer).getFilters();
+ ViewerFilter[] filters = fViewer.getFilters();
if (filters.length > 0) {
for (int j = 0; j < filters.length; j++) {
- if (!(filters[j].select(fViewer, parentElementOrTreePath, element))) {
+ if (!(filters[j].select((Viewer)fViewer, parentElementOrTreePath, element))) {
return true;
}
}
@@ -1103,7 +1153,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
CheckState state = new CheckState();
fPendingState.accept(state);
if (state.isComplete()) {
- if (DEBUG_STATE_SAVE_RESTORE) {
+ if (DEBUG_STATE_SAVE_RESTORE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("RESTORE COMPELTE: " + fPendingState); //$NON-NLS-1$
}
fPendingState = null;
@@ -1135,12 +1185,12 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
requests.add(update);
}
if (begin) {
- if (DEBUG_UPDATE_SEQUENCE) {
+ if (DEBUG_UPDATE_SEQUENCE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("MODEL SEQUENCE BEGINS"); //$NON-NLS-1$
}
notifyUpdate(UPDATE_SEQUENCE_BEGINS, null);
}
- if (DEBUG_UPDATE_SEQUENCE) {
+ if (DEBUG_UPDATE_SEQUENCE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("\tBEGIN - " + update); //$NON-NLS-1$
}
notifyUpdate(UPDATE_BEGINS, update);
@@ -1165,11 +1215,11 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
end = fRequestsInProgress.isEmpty();
}
notifyUpdate(UPDATE_COMPLETE, update);
- if (DEBUG_UPDATE_SEQUENCE) {
+ if (DEBUG_UPDATE_SEQUENCE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("\tEND - " + update); //$NON-NLS-1$
}
if (end) {
- if (DEBUG_UPDATE_SEQUENCE) {
+ if (DEBUG_UPDATE_SEQUENCE && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("MODEL SEQUENCE ENDS"); //$NON-NLS-1$
}
notifyUpdate(UPDATE_SEQUENCE_COMPLETE, null);
@@ -1350,11 +1400,6 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
*/
void addModelChangedListener(IModelChangedListener listener) {
fModelListeners.add(listener);
- Iterator proxies = fModelProxies.values().iterator();
- while (proxies.hasNext()) {
- IModelProxy proxy = (IModelProxy) proxies.next();
- proxy.addModelChangedListener(listener);
- }
}
/**
@@ -1364,11 +1409,6 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
*/
void removeModelChangedListener(IModelChangedListener listener) {
fModelListeners.remove(listener);
- Iterator proxies = fModelProxies.values().iterator();
- while (proxies.hasNext()) {
- IModelProxy proxy = (IModelProxy) proxies.next();
- proxy.removeModelChangedListener(listener);
- }
}
/**
@@ -1408,7 +1448,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
reCreate = new ArrayList();
}
reCreate.add(childrenUpdate);
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && getPresentationContext().getId().equals(DEBUG_PRESENTATION_ID)) {
System.out.println("canceled update in progress handling REMOVE: " + childrenUpdate); //$NON-NLS-1$
}
}
@@ -1424,7 +1464,7 @@ abstract class ModelContentProvider implements IContentProvider, IModelChangedLi
IChildrenUpdate childrenUpdate = (IChildrenUpdate) update;
if (childrenUpdate.getOffset() > modelIndex) {
((ChildrenUpdate)childrenUpdate).setOffset(childrenUpdate.getOffset() - 1);
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("modified waiting update handling REMOVE: " + childrenUpdate); //$NON-NLS-1$
}
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
index 30b010fdd..89a7634f7 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelContentProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -8,29 +8,18 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River Systems - Fix for viewer state save/restore [188704]
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.Set;
-
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementContentProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDeltaVisitor;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationContext;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
-import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
-import org.eclipse.jface.viewers.IBasicPropertyConstants;
import org.eclipse.jface.viewers.ILazyTreePathContentProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
-import org.eclipse.jface.viewers.TreeViewer;
-import org.eclipse.swt.widgets.Item;
-import org.eclipse.swt.widgets.Tree;
-import org.eclipse.swt.widgets.TreeItem;
-import org.eclipse.swt.widgets.Widget;
/**
* Content provider for a virtual tree.
@@ -39,8 +28,6 @@ import org.eclipse.swt.widgets.Widget;
*/
public class TreeModelContentProvider extends ModelContentProvider implements ILazyTreePathContentProvider {
- protected static final String[] STATE_PROPERTIES = new String[]{IBasicPropertyConstants.P_TEXT, IBasicPropertyConstants.P_IMAGE};
-
/**
* Re-filters any filtered children of the given parent element.
*
@@ -61,7 +48,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
Object element = getElement(path);
IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(element);
if (contentAdapter != null) {
- ChildrenCountUpdate request = new ChildrenCountUpdate(this, getTreeViewer().getInput(), path, element, contentAdapter, getPresentationContext());
+ ChildrenCountUpdate request = new ChildrenCountUpdate(this, getViewer().getInput(), path, element, contentAdapter, getPresentationContext());
schedule(request);
}
}
@@ -70,7 +57,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
Object parent = getElement(parentPath);
IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(parent);
if (contentAdapter != null) {
- ChildrenUpdate request = new ChildrenUpdate(this, getTreeViewer().getInput(), parentPath, parent, modelIndex, contentAdapter, getPresentationContext());
+ ChildrenUpdate request = new ChildrenUpdate(this, getViewer().getInput(), parentPath, parent, modelIndex, contentAdapter, getPresentationContext());
schedule(request);
}
}
@@ -79,7 +66,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
Object element = getElement(path);
IElementContentProvider contentAdapter = ViewerAdapterService.getContentProvider(element);
if (contentAdapter != null) {
- HasChildrenUpdate request = new HasChildrenUpdate(this, getTreeViewer().getInput(), path, element, contentAdapter, getPresentationContext());
+ HasChildrenUpdate request = new HasChildrenUpdate(this, getViewer().getInput(), path, element, contentAdapter, getPresentationContext());
schedule(request);
}
}
@@ -88,26 +75,26 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#getPresentationContext()
*/
protected IPresentationContext getPresentationContext() {
- return ((TreeModelViewer)getViewer()).getPresentationContext();
+ return ((ITreeModelViewer)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.viewers.model.provisional.viewers.ModelContentProvider#handleAdd(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta)
*/
protected void handleAdd(IModelDelta delta) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("handleAdd(" + delta.getElement() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
- doUpdateChildCount(getViewerTreePath(delta.getParentDelta()));
+ IModelDelta parentDelta = delta.getParentDelta();
+ TreePath parentPath = getViewerTreePath(parentDelta);
+ int count = parentDelta.getChildCount();
+ if (count > 0) {
+ setModelChildCount(parentPath, count);
+ int viewCount = modelToViewChildCount(parentPath, count);
+ getViewer().setChildCount(parentPath, viewCount);
+ } else {
+ doUpdateChildCount(getViewerTreePath(delta.getParentDelta()));
+ }
}
/* (non-Javadoc)
@@ -122,7 +109,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
TreePath treePath = getViewerTreePath(delta);
cancelSubtreeUpdates(treePath);
- getTreeViewer().refresh(getElement(treePath));
+ getViewer().refresh(getElement(treePath));
}
/* (non-Javadoc)
@@ -130,7 +117,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
protected void handleCollapse(IModelDelta delta) {
TreePath elementPath = getViewerTreePath(delta);
- getTreeViewer().setExpandedState(elementPath, false);
+ getViewer().setExpandedState(elementPath, false);
}
/* (non-Javadoc)
@@ -142,13 +129,23 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
if (parentDelta != null) {
handleExpand(parentDelta);
expand(delta);
+ } else {
+ int childCount = delta.getChildCount();
+ TreePath elementPath = getViewerTreePath(delta);
+ if (childCount > 0) {
+ int viewCount = modelToViewChildCount(elementPath, childCount);
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
+ System.out.println("[expand] setChildCount(" + delta.getElement() + ", (model) " + childCount + " (view) " + viewCount); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
+ }
+ getViewer().setChildCount(elementPath, viewCount);
+ }
}
}
protected void expand(IModelDelta delta) {
int childCount = delta.getChildCount();
int modelIndex = delta.getIndex();
- TreeViewer treeViewer = getTreeViewer();
+ ITreeModelContentProviderTarget treeViewer = getViewer();
TreePath elementPath = getViewerTreePath(delta);
if (modelIndex >= 0) {
TreePath parentPath = elementPath.getParentPath();
@@ -157,7 +154,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
int viewIndex = modelToViewIndex(parentPath, modelIndex);
if (viewIndex >= 0) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[expand] replace(" + delta.getParentDelta().getElement() + ", (model) " + modelIndex + " (view) " + viewIndex + ", " + delta.getElement()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
treeViewer.replace(parentPath, viewIndex, delta.getElement());
@@ -172,7 +169,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
if (childCount > 0) {
int viewCount = modelToViewChildCount(elementPath, childCount);
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[expand] setChildCount(" + delta.getElement() + ", (model) " + childCount + " (view) " + viewCount); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
treeViewer.setChildCount(elementPath, viewCount);
@@ -195,7 +192,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
protected int unfilterElement(TreePath parentPath, Object element, int modelIndex) {
// Element is filtered - if no longer filtered, insert the element
if (shouldFilter(parentPath, element)) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[unfilter] abort unfilter element: " + element + ", (model) " + modelIndex); //$NON-NLS-1$ //$NON-NLS-2$
}
// still filtered, stop
@@ -205,10 +202,10 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
clearFilteredChild(parentPath, modelIndex);
int viewIndex = modelToViewIndex(parentPath, modelIndex);
if (viewIndex >= 0) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[unfilter] insert(" + parentPath.getLastSegment() + ", (model) " + modelIndex + " (view) " + viewIndex + ", " + element); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
- getTreeViewer().insert(parentPath, element, viewIndex);
+ getViewer().insert(parentPath, element, viewIndex);
return viewIndex;
} else {
// still filtered - should not happen
@@ -221,25 +218,19 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
protected void handleInsert(IModelDelta delta) {
// TODO: filters
- getTreeViewer().insert(getViewerTreePath(delta.getParentDelta()), delta.getElement(), delta.getIndex());
+ getViewer().insert(getViewerTreePath(delta.getParentDelta()), delta.getElement(), delta.getIndex());
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleRemove(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta)
*/
protected void handleRemove(IModelDelta delta) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("handleRemove(" + delta.getElement() + ")"); //$NON-NLS-1$ //$NON-NLS-2$
}
IModelDelta parentDelta = delta.getParentDelta();
- InternalTreeModelViewer treeViewer = (InternalTreeModelViewer)getViewer();
+ ITreeModelContentProviderTarget treeViewer = getViewer();
TreePath parentPath = getViewerTreePath(parentDelta);
- Widget parentItem = treeViewer.findItem(parentPath);
- if (parentItem == null) {
- // if we can't see the parent, nothing to worry about (but clear the filters, if any)
- clearFilters(parentPath);
- return;
- }
Object element = delta.getElement();
if (removeElementFromFilters(parentPath, element)) {
// element was filtered - done
@@ -250,42 +241,38 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
int unmappedIndex = -1;
int itemCount = -1;
if (modelIndex < 0) {
- // index not provided by delta
- Item[] children = treeViewer.getChildren(parentItem);
- itemCount = children.length;
- for (int i = 0; i < children.length; i++) {
- Item item = children[i];
- Object data = item.getData();
- if (element.equals(data)) {
- viewIndex = i;
- modelIndex = viewToModelIndex(parentPath, i);
- break;
- } else if (data == null) {
- unmappedIndex = i;
- }
- }
+ itemCount = treeViewer.getChildCount(parentPath);
+ if (itemCount == -1) {
+ clearFilters(parentPath);
+ }
+ viewIndex = treeViewer.findElementIndex(parentPath, element);
+ if (viewIndex > 0) {
+ modelIndex = viewToModelIndex(parentPath, viewIndex);
+ } else {
+ unmappedIndex = treeViewer.findElementIndex(parentPath, null);
+ }
} else {
viewIndex = modelToViewIndex(parentPath, modelIndex);
}
if (modelIndex >= 0) {
// found the element
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println(" - (found) remove(" + parentPath.getLastSegment() + ", viewIndex: " + viewIndex + " modelIndex: " + modelIndex); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
rescheduleUpdates(parentPath, modelIndex);
- getTreeViewer().remove(parentPath, viewIndex);
+ getViewer().remove(parentPath, viewIndex);
removeElementFromFilters(parentPath, modelIndex);
return;
}
if (unmappedIndex >= 0) {
// did not find the element, but found an unmapped item.
// remove the unmapped item in it's place and update filters
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println(" - (not found) remove(" + parentPath.getLastSegment() + ", viewIndex: " + viewIndex + " modelIndex: " + modelIndex); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
modelIndex = viewToModelIndex(parentPath, unmappedIndex);
rescheduleUpdates(parentPath, modelIndex);
- getTreeViewer().remove(parentPath, unmappedIndex);
+ getViewer().remove(parentPath, unmappedIndex);
removeElementFromFilters(parentPath, modelIndex);
return;
}
@@ -298,12 +285,12 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
// failing that, refresh the parent to properly update for non-visible/unmapped children
// and update filtered indexes
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println(" - (not found) remove/refresh(" + delta.getElement()); //$NON-NLS-1$
}
- getTreeViewer().remove(getViewerTreePath(delta));
+ getViewer().remove(getViewerTreePath(delta));
clearFilters(parentPath);
- getTreeViewer().refresh(parentDelta.getElement());
+ getViewer().refresh(parentDelta.getElement());
}
/* (non-Javadoc)
@@ -311,7 +298,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
protected void handleReplace(IModelDelta delta) {
TreePath parentPath = getViewerTreePath(delta.getParentDelta());
- getTreeViewer().replace(parentPath, delta.getIndex(), delta.getElement());
+ getViewer().replace(parentPath, delta.getIndex(), delta.getElement());
}
/* (non-Javadoc)
@@ -319,7 +306,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
protected void handleSelect(IModelDelta delta) {
int modelIndex = delta.getIndex();
- TreeViewer treeViewer = getTreeViewer();
+ ITreeModelContentProviderTarget treeViewer = getViewer();
if (modelIndex >= 0) {
IModelDelta parentDelta = delta.getParentDelta();
TreePath parentPath = getViewerTreePath(parentDelta);
@@ -329,25 +316,25 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
int modelCount = parentDelta.getChildCount();
if (modelCount > 0) {
int viewCount = modelToViewChildCount(parentPath, modelCount);
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[select] setChildCount(" + parentDelta.getElement() + ", (model) " + parentDelta.getChildCount() + " (view) " + viewCount ); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
treeViewer.setChildCount(parentPath, viewCount);
}
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[select] replace(" + parentDelta.getElement() + ", (model) " + modelIndex + " (view) " + viewIndex + ", " + delta.getElement()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
treeViewer.replace(parentPath, viewIndex, delta.getElement());
}
}
- treeViewer.setSelection(new TreeSelection(getViewerTreePath(delta)));
+ treeViewer.setSelection(new TreeSelection(getViewerTreePath(delta)), false);
}
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#handleState(org.eclipse.debug.internal.ui.viewers.provisional.IModelDelta)
*/
protected void handleState(IModelDelta delta) {
- getTreeViewer().update(delta.getElement(), STATE_PROPERTIES);
+ getViewer().update(delta.getElement());
}
/* (non-Javadoc)
@@ -363,7 +350,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
protected void reveal(IModelDelta delta) {
int modelIndex = delta.getIndex();
- InternalTreeModelViewer treeViewer = (InternalTreeModelViewer) getTreeViewer();
+ ITreeModelContentProviderTarget treeViewer = getViewer();
TreePath elementPath = getViewerTreePath(delta);
if (modelIndex >= 0) {
TreePath parentPath = elementPath.getParentPath();
@@ -372,7 +359,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
int viewIndex = modelToViewIndex(parentPath, modelIndex);
if (viewIndex >= 0) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("[reveal] replace(" + delta.getParentDelta().getElement() + ", (model) " + modelIndex + " (view) " + viewIndex + ", " + delta.getElement()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
}
treeViewer.replace(parentPath, viewIndex, delta.getElement());
@@ -386,10 +373,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
// only move tree based on selection policy
if (treeViewer.overrideSelection(treeViewer.getSelection(), new TreeSelection(elementPath))) {
- Widget item = treeViewer.findItem(elementPath);
- if (item instanceof TreeItem) {
- treeViewer.getTree().setTopItem((TreeItem) item);
- }
+ treeViewer.reveal(parentPath, viewIndex);
}
}
}
@@ -398,75 +382,33 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.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(EMPTY_TREE_PATH, delta, items[i], set, i);
- }
+ ITreeModelContentProviderTarget viewer = getViewer();
+ viewer.saveElementState(EMPTY_TREE_PATH, delta);
+
// Add memento for top item if it is mapped to an element. The reveal memento
// is in its own path to avoid requesting unnecessary data when restoring it.
- TreeItem topItem = tree.getTopItem();
- if (topItem != null && topItem.getData() != null) {
- LinkedList itemsInPath = new LinkedList();
- TreeItem item = topItem;
- while (item != null) {
- itemsInPath.addFirst(item);
- item = item.getParentItem();
- }
- ModelDelta parentDelta = delta;
- for (Iterator itr = itemsInPath.iterator(); itr.hasNext();) {
- TreeItem next = (TreeItem)itr.next();
- Object element = next.getData();
- int index = next.getParentItem() == null ? tree.indexOf(next) : next.getParentItem().indexOf(next);
- ModelDelta childDelta = parentDelta.getChildDelta(element);
- if (childDelta == null) {
- parentDelta = parentDelta.addNode(element, index, IModelDelta.NO_CHANGE);
- } else {
- parentDelta = childDelta;
- }
- }
- parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.REVEAL);
- }
- }
-
- /**
- * @param delta parent delta to build on
- * @param item item
- * @param set set of selected tree items
- * @param index the item's index relative to it's parent
- */
- private void buildViewerState(TreePath parentPath, ModelDelta delta, TreeItem item, Set set, int index) {
- 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;
- }
- int modelIndex = viewToModelIndex(parentPath, index);
- TreePath elementPath = parentPath.createChildPath(element);
- int numChildren = viewToModelCount(elementPath, item.getItemCount());
- ModelDelta childDelta = delta.addNode(element, modelIndex, flags, numChildren);
- if (expanded) {
- TreeItem[] items = item.getItems();
- for (int i = 0; i < items.length; i++) {
- buildViewerState(elementPath, childDelta, items[i], set, i);
- }
- }
- }
+ if (viewer.getInput() != null) {
+ TreePath topElementPath = viewer.getTopElementPath();
+ if (topElementPath != null) {
+ ModelDelta parentDelta = delta;
+ TreePath parentPath = EMPTY_TREE_PATH;
+ for (int i = 0; i < topElementPath.getSegmentCount(); i++) {
+ Object element = topElementPath.getSegment(i);
+ int index = viewer.findElementIndex(parentPath, element);
+ ModelDelta childDelta = parentDelta.getChildDelta(element);
+ if (childDelta == null) {
+ parentDelta = parentDelta.addNode(element, index, IModelDelta.NO_CHANGE);
+ } else {
+ parentDelta = childDelta;
+ }
+ parentPath = parentPath.createChildPath(element);
+ }
+ parentDelta.setFlags(parentDelta.getFlags() | IModelDelta.REVEAL);
+ }
}
}
+
/* (non-Javadoc)
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.viewers.ModelContentProvider#doInitialRestore()
*/
@@ -479,11 +421,9 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
// Note (Pawel Piech): the initial list of items is normally
// empty, so in most cases the code below does not do anything.
// Instead doRestore() is called when various updates complete.
- 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();
+ int count = getViewer().getChildCount(TreePath.EMPTY);
+ for (int i = 0; i < count; i++) {
+ Object data = getViewer().getChildElement(TreePath.EMPTY, i);
if (data != null) {
doRestore(new TreePath(new Object[]{data}), i, false, false);
}
@@ -535,7 +475,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
* @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#updateChildCount(org.eclipse.jface.viewers.TreePath, int)
*/
public synchronized void updateChildCount(TreePath treePath, int currentChildCount) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("updateChildCount(" + getElement(treePath) + ", " + currentChildCount + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
refilterChildren(treePath);
@@ -549,7 +489,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
public synchronized void updateElement(TreePath parentPath, int viewIndex) {
int modelIndex = viewToModelIndex(parentPath, viewIndex);
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("updateElement("+ getElement(parentPath) + ", " + viewIndex + ") > modelIndex = " + modelIndex); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
doUpdateElement(parentPath, modelIndex);
@@ -559,7 +499,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
* @see org.eclipse.jface.viewers.ILazyTreePathContentProvider#updateHasChildren(org.eclipse.jface.viewers.TreePath)
*/
public synchronized void updateHasChildren(TreePath path) {
- if (DEBUG_CONTENT_PROVIDER) {
+ if (DEBUG_CONTENT_PROVIDER && (DEBUG_PRESENTATION_ID == null || DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("updateHasChildren(" + getElement(path)); //$NON-NLS-1$
}
doUpdateHasChildren(path);
@@ -570,7 +510,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
*/
void doRestore(ModelDelta delta, boolean knowsHasChildren, boolean knowsChildCount) {
TreePath treePath = getViewerTreePath(delta);
- InternalTreeModelViewer viewer = (InternalTreeModelViewer)getViewer();
+ ITreeModelContentProviderTarget viewer = getViewer();
// Attempt to expand the node only if the children are known.
if (knowsHasChildren && (delta.getFlags() & IModelDelta.EXPAND) != 0) {
viewer.expandToLevel(treePath, 1);
@@ -596,9 +536,10 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
}
if (setTopItem) {
- Widget topItem = viewer.findItem(treePath);
- if (topItem instanceof TreeItem) {
- viewer.getTree().setTopItem((TreeItem) topItem);
+ TreePath parentPath = treePath.getParentPath();
+ int index = viewer.findElementIndex(parentPath, treePath.getLastSegment());
+ if (index >= 0) {
+ viewer.reveal(parentPath, index);
}
}
}
@@ -609,13 +550,7 @@ public class TreeModelContentProvider extends ModelContentProvider implements IL
// If the child delta's index is out of range, strip the reveal flag
// since it is no longer applicable.
if (knowsChildCount) {
- Widget item = viewer.findItem(treePath);
- int childCount = -1;
- if (item instanceof TreeItem) {
- childCount = ((TreeItem)item).getItemCount();
- } else if (item instanceof Tree) {
- childCount = ((Tree)item).getItemCount();
- }
+ int childCount = viewer.getChildCount(treePath);
if (childCount >= 0) {
ModelDelta[] childDeltas = (ModelDelta[])delta.getChildDeltas();
for (int i = 0; i < childDeltas.length; i++) {
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java
index 13ff585d8..1a303b60d 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/TreeModelLabelProvider.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2007 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River - Pawel Piech - Added coalescing of label updates (bug 247575).
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -32,14 +33,12 @@ import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ColumnLabelProvider;
import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.ViewerCell;
-import org.eclipse.jface.viewers.ViewerRow;
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.swt.widgets.TreeItem;
import org.eclipse.ui.progress.UIJob;
/**
@@ -47,7 +46,7 @@ import org.eclipse.ui.progress.UIJob;
*/
public class TreeModelLabelProvider extends ColumnLabelProvider {
- private InternalTreeModelViewer fViewer;
+ private ITreeModelLabelProviderTarget fViewer;
private List fComplete;
/**
@@ -105,7 +104,7 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
/**
* Constructs a new label provider on the given display
*/
- public TreeModelLabelProvider(InternalTreeModelViewer viewer) {
+ public TreeModelLabelProvider(ITreeModelLabelProviderTarget viewer) {
fViewer = viewer;
}
@@ -134,7 +133,7 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
* @return display
*/
private Display getDisplay() {
- return fViewer.getControl().getDisplay();
+ return fViewer.getDisplay();
}
/**
@@ -220,7 +219,7 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
// NOT USED - the viewer updates each row instead
}
- public synchronized void update(TreePath elementPath, ViewerRow row) {
+ public synchronized boolean update(TreePath elementPath) {
String[] visibleColumns = fViewer.getVisibleColumns();
Object element = elementPath.getLastSegment();
IElementLabelProvider presentation = ViewerAdapterService.getLabelProvider(element);
@@ -230,11 +229,11 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
updates = new LinkedList();
fPendingUpdates.put(presentation, updates);
}
- updates.add(new LabelUpdate(fViewer.getInput(), elementPath, (TreeItem) row.getItem(), this, visibleColumns, fViewer.getPresentationContext()));
+ updates.add(new LabelUpdate(fViewer.getInput(), elementPath, this, fViewer, visibleColumns, fViewer.getPresentationContext()));
if (fPendingUpdatesJob != null) {
fPendingUpdatesJob.cancel();
}
- fPendingUpdatesJob = new UIJob(fViewer.getControl().getDisplay(), "Schedule Pending Label Updates") { //$NON-NLS-1$
+ fPendingUpdatesJob = new UIJob(fViewer.getDisplay(), "Schedule Pending Label Updates") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
startRequests(this);
return Status.OK_STATUS;
@@ -242,10 +241,10 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
};
fPendingUpdatesJob.setSystem(true);
fPendingUpdatesJob.schedule();
- } else if (element instanceof String) {
- // for example, expression error messages
- row.setText(0, (String)element);
- }
+ return true;
+ } else {
+ return false;
+ }
}
private void startRequests(UIJob updateJob) {
@@ -333,12 +332,12 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
fUpdatesInProgress.add(update);
}
if (begin) {
- if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE) {
+ if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("LABEL SEQUENCE BEGINS"); //$NON-NLS-1$
}
notifyUpdate(ModelContentProvider.UPDATE_SEQUENCE_BEGINS, null);
}
- if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE) {
+ if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("\tBEGIN - " + update); //$NON-NLS-1$
}
notifyUpdate(ModelContentProvider.UPDATE_BEGINS, update);
@@ -355,12 +354,12 @@ public class TreeModelLabelProvider extends ColumnLabelProvider {
fUpdatesInProgress.remove(update);
end = fUpdatesInProgress.isEmpty();
}
- if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE) {
+ if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("\tEND - " + update); //$NON-NLS-1$
}
notifyUpdate(ModelContentProvider.UPDATE_COMPLETE, update);
if (end) {
- if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE) {
+ if (ModelContentProvider.DEBUG_UPDATE_SEQUENCE && (ModelContentProvider.DEBUG_PRESENTATION_ID == null || ModelContentProvider.DEBUG_PRESENTATION_ID.equals(getPresentationContext().getId()))) {
System.out.println("LABEL SEQUENCE ENDS"); //$NON-NLS-1$
}
notifyUpdate(ModelContentProvider.UPDATE_SEQUENCE_COMPLETE, null);
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java
index 1d68c19d0..9a137d227 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/ViewerUpdateMonitor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006, 2008 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added support for a virtual tree model viewer (Bug 242489)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -69,23 +70,9 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda
* Presentation context
*/
private IPresentationContext fContext;
+
- 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()) {
- performUpdate();
- }
- }
- } finally {
- getContentProvider().updateComplete(ViewerUpdateMonitor.this);
- }
- return Status.OK_STATUS;
- }
- };
+ protected WorkbenchJob fViewerUpdateJob;
/**
* Constructs an update for the given content provider
@@ -102,6 +89,22 @@ public abstract class ViewerUpdateMonitor extends Request implements IViewerUpda
fElement = element;
fElementPath = elementPath;
// serialize updates per viewer
+ fViewerUpdateJob = new WorkbenchJob(contentProvider.getViewer().getDisplay(), "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()) {
+ performUpdate();
+ }
+ }
+ } finally {
+ getContentProvider().updateComplete(ViewerUpdateMonitor.this);
+ }
+ return Status.OK_STATUS;
+ }
+ };
fViewerUpdateJob.setRule(getUpdateSchedulingRule());
fViewerUpdateJob.setSystem(true);
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java
index 41207a119..dccec9f0b 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualFindAction.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2004, 2008 IBM Corporation and others.
+ * Copyright (c) 2004, 2009 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial implementation
+ * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model;
@@ -133,7 +134,7 @@ public class VirtualFindAction extends Action implements IUpdate {
VirtualElement element = (VirtualElement)elements[0];
TreePath path = element.realize();
if (path != null) {
- fViewer.setSelection(new TreeSelection(path));
+ fViewer.setSelection(new TreeSelection(path), true, true);
} else {
DebugUIPlugin.errorDialog(fViewer.getControl().getShell(), ActionMessages.VirtualFindAction_0,
MessageFormat.format(ActionMessages.VirtualFindAction_1, new String[]{element.getLabel()[0]}),
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java
new file mode 100644
index 000000000..337ef7197
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualItem.java
@@ -0,0 +1,387 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * Virtual item, which is analogous to the SWT's tree item.
+ *
+ * @since 3.5
+ */
+class VirtualItem {
+
+ // Data keys for display attributes of an item.
+ static String LABEL_KEY = "LABEL_KEY"; //$NON-NLS-1$
+ static String IMAGE_KEY = "IMAGE_KEY"; //$NON-NLS-1$
+ static String FONT_KEY = "FONT_KEY"; //$NON-NLS-1$
+ static String FOREGROUND_KEY = "FOREGROUND_KEY"; //$NON-NLS-1$
+ static String BACKGROUND_KEY = "BACKGROUND_KEY"; //$NON-NLS-1$
+
+ static String ELEMENT_DATA_KEY = "element"; //$NON-NLS-1$
+
+ /**
+ * Index object of a tree item. It allows the indexes to be modified
+ * as items are inserted and removed.
+ */
+ public static class Index implements Comparable {
+ private Integer fIndexValue;
+
+ public Index(int index) {
+ fIndexValue = new Integer(index);
+ }
+
+ public boolean equals(Object obj) {
+ return obj instanceof Index && ((Index)obj).fIndexValue.equals(fIndexValue);
+ }
+
+ public int hashCode() {
+ return fIndexValue.hashCode();
+ }
+
+ public void increment() {
+ fIndexValue = new Integer(fIndexValue.intValue() + 1);
+ }
+
+ public void decrement() {
+ fIndexValue = new Integer(fIndexValue.intValue() - 1);
+ }
+
+ public int intValue() {
+ return fIndexValue.intValue();
+ }
+
+ public int compareTo(Object obj) {
+ return obj instanceof Index ? ((Index)obj).fIndexValue.compareTo(fIndexValue) : 0;
+ }
+
+ public String toString() {
+ return fIndexValue.toString();
+ }
+ }
+
+ /**
+ * Parent items of this item.
+ */
+ final private VirtualItem fParent;
+
+ /**
+ * The index of this item.
+ */
+ final private Index fIndex;
+
+ /**
+ * Map of child items. The key to the map is the item's index, which
+ * must be the same object instance as the index in the item. The tree map
+ * keeps the items sorted while allowing indexes (keys) to be modified as
+ * child items are inserted and removed.
+ */
+ private Map fItems = new TreeMap();
+
+ /**
+ * Flag indicating whether this item has child items.
+ */
+ private boolean fHasItems = false;
+
+ /**
+ * Indicates that this item has been expanded. It should only
+ * be set to <code>true</code> if fHasItems is <code>true</code>.
+ */
+ private boolean fExpanded = false;
+
+ /**
+ * The cound of child items. <code>-1</code> indicates that the count
+ * is not known.
+ */
+ private int fItemCount = -1;
+
+ /**
+ * The data held by this item. It includes the element as well as the item
+ * display attributes.
+ */
+ private Map fData = new HashMap(1);
+
+ /**
+ * Flag indicating that the item needs to have it's label updated.
+ */
+ private boolean fNeedsLabelUpdate = true;
+
+ /**
+ * Flag indicating that the item's count needs to be updated.
+ */
+ private boolean fNeedsCountUpdate = true;
+
+ /**
+ * Flag indicating that the item's element needs to be updated.
+ */
+ private boolean fNeedsDataUpdate = true;
+
+ /**
+ * Indicates that this item has been disposed.
+ */
+ private boolean fDisposed = false;
+
+
+ VirtualItem(VirtualItem parent, Index index) {
+ fParent = parent;
+ fIndex = index;
+ }
+
+ void setNeedsCountUpdate() {
+ fNeedsCountUpdate = true;
+ fItemCount = -1;
+ }
+
+ void setNeedsLabelUpdate() {
+ fNeedsLabelUpdate = true;
+ }
+
+ void setNeedsDataUpdate() {
+ fNeedsDataUpdate = true;
+ }
+
+ void clear(Index index) {
+ VirtualItem item = (VirtualItem)fItems.remove(index);
+ if (item != null) {
+ item.dispose();
+ }
+ }
+
+ VirtualItem getParent() {
+ return fParent;
+ }
+
+ Index getIndex() {
+ return fIndex;
+ }
+
+ VirtualItem findItem(Object element) {
+ for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
+ VirtualItem next = (VirtualItem)itr.next();
+ Object nextData = next.getData();
+ if ( (element != null && element.equals(nextData)) || (element == null && nextData == null) ) {
+ return next;
+ }
+ }
+ return null;
+ }
+
+ boolean needsDataUpdate() {
+ return fNeedsDataUpdate;
+ }
+
+ void clearNeedsDataUpdate() {
+ fNeedsDataUpdate = false;
+ }
+
+ boolean needsCountUpdate() {
+ return fNeedsCountUpdate;
+ }
+
+ void clearNeedsCountUpdate() {
+ fNeedsCountUpdate = false;
+ }
+
+ boolean needsLabelUpdate() {
+ return fNeedsLabelUpdate;
+ }
+
+ void clearNeedsLabelUpdate() {
+ fNeedsLabelUpdate = false;
+ }
+
+ boolean isDisposed() {
+ return fDisposed;
+ }
+
+ void dispose() {
+ fData.clear();
+ for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
+ ((VirtualItem)itr.next()).dispose();
+ }
+ fItems.clear();
+
+ fDisposed = true;
+ findTree().fireItemDisposed(this);
+ }
+
+ Object getData (String key) {
+ return fData.get(key);
+ }
+
+ void setData(String key, Object data) {
+ fData.put(key, data);
+ }
+
+ void setData(Object data) {
+ fData.put(ELEMENT_DATA_KEY, data);
+ if (data == null) {
+ fNeedsDataUpdate = true;
+ }
+ }
+
+ Object getData () {
+ return fData.get(ELEMENT_DATA_KEY);
+ }
+
+ void setExpanded(boolean expanded) {
+ fExpanded = expanded;
+
+ //Assert.assrt(!fExpanded || hasItems());
+
+ // If collapsed, make sure that all the children are collapsed as well.
+ if (!fExpanded) {
+ for (Iterator itr = fItems.values().iterator(); itr.hasNext();) {
+ ((VirtualItem)itr.next()).setExpanded(expanded);
+ }
+ }
+ }
+
+ boolean getExpanded() {
+ return fExpanded;
+ }
+
+ void setHasItems(boolean hasChildren) {
+ fHasItems = hasChildren;
+ if (!fHasItems) {
+ if (getItemCount() != 0) {
+ setItemCount(0);
+ }
+ } else if (getItemCount() == 0) {
+ setItemCount(-1);
+ }
+ }
+
+ boolean hasItems() {
+ return fHasItems;
+ }
+
+ void setItemCount(int count) {
+ fItemCount = count;
+ for (Iterator itr = fItems.entrySet().iterator(); itr.hasNext();) {
+ Map.Entry entry = (Map.Entry)itr.next();
+ int index = ((Index)entry.getKey()).intValue();
+ if (index >= count) {
+ itr.remove();
+ VirtualItem item = (VirtualItem)entry.getValue();
+ item.dispose();
+ }
+ }
+ if (fItemCount == 0) {
+ if (hasItems()) {
+ setHasItems(false);
+ }
+ if (getExpanded()) {
+ setExpanded(false);
+ }
+ } else {
+ if (!hasItems()) {
+ setHasItems(true);
+ }
+ }
+ }
+
+ int getItemCount() {
+ return fItemCount;
+ }
+
+ VirtualItem getItem(Index index) {
+ ensureItems();
+
+ VirtualItem item = (VirtualItem)fItems.get(index);
+ if (item == null) {
+ item = new VirtualItem(this, index);
+ fItems.put(index, item);
+ }
+ return item;
+ }
+
+ VirtualItem[] getItems() {
+ return (VirtualItem[]) fItems.values().toArray(new VirtualItem[fItems.size()]);
+ }
+
+ VirtualItem addItem(int position) {
+ if (!fHasItems) {
+ fHasItems = true;
+ }
+ if (fItemCount < 0) {
+ fItemCount = 0;
+ }
+
+ // Increment all items with an index higher than the given position.
+ fItemCount++;
+ ensureItems();
+ for (Iterator itr = fItems.keySet().iterator(); itr.hasNext();) {
+ Index childIndex = (Index)itr.next();
+ if (childIndex.intValue() >= position) {
+ childIndex.increment();
+ }
+ }
+
+ // Note: the same index object used to create the item has to
+ // be used as the key into the map.
+ Index childIndex = new Index(position);
+ VirtualItem newChild = new VirtualItem(this, childIndex);
+ fItems.put(childIndex, newChild);
+ return newChild;
+ }
+
+ void remove(Index position) {
+ fItemCount--;
+ if (fItemCount < 0) {
+ fHasItems = false;
+ }
+
+ ensureItems();
+
+ VirtualItem removedItem = null;
+ for (Iterator itr = fItems.entrySet().iterator(); itr.hasNext();) {
+ Map.Entry entry = (Map.Entry)itr.next();
+ Index childIndex = (Index)entry.getKey();
+ if (childIndex.intValue() > position.intValue()) {
+ childIndex.decrement();
+ } else if (childIndex.intValue() == position.intValue()) {
+ removedItem = (VirtualItem)entry.getValue();
+ removedItem.dispose();
+ itr.remove();
+ }
+ }
+ }
+
+ private void ensureItems() {
+ if (fItems == null) {
+ fItems = new HashMap( Math.max(1, Math.min(fItemCount, 16)) );
+ }
+ }
+
+ private VirtualTree findTree() {
+ VirtualItem item = this;
+ while (!(item instanceof VirtualTree)) {
+ item = item.fParent;
+ }
+ return (VirtualTree)item;
+ }
+
+ public String toString() {
+ String[] label = (String[])fData.get(LABEL_KEY);
+ if (label != null && label.length != 0) {
+ return label[0];
+ }
+ Object data = fData.get(ELEMENT_DATA_KEY);
+ if (data != null) {
+ return data.toString();
+ }
+ return super.toString();
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java
new file mode 100644
index 000000000..89c2f366f
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/VirtualTree.java
@@ -0,0 +1,209 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model;
+
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Set;
+
+import org.eclipse.swt.SWT;
+
+
+/**
+ * Tree of virtual items that is analogous to SWT's tree control.
+ *
+ * @since 3.5
+ */
+class VirtualTree extends VirtualItem {
+
+ /**
+ * Lazy virtual tree does not retrieve elements or labels,
+ * except for the selected elements.
+ */
+ private boolean fLazy;
+
+ /**
+ * The item currently at the top of the "virtual" view-port.
+ */
+ private VirtualItem fTopItem;
+
+ /**
+ * Interface for listeners that need to be notified when items
+ * are disposed or revealed. It should be implemented by the viewer.
+ */
+ public static interface IVirtualItemListener {
+
+ /**
+ * Called when the item has been shown in the virtual viewer's
+ * view-port. This indicates to the viewer that it should check
+ * the item's status and request needed data.
+ *
+ * @param item The item that was revealed.
+ */
+ public void revealed(VirtualItem item);
+
+ /**
+ * Called when an item is disposed. It tells the viewer to
+ * clean up any remaining mappings and cached data of this item.
+ *
+ * @param item The itam that was disposed.
+ */
+ public void disposed(VirtualItem item);
+ }
+
+ /**
+ * Set of listeners of the virtual tree.
+ */
+ private Set fVirtualItemListeners = new HashSet(1);
+
+ /**
+ * The currently selected items. This array contains only
+ * the leaf items which are selected.
+ */
+ private VirtualItem[] fSelection = new VirtualItem[0];
+
+ VirtualTree(int style) {
+ super(null, new VirtualItem.Index(0));
+ fLazy = (style & SWT.VIRTUAL) != 0;
+ clearNeedsLabelUpdate();
+ clearNeedsDataUpdate();
+ }
+
+ void dispose() {
+ super.dispose();
+ fVirtualItemListeners.clear();
+ }
+
+ void setNeedsCountUpdate() {
+ super.setNeedsCountUpdate();
+ clearNeedsLabelUpdate();
+ clearNeedsDataUpdate();
+ }
+
+ void setNeedsLabelUpdate() {
+ // no-op
+ }
+
+ void setData(String key, Object data) {
+ super.setData(key, data);
+ if (data == null) {
+ clearNeedsDataUpdate();
+ }
+ }
+
+ void addItemListener(IVirtualItemListener listener) {
+ fVirtualItemListeners.add(listener);
+ }
+
+ void removeItemListener(IVirtualItemListener listener) {
+ fVirtualItemListeners.remove(listener);
+ }
+
+ VirtualItem[] getSelection() {
+ return fSelection;
+ }
+
+ void setSelection(VirtualItem[] items) {
+ fSelection = items;
+ }
+
+ void showItem(VirtualItem item) {
+ setTopItem(item);
+ }
+
+ void fireItemDisposed(VirtualItem item) {
+ for (Iterator itr = fVirtualItemListeners.iterator(); itr.hasNext();) {
+ ((IVirtualItemListener)itr.next()).disposed(item);
+ }
+ }
+
+ void fireItemRevealed(VirtualItem item) {
+ for (Iterator itr = fVirtualItemListeners.iterator(); itr.hasNext();) {
+ ((IVirtualItemListener)itr.next()).revealed(item);
+ }
+ }
+
+ void setData(Object data) {
+ super.setData(data);
+ // The root item always has children as long as the input is non-null,
+ // so that it should be expanded.
+ setHasItems(data != null);
+ }
+
+ void setTopItem(VirtualItem item) {
+ fTopItem = item;
+ }
+
+ VirtualItem getTopItem() {
+ return fTopItem;
+ }
+
+ void setHasItems(boolean hasChildren) {
+ super.setHasItems(hasChildren);
+ // The root item is always expanded as long as it has children.
+ if (hasChildren) {
+ setExpanded(true);
+ }
+ }
+
+ boolean isItemVisible(VirtualItem item) {
+ if (!fLazy) {
+ // If not in lazy mode, all items are visible.
+ return true;
+ } else {
+ // TODO: use top item and visible item count to determine list of
+ // visible items. For now only mark the selected items as visible.
+ for (int i = 0; i < fSelection.length; i++) {
+ VirtualItem selectionItem = fSelection[i];
+ while (selectionItem != null) {
+ if (item.equals(selectionItem)) {
+ return true;
+ }
+ selectionItem = selectionItem.getParent();
+ }
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Validates the entire tree.
+ */
+ void validate() {
+ validate(VirtualTree.this);
+ }
+
+ /**
+ * Validates the item and its children, identifying children which were
+ * revealed and need to be updated.
+ *
+ * @param item The item which to validate.
+ */
+ void validate(VirtualItem item) {
+ if (item.needsDataUpdate()) {
+ if (isItemVisible(item)) {
+ fireItemRevealed(item);
+ }
+ } else if (item.getData() != null) {
+ if ( item.needsLabelUpdate() || (item.needsCountUpdate() && item.hasItems() && item.getExpanded()) ) {
+ if (isItemVisible(item)) {
+ fireItemRevealed(item);
+ }
+ }
+
+ if (item.getData() != null && item.getItemCount() > 0 && item.getExpanded()) {
+ for (int i = 0; i < item.getItemCount(); i++) {
+ validate(item.getItem(new Index(i)));
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IPresentationContext.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IPresentationContext.java
index 274aa11ce..780d18a6e 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IPresentationContext.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/IPresentationContext.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2008 IBM Corporation and others.
+ * Copyright (c) 2005, 2009 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model.provisional;
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java
index 0ca9602a4..3de2a9760 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/TreeModelViewer.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2006 IBM Corporation and others.
+ * Copyright (c) 2006, 2009 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
@@ -7,29 +7,34 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.model.provisional;
import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ViewerLabel;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.ui.IMemento;
/**
* A tree viewer for a model.
* <p>
- * Clients may instantiate this class. Clients may not subclass this class.
+ * Style flags supported by this viewer are the same as for {@link TreeViewer},
+ * except:
+ * <ul>
+ * <li>SWT.VIRTUAL - Indicates that the viewer should be in lazy mode. This
+ * flag is required when creating this viewer.
+ * <li>SWT.POP_UP - Indicates that the viewer is used in a popup window, and it
+ * should ignore requests from the model to select, expand, or collapse tree
+ * elements.</li>
+ * </ul>
* </p>
- * @since 3.3
+ * @since 3.3
+ * @noextend Clients may instantiate this class. Clients may not subclass this class.
*/
public class TreeModelViewer extends InternalTreeModelViewer {
- /**
- * Constructs a new viewer.
- *
- * @param parent parent widget
- * @param style viewer style bits
- * @param context presentation context
- */
public TreeModelViewer(Composite parent, int style, IPresentationContext context) {
super(parent, style, context);
}
@@ -166,4 +171,8 @@ public class TreeModelViewer extends InternalTreeModelViewer {
public void setVisibleColumns(String[] ids) {
super.setVisibleColumns(ids);
}
+
+ public ViewerLabel getElementLabel(TreePath path, String columnId) {
+ return super.getElementLabel(path, columnId);
+ }
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java
new file mode 100644
index 000000000..3134ab2ac
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/model/provisional/VirtualTreeModelViewer.java
@@ -0,0 +1,174 @@
+/*******************************************************************************
+ * Copyright (c) 2008 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.viewers.model.provisional;
+
+import org.eclipse.debug.internal.ui.viewers.model.InternalVirtualTreeModelViewer;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.ui.IMemento;
+
+/**
+ * A virtual tree model viewer. This viewer does not have a user
+ * interface. Instead, clients configure and access the viewer's data through
+ * its public methods.
+ * <p>
+ * Style flags supported by this viewer are:
+ * <ul>
+ * <li>SWT.VIRTUAL - Indicates that the viewer should be in lazy mode:
+ * retrieving only elements that are requested.</li>
+ * <li>SWT.POP_UP - Indicates that the viewer should ignore requests from the
+ * model to select, expand, or collapse tree elements.</li>
+ * </ul>
+ * </p>
+ * @since 3.5
+ * @noextend
+ */
+public class VirtualTreeModelViewer extends InternalVirtualTreeModelViewer {
+
+
+ public VirtualTreeModelViewer(Display display, int style, IPresentationContext context) {
+ super(display, style, context);
+ }
+
+
+ /**
+ * Returns this viewer's presentation context.
+ *
+ * @return presentation context
+ */
+ public IPresentationContext getPresentationContext() {
+ return super.getPresentationContext();
+ }
+
+ /**
+ * Registers the given listener for model delta notification.
+ *
+ * @param listener model delta listener
+ */
+ public void addModelChangedListener(IModelChangedListener listener) {
+ super.addModelChangedListener(listener);
+ }
+
+ /**
+ * Unregisters the given listener from model delta notification.
+ *
+ * @param listener model delta listener
+ */
+ public void removeModelChangedListener(IModelChangedListener listener) {
+ super.removeModelChangedListener(listener);
+ }
+
+ /**
+ * Registers the specified listener for view update notifications.
+ *
+ * @param listener listener
+ */
+ public void addViewerUpdateListener(IViewerUpdateListener listener) {
+ super.addViewerUpdateListener(listener);
+ }
+
+ /**
+ * Removes the specified listener from update notifications.
+ *
+ * @param listener listener
+ */
+ public void removeViewerUpdateListener(IViewerUpdateListener listener) {
+ super.removeViewerUpdateListener(listener);
+ }
+
+ /**
+ * Returns whether columns can be toggled on/off for this viewer's current
+ * input element.
+ *
+ * @return whether columns can be toggled on/off
+ */
+ public boolean canToggleColumns() {
+ return super.canToggleColumns();
+ }
+
+ /**
+ * 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 super.getColumnPresentation();
+ }
+
+ /**
+ * Returns identifiers of the visible columns in this viewer, or <code>null</code>
+ * if there are currently no columns.
+ *
+ * @return visible columns identifiers or <code>null</code>
+ */
+ public String[] getVisibleColumns() {
+ return super.getVisibleColumns();
+ }
+
+ /**
+ * Initializes viewer state from the memento
+ *
+ * @param memento
+ */
+ public void initState(IMemento memento) {
+ super.initState(memento);
+ }
+
+ /**
+ * Save viewer state into the given memento.
+ *
+ * @param memento
+ */
+ public void saveState(IMemento memento) {
+ super.saveState(memento);
+ }
+
+ /**
+ * Returns whether columns are being displayed currently.
+ *
+ * @return
+ */
+ public boolean isShowColumns() {
+ return super.isShowColumns();
+ }
+
+ /**
+ * 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) {
+ super.setShowColumns(show);
+ }
+
+ /**
+ * Sets the visible columns for this viewer. Id's correspond to
+ * column identifiers from a column presentation. Use <code>null</code>
+ * or an empty collection to display default columns for the current
+ * column presentation. Only effects the current column presentation.
+ *
+ * @param ids column identifiers or <code>null</code>
+ */
+ public void setVisibleColumns(String[] ids) {
+ super.setVisibleColumns(ids);
+ }
+
+ public void updateViewer(IModelDelta delta) {
+ super.updateViewer(delta);
+ }
+
+ public ViewerLabel getElementLabel(TreePath path, String columnId) {
+ return super.getElementLabel(path, columnId);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultSelectionPolicy.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultSelectionPolicy.java
index 5c3f31528..a19cf97d2 100644
--- a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultSelectionPolicy.java
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/viewers/update/DefaultSelectionPolicy.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2005, 2007 IBM Corporation and others.
+ * Copyright (c) 2005, 2009 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
@@ -7,6 +7,7 @@
*
* Contributors:
* IBM Corporation - initial API and implementation
+ * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677)
*******************************************************************************/
package org.eclipse.debug.internal.ui.viewers.update;
@@ -17,6 +18,9 @@ import org.eclipse.debug.internal.ui.viewers.model.provisional.IPresentationCont
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.viewers.ISelection;
import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
/**
* Default selection policy for the debug view.
@@ -103,10 +107,20 @@ public class DefaultSelectionPolicy implements IModelSelectionPolicy {
return false;
}
- /* (non-Javadoc)
+ /**
+ * If an attempt is made to select an invalid element, it usually indicates that the
+ * currently selected element was removed from the model. Instead of leaving the
+ * selection empty, attempt to select the parent element instead.
+ *
* @see org.eclipse.debug.internal.ui.viewers.model.provisional.IModelSelectionPolicy#handleInvalidSelection(org.eclipse.jface.viewers.ISelection, org.eclipse.jface.viewers.ISelection)
*/
public ISelection replaceInvalidSelection(ISelection selection, ISelection newSelection) {
+ if (selection instanceof ITreeSelection) {
+ TreePath[] paths = ((ITreeSelection)selection).getPaths();
+ if (paths.length > 0 && paths[0].getSegmentCount() > 1) {
+ return new TreeSelection(paths[0].getParentPath());
+ }
+ }
return newSelection;
}
}
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 359140044..02ea1b217 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
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2000, 2008 IBM Corporation and others.
+ * Copyright (c) 2000, 2009 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
@@ -8,6 +8,7 @@
* Contributors:
* IBM Corporation - initial API and implementation
* Wind River - Pawel Piech - Busy status while updates in progress (Bug 206822)
+ * Pawel Piech (Wind River) - added a breadcrumb mode to Debug view (Bug 252677)
*******************************************************************************/
package org.eclipse.debug.internal.ui.views.launch;
@@ -67,6 +68,8 @@ import org.eclipse.debug.ui.IDebugModelPresentation;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.debug.ui.contexts.AbstractDebugContextProvider;
import org.eclipse.debug.ui.contexts.DebugContextEvent;
+import org.eclipse.debug.ui.contexts.IDebugContextListener;
+import org.eclipse.debug.ui.contexts.IDebugContextProvider;
import org.eclipse.jface.action.GroupMarker;
import org.eclipse.jface.action.IMenuManager;
import org.eclipse.jface.action.IToolBarManager;
@@ -83,9 +86,12 @@ import org.eclipse.jface.viewers.TreePath;
import org.eclipse.jface.viewers.TreeSelection;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.ControlEvent;
+import org.eclipse.swt.events.ControlListener;
import org.eclipse.swt.events.KeyAdapter;
import org.eclipse.swt.events.KeyEvent;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
import org.eclipse.ui.IMemento;
import org.eclipse.ui.IPageListener;
import org.eclipse.ui.IPartListener2;
@@ -100,9 +106,12 @@ import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.actions.SelectionListenerAction;
import org.eclipse.ui.dialogs.PropertyDialogAction;
+import org.eclipse.ui.part.IPageBookViewPage;
+import org.eclipse.ui.part.IPageSite;
import org.eclipse.ui.part.IShowInSource;
import org.eclipse.ui.part.IShowInTarget;
import org.eclipse.ui.part.IShowInTargetList;
+import org.eclipse.ui.part.Page;
import org.eclipse.ui.part.ShowInContext;
import org.eclipse.ui.progress.IWorkbenchSiteProgressService;
import org.eclipse.ui.progress.UIJob;
@@ -150,7 +159,45 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
private EditSourceLookupPathAction fEditSourceAction = null;
private LookupSourceAction fLookupAction = null;
- class ContextProvider extends AbstractDebugContextProvider implements IModelChangedListener {
+ /**
+ * Page-book page for the breadcrumb viewer. This page is activated in
+ * Debug view when the height of the view is reduced to just one line.
+ */
+ private class BreadcrumbPage extends Page {
+
+ LaunchViewBreadcrumb fCrumb;
+ Control fControl;
+
+ public void createControl(Composite parent) {
+ fCrumb = new LaunchViewBreadcrumb(LaunchView.this, (TreeModelViewer)getViewer(), fTreeViewerDebugContextProvider);
+ fControl = fCrumb.createContent(parent);
+ }
+
+ public void init(IPageSite pageSite) {
+ super.init(pageSite);
+ pageSite.setSelectionProvider(fCrumb.getSelectionProvider());
+ }
+
+ public Control getControl() {
+ return fControl;
+ }
+
+ public void setFocus() {
+ fCrumb.activate();
+ }
+
+ IDebugContextProvider getContextProvider() {
+ return fCrumb.getContextProvider();
+ }
+
+ int getHeight() {
+ return fCrumb.getHeight();
+ }
+ }
+
+ private BreadcrumbPage fBreadcrumbPage;
+
+ class TreeViewerContextProvider extends AbstractDebugContextProvider implements IModelChangedListener {
private ISelection fContext = null;
private TreeModelViewer fViewer = null;
@@ -193,7 +240,7 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
return new TreePath(list.toArray());
}
- public ContextProvider(TreeModelViewer viewer) {
+ public TreeViewerContextProvider(TreeModelViewer viewer) {
super(LaunchView.this);
fViewer = viewer;
fViewer.addModelChangedListener(this);
@@ -247,7 +294,7 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
Job job = new UIJob("context change") { //$NON-NLS-1$
public IStatus runInUIThread(IProgressMonitor monitor) {
// verify selection is still the same context since job was scheduled
- synchronized (ContextProvider.this) {
+ synchronized (TreeViewerContextProvider.this) {
if (fContext instanceof IStructuredSelection) {
IStructuredSelection ss = (IStructuredSelection) fContext;
Object changed = ((IStructuredSelection)finalEvent.getContext()).getFirstElement();
@@ -277,7 +324,62 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
/**
* Context provider
*/
- private ContextProvider fProvider;
+ private TreeViewerContextProvider fTreeViewerDebugContextProvider;
+
+ private ISelectionChangedListener fTreeViewerSelectionChangedListener = new ISelectionChangedListener() {
+ public void selectionChanged(SelectionChangedEvent event) {
+ fTreeViewerDebugContextProvider.activate(event.getSelection());
+ }
+ };
+
+ private class ContextProviderProxy extends AbstractDebugContextProvider implements IDebugContextListener {
+ private IDebugContextProvider fActiveProvider;
+ private IDebugContextProvider[] fProviders;
+
+ ContextProviderProxy(IDebugContextProvider[] providers) {
+ super(LaunchView.this);
+ fProviders = providers;
+ fActiveProvider = providers[0];
+ for (int i = 0; i < fProviders.length; i++) {
+ fProviders[i].addDebugContextListener(this);
+ }
+ }
+
+ void setActiveProvider(IDebugContextProvider provider) {
+ if (!provider.equals(fActiveProvider)) {
+ ISelection activeContext = getActiveContext();
+ fActiveProvider = provider;
+ ISelection newActiveContext = getActiveContext();
+ if (!activeContext.equals(newActiveContext)) {
+ fire(new DebugContextEvent(this, getActiveContext(), DebugContextEvent.ACTIVATED));
+ }
+ }
+ }
+
+ public ISelection getActiveContext() {
+ ISelection activeContext = fActiveProvider.getActiveContext();
+ if (activeContext != null) {
+ return activeContext;
+ }
+ return TreeSelection.EMPTY;
+ }
+
+ public void debugContextChanged(DebugContextEvent event) {
+ if (event.getSource().equals(fActiveProvider)) {
+ fire(new DebugContextEvent(this, event.getContext(), event.getFlags()));
+ }
+ }
+
+ void dispose() {
+ for (int i = 0; i < fProviders.length; i++) {
+ fProviders[i].removeDebugContextListener(this);
+ }
+ fProviders = null;
+ fActiveProvider = null;
+ }
+ }
+
+ private ContextProviderProxy fContextProviderProxy;
/* (non-Javadoc)
* @see org.eclipse.debug.ui.AbstractDebugView#getHelpContextId()
@@ -333,6 +435,40 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
action.dispose();
}
+ public void createPartControl(final Composite parent) {
+ super.createPartControl(parent);
+
+ getSite().getSelectionProvider().addSelectionChangedListener(this);
+
+ ((IPageBookViewPage)getDefaultPage()).getSite().setSelectionProvider(getViewer());
+
+ fBreadcrumbPage = new BreadcrumbPage();
+ fBreadcrumbPage.createControl(getPageBook());
+ initPage(fBreadcrumbPage);
+
+ fContextProviderProxy = new ContextProviderProxy(
+ new IDebugContextProvider[] {fTreeViewerDebugContextProvider, fBreadcrumbPage.getContextProvider()});
+ DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()).addDebugContextProvider(fContextProviderProxy);
+
+ parent.addControlListener(new ControlListener() {
+ public void controlMoved(ControlEvent e) {
+ }
+ public void controlResized(ControlEvent e) {
+ if (parent.isDisposed()) {
+ return;
+ }
+ int breadcrumbHeight = fBreadcrumbPage.getHeight();
+ if (parent.getClientArea().height < breadcrumbHeight + 5) {
+ getPageBook().showPage(fBreadcrumbPage.getControl());
+ fContextProviderProxy.setActiveProvider(fBreadcrumbPage.getContextProvider());
+ } else {
+ showViewer();
+ fContextProviderProxy.setActiveProvider(fTreeViewerDebugContextProvider);
+ }
+ }
+ });
+ }
+
/* (non-Javadoc)
* @see org.eclipse.debug.ui.AbstractDebugView#createViewer(org.eclipse.swt.widgets.Composite)
*/
@@ -342,7 +478,7 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
SWT.MULTI | SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL,
new DebugModelPresentationContext(IDebugUIConstants.ID_DEBUG_VIEW, fPresentation));
- viewer.addSelectionChangedListener(this);
+ viewer.addSelectionChangedListener(fTreeViewerSelectionChangedListener);
viewer.getControl().addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent event) {
if (event.character == SWT.DEL && event.stateMask == 0) {
@@ -351,12 +487,11 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
}
});
viewer.addViewerUpdateListener(this);
- // add my viewer as a selection provider, so selective re-launch works
- getSite().setSelectionProvider(viewer);
+
viewer.setInput(DebugPlugin.getDefault().getLaunchManager());
//setEventHandler(new LaunchViewEventHandler(this));
- fProvider = new ContextProvider(viewer);
- DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()).addDebugContextProvider(fProvider);
+ fTreeViewerDebugContextProvider = new TreeViewerContextProvider(viewer);
+
return viewer;
}
@@ -491,12 +626,13 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
* @see org.eclipse.ui.IWorkbenchPart#dispose()
*/
public void dispose() {
- DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()).removeDebugContextProvider(fProvider);
+ getSite().getSelectionProvider().removeSelectionChangedListener(this);
+ DebugUITools.getDebugContextManager().getContextService(getSite().getWorkbenchWindow()).removeDebugContextProvider(fTreeViewerDebugContextProvider);
disposeActions();
- fProvider.dispose();
+ fTreeViewerDebugContextProvider.dispose();
Viewer viewer = getViewer();
if (viewer != null) {
- viewer.removeSelectionChangedListener(this);
+ viewer.removeSelectionChangedListener(fTreeViewerSelectionChangedListener);
((TreeModelViewer)viewer).removeViewerUpdateListener(this);
}
IWorkbenchPage page = getSite().getPage();
@@ -532,7 +668,6 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
* @see org.eclipse.jface.viewers.ISelectionChangedListener#selectionChanged(org.eclipse.jface.viewers.SelectionChangedEvent)
*/
public void selectionChanged(SelectionChangedEvent event) {
- fProvider.activate(event.getSelection());
updateObjects();
}
@@ -633,7 +768,7 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
/**
* TODO hack to get around bug 148424, remove if UI ever fixes the PropertyDialogAction to respect enablesWhen conditions
*/
- TreeSelection sel = (TreeSelection) fProvider.getActiveContext();
+ TreeSelection sel = (TreeSelection) fTreeViewerDebugContextProvider.getActiveContext();
boolean enabled = true;
if(sel != null && sel.size() > 0) {
enabled = !(sel.getFirstElement() instanceof ILaunch);
@@ -867,5 +1002,10 @@ public class LaunchView extends AbstractDebugView implements ISelectionChangedLi
}
}
-
+ /**
+ * Returns whether the breadcrumb viewer is currently visible in the view.
+ */
+ boolean isBreadcrumbVisible() {
+ return fBreadcrumbPage.getControl().isVisible();
+ }
}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java
new file mode 100644
index 000000000..739dde8d7
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewBreadcrumb.java
@@ -0,0 +1,406 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.views.launch;
+
+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.breadcrumb.AbstractBreadcrumb;
+import org.eclipse.debug.internal.ui.viewers.breadcrumb.BreadcrumbViewer;
+import org.eclipse.debug.internal.ui.viewers.breadcrumb.IBreadcrumbDropDownSite;
+import org.eclipse.debug.internal.ui.viewers.breadcrumb.TreeViewerDropDown;
+import org.eclipse.debug.internal.ui.viewers.model.ILabelUpdateListener;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.ModelDelta;
+import org.eclipse.debug.internal.ui.viewers.model.provisional.TreeModelViewer;
+import org.eclipse.debug.ui.contexts.AbstractDebugContextProvider;
+import org.eclipse.debug.ui.contexts.DebugContextEvent;
+import org.eclipse.debug.ui.contexts.IDebugContextListener;
+import org.eclipse.debug.ui.contexts.IDebugContextProvider;
+import org.eclipse.jface.action.IMenuListener;
+import org.eclipse.jface.action.IMenuManager;
+import org.eclipse.jface.action.MenuManager;
+import org.eclipse.jface.viewers.BaseLabelProvider;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ISelectionChangedListener;
+import org.eclipse.jface.viewers.ITreePathContentProvider;
+import org.eclipse.jface.viewers.ITreePathLabelProvider;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.SelectionChangedEvent;
+import org.eclipse.jface.viewers.StructuredSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.jface.viewers.TreeViewer;
+import org.eclipse.jface.viewers.Viewer;
+import org.eclipse.jface.viewers.ViewerFilter;
+import org.eclipse.jface.viewers.ViewerLabel;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.MenuDetectEvent;
+import org.eclipse.swt.events.MenuDetectListener;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
+import org.eclipse.swt.widgets.Menu;
+import org.eclipse.swt.widgets.Shell;
+import org.eclipse.ui.progress.UIJob;
+
+/**
+ * @since 3.5
+ */
+public class LaunchViewBreadcrumb extends AbstractBreadcrumb implements IDebugContextListener, ILabelUpdateListener {
+
+ private static class Input {
+ final TreePath fPath;
+
+ Input(TreePath path) {
+ fPath = path;
+ }
+ }
+
+ private static class ContentProvider implements ITreePathContentProvider {
+
+ private static final Object[] EMPTY_ELEMENTS_ARRAY = new Object[0];
+
+ public Input fInput;
+
+ public Object[] getChildren(TreePath parentPath) {
+ if (hasChildren(parentPath)) {
+ return new Object[] { fInput.fPath.getSegment(parentPath.getSegmentCount()) };
+ }
+ return EMPTY_ELEMENTS_ARRAY;
+ }
+
+ public TreePath[] getParents(Object element) {
+ // Not supported
+ return new TreePath[] { TreePath.EMPTY };
+ }
+
+ public boolean hasChildren(TreePath parentPath) {
+ if ( parentPath.getSegmentCount() == 0) {
+ return fInput != null;
+ } else if (fInput != null &&
+ fInput.fPath != null &&
+ fInput.fPath.getSegmentCount() > parentPath.getSegmentCount())
+ {
+ for (int i = 0; i < parentPath.getSegmentCount(); i++) {
+ if (i >= fInput.fPath.getSegmentCount()) {
+ return false;
+ } else {
+ Object parentElement = parentPath.getSegment(i);
+ Object contextElement = fInput.fPath.getSegment(i);
+ if (!parentElement.equals(contextElement)) {
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+
+ public Object[] getElements(Object inputElement) {
+ if (fInput != null &&
+ fInput.fPath != null)
+ {
+ return getChildren(TreePath.EMPTY);
+ } else {
+ return new Object[] { fgEmptyDebugContextElement };
+ }
+ }
+
+ public void dispose() {
+ fInput = null;
+ }
+
+ public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
+ if (newInput instanceof Input) {
+ fInput = ((Input)newInput);
+ } else {
+ fInput = null;
+ }
+ }
+ }
+
+ private class LabelProvider extends BaseLabelProvider implements ITreePathLabelProvider {
+ public void updateLabel(ViewerLabel label, TreePath elementPath) {
+ if (fgEmptyDebugContextElement.equals(elementPath.getLastSegment())) {
+ label.setText(LaunchViewMessages.Breadcrumb_NoActiveContext);
+ label.setImage(null);
+ } else {
+ ViewerLabel treeViewerLabel = fTreeViewer.getElementLabel(elementPath, null);
+ if (treeViewerLabel == null) {
+ label.setText(LaunchViewMessages.Breadcrumb_NoActiveContext);
+ label.setImage(null);
+ } else {
+ label.setText(treeViewerLabel.getText());
+ label.setTooltipText(treeViewerLabel.getText());
+ label.setImage(treeViewerLabel.getImage());
+ label.setFont(treeViewerLabel.getFont());
+ label.setForeground(treeViewerLabel.getForeground());
+ label.setBackground(treeViewerLabel.getBackground());
+
+ }
+ }
+ }
+ }
+
+ private final LaunchView fView;
+ private final TreeModelViewer fTreeViewer;
+ private final IDebugContextProvider fTreeViewerContextProvider;
+ private Input fBreadcrumbInput;
+ static final private Object fgEmptyDebugContextElement = new Object();
+ private BreadcrumbViewer fViewer;
+ private boolean fRefreshBreadcrumb = false;
+
+ private class BreadcrumbContextProvider extends AbstractDebugContextProvider implements IDebugContextListener, ISelectionChangedListener {
+
+ private ISelection fBreadcrumbSelection = null;
+
+ BreadcrumbContextProvider() {
+ super(fView);
+ fViewer.addSelectionChangedListener(this);
+ fBreadcrumbSelection = fViewer.getSelection();
+ fTreeViewerContextProvider.addDebugContextListener(this);
+ }
+
+ public ISelection getActiveContext() {
+ if (fBreadcrumbSelection != null && !fBreadcrumbSelection.isEmpty()) {
+ return fBreadcrumbSelection;
+ } else {
+ return fTreeViewerContextProvider.getActiveContext();
+ }
+ }
+
+ void dispose() {
+ fViewer.removeSelectionChangedListener(this);
+ fTreeViewerContextProvider.removeDebugContextListener(this);
+ }
+
+ public void debugContextChanged(DebugContextEvent event) {
+ fire(new DebugContextEvent(this, getActiveContext(), event.getFlags()));
+ }
+
+ public void selectionChanged(SelectionChangedEvent event) {
+ ISelection oldContext = getActiveContext();
+ fBreadcrumbSelection = event.getSelection();
+ if (!getActiveContext().equals(oldContext)) {
+ fire(new DebugContextEvent(this, getActiveContext(), DebugContextEvent.ACTIVATED));
+ }
+ }
+ }
+
+ private BreadcrumbContextProvider fBreadcrumbContextProvider;
+
+ public LaunchViewBreadcrumb(LaunchView view, TreeModelViewer treeViewer, IDebugContextProvider contextProvider) {
+ fView = view;
+ fTreeViewer = treeViewer;
+ fTreeViewer.addLabelUpdateListener(this);
+ fTreeViewerContextProvider = contextProvider;
+ fBreadcrumbInput = new Input( getPathForSelection(fTreeViewerContextProvider.getActiveContext()) );
+ fTreeViewerContextProvider.addDebugContextListener(this);
+ }
+
+ protected void activateBreadcrumb() {
+ }
+
+ protected void deactivateBreadcrumb() {
+ if (fViewer.isDropDownOpen()) {
+ Shell shell = fViewer.getDropDownShell();
+ if (shell != null) {
+ shell.close();
+ }
+ }
+ }
+
+ protected BreadcrumbViewer createViewer(Composite parent) {
+ fViewer = new BreadcrumbViewer(parent, SWT.NONE) {
+ protected Control createDropDown(Composite dropDownParent, IBreadcrumbDropDownSite site, TreePath path) {
+ return createDropDownControl(dropDownParent, site, path);
+ }
+ };
+
+ // Force the layout of the breadcrumb viewer so that we may calcualte
+ // its proper size.
+ parent.pack(true);
+
+ fViewer.setContentProvider(new ContentProvider());
+ fViewer.setLabelProvider(new LabelProvider());
+
+ createMenuManager();
+
+ fViewer.setInput(getCurrentInput());
+
+ fBreadcrumbContextProvider = new BreadcrumbContextProvider();
+
+ return fViewer;
+ }
+
+ protected void createMenuManager() {
+ MenuManager menuMgr = new MenuManager("#PopUp"); //$NON-NLS-1$
+ menuMgr.setRemoveAllWhenShown(true);
+ menuMgr.addMenuListener(new IMenuListener() {
+ public void menuAboutToShow(IMenuManager mgr) {
+ fView.fillContextMenu(mgr);
+ }
+ });
+ final Menu menu= menuMgr.createContextMenu(fViewer.getControl());
+
+ // register the context menu such that other plug-ins may contribute to it
+ if (fView.getSite() != null) {
+ fView.getSite().registerContextMenu(menuMgr, fViewer);
+ }
+ fView.addContextMenuManager(menuMgr);
+
+ fViewer.addMenuDetectListener(new MenuDetectListener() {
+ public void menuDetected(MenuDetectEvent event) {
+ menu.setLocation(event.x + 10, event.y + 10);
+ menu.setVisible(true);
+ while (!menu.isDisposed() && menu.isVisible()) {
+ if (!menu.getDisplay().readAndDispatch())
+ menu.getDisplay().sleep();
+ }
+ }
+ });
+ }
+
+ protected Object getCurrentInput() {
+ return fBreadcrumbInput;
+ }
+
+ protected boolean open(ISelection selection) {
+ // Let the drop-down control implementation itself handle activating a new context.
+ return false;
+ }
+
+ public void dispose() {
+ fBreadcrumbContextProvider = null;
+ fTreeViewerContextProvider.removeDebugContextListener(this);
+ fTreeViewer.removeLabelUpdateListener(this);
+ fBreadcrumbContextProvider.dispose();
+ fViewer = null;
+ super.dispose();
+ }
+
+ public void debugContextChanged(DebugContextEvent event) {
+ fBreadcrumbInput = new Input(getPathForSelection(event.getContext()));
+ setInput(getCurrentInput());
+ refresh();
+ }
+
+ public void labelUpdateStarted(ILabelUpdate update) {
+ }
+
+ public void labelUpdateComplete(ILabelUpdate update) {
+ if (fBreadcrumbInput != null && fBreadcrumbInput.fPath != null) {
+ if (fBreadcrumbInput.fPath.startsWith(update.getElementPath(), null)) {
+ synchronized (this) {
+ fRefreshBreadcrumb = true;
+ }
+ }
+ }
+ }
+
+ public void labelUpdatesBegin() {
+ }
+
+ public void labelUpdatesComplete() {
+ boolean refresh = false;
+ synchronized(this) {
+ refresh = fRefreshBreadcrumb;
+ fRefreshBreadcrumb = false;
+ }
+ if (refresh) {
+ new UIJob(fViewer.getControl().getDisplay(), "refresh breadcrumb") { //$NON-NLS-1$
+ { setSystem(true); }
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ refresh();
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+ }
+
+ IDebugContextProvider getContextProvider() {
+ return fBreadcrumbContextProvider;
+ }
+
+ int getHeight() {
+ return fViewer.getControl().getSize().y;
+ }
+
+ private TreePath getPathForSelection(ISelection selection) {
+ if (selection instanceof ITreeSelection && !selection.isEmpty()) {
+ return ((ITreeSelection)selection).getPaths()[0];
+ }
+ return null;
+ }
+
+ public Control createDropDownControl(Composite parent, final IBreadcrumbDropDownSite site, TreePath paramPath) {
+
+ TreeViewerDropDown dropDownTreeViewer = new TreeViewerDropDown() {
+
+ TreeModelViewer fDropDownViewer;
+
+ protected TreeViewer createTreeViewer(Composite composite, int style, TreePath path) {
+ fDropDownViewer = new TreeModelViewer(
+ composite, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL | SWT.VIRTUAL | SWT.POP_UP,
+ fTreeViewer.getPresentationContext());
+
+ Object launchViewInput = fTreeViewer.getInput();
+ fDropDownViewer.setInput(launchViewInput);
+
+ ViewerFilter[] filters = fTreeViewer.getFilters();
+ fDropDownViewer.setFilters(filters);
+
+ ModelDelta delta = new ModelDelta(launchViewInput, IModelDelta.NO_CHANGE);
+ fTreeViewer.saveElementState(TreePath.EMPTY, delta);
+ fDropDownViewer.updateViewer(delta);
+
+ if (path.getSegmentCount() != 0) {
+ fDropDownViewer.setSelection(new TreeSelection(path), true, true);
+ }
+
+ fDropDownViewer.addLabelUpdateListener(new ILabelUpdateListener() {
+ public void labelUpdateComplete(ILabelUpdate update) {}
+ public void labelUpdatesBegin() {}
+ public void labelUpdateStarted(ILabelUpdate update) {}
+ public void labelUpdatesComplete() {
+ new UIJob(fViewer.getControl().getDisplay(), "resize breadcrub dropdown") { //$NON-NLS-1$
+ { setSystem(true); }
+ public IStatus runInUIThread(IProgressMonitor monitor) {
+ site.updateSize();
+ return Status.OK_STATUS;
+ }
+ }.schedule();
+ }
+ });
+
+ return fDropDownViewer;
+ }
+
+ protected void openElement(ISelection selection) {
+ if (selection != null && (selection instanceof ITreeSelection) && !selection.isEmpty()) {
+ ModelDelta delta = new ModelDelta(fDropDownViewer.getInput(), IModelDelta.NO_CHANGE);
+ fDropDownViewer.saveElementState(TreePath.EMPTY, delta);
+ fTreeViewer.updateViewer(delta);
+ fTreeViewer.setSelection(selection, true, true);
+ fViewer.setSelection(StructuredSelection.EMPTY);
+ site.close();
+ }
+
+ super.openElement(selection);
+ }
+ };
+
+
+ return dropDownTreeViewer.createDropDown(parent, site, paramPath);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java
new file mode 100644
index 000000000..424c88b06
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewCopyToClipboardActionDelegate.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (c) 2009 Wind River Systems 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:
+ * Wind River Systems - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.views.launch;
+
+import org.eclipse.debug.internal.ui.viewers.model.InternalTreeModelViewer;
+import org.eclipse.debug.internal.ui.viewers.model.VirtualCopyToClipboardActionDelegate;
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.ITreeSelection;
+import org.eclipse.jface.viewers.TreePath;
+import org.eclipse.jface.viewers.TreeSelection;
+import org.eclipse.swt.widgets.Tree;
+import org.eclipse.swt.widgets.TreeItem;
+import org.eclipse.swt.widgets.Widget;
+
+/**
+ * @since 3.5
+ */
+public class LaunchViewCopyToClipboardActionDelegate extends VirtualCopyToClipboardActionDelegate {
+
+ protected TreeItem[] getPrunedSelection() {
+ LaunchView view = (LaunchView)getView();
+ if (view.isBreadcrumbVisible()) {
+ ISelection selection = getSelection();
+ if (selection instanceof ITreeSelection && getViewer() instanceof InternalTreeModelViewer) {
+ if (selection.isEmpty()) {
+ selection = new TreeSelection(TreePath.EMPTY);
+ }
+ return getSelectedItems((InternalTreeModelViewer)getViewer(), ((ITreeSelection)selection).getPaths()[0]);
+ }
+ return new TreeItem[0];
+ } else {
+ return super.getPrunedSelection();
+ }
+ }
+
+ private TreeItem[] getSelectedItems(InternalTreeModelViewer viewer, TreePath path) {
+ Widget item = viewer.findItem(path);
+ if (item instanceof TreeItem) {
+ return new TreeItem[] { (TreeItem)item };
+ } else if (item instanceof Tree) {
+ return ((Tree)item).getItems();
+ }
+ return new TreeItem[0];
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.java b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.java
new file mode 100755
index 000000000..e49c5d32e
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.java
@@ -0,0 +1,25 @@
+/*******************************************************************************
+ * Copyright (c) 2000, 2007 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 - Initial API and implementation
+ *******************************************************************************/
+package org.eclipse.debug.internal.ui.views.launch;
+
+import org.eclipse.osgi.util.NLS;
+
+public class LaunchViewMessages extends NLS {
+ private static final String BUNDLE_NAME = "org.eclipse.debug.internal.ui.views.launch.LaunchViewMessages";//$NON-NLS-1$
+
+ public static String Breadcrumb_NoActiveContext;
+ public static String breadcrumb_LabelPending;
+
+ static {
+ // load message values from bundle file
+ NLS.initializeMessages(BUNDLE_NAME, LaunchViewMessages.class);
+ }
+}
diff --git a/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.properties b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.properties
new file mode 100755
index 000000000..28898a431
--- /dev/null
+++ b/org.eclipse.debug.ui/ui/org/eclipse/debug/internal/ui/views/launch/LaunchViewMessages.properties
@@ -0,0 +1,13 @@
+###############################################################################
+# Copyright (c) 2000, 2007 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
+###############################################################################
+
+Breadcrumb_NoActiveContext=No Active Context
+breadcrumb_LabelPending=... \ No newline at end of file

Back to the top