### Eclipse Workspace Patch 1.0 #P org.eclipse.pde.ui Index: src/org/eclipse/pde/internal/ui/launcher/OSGiFrameworkBlock.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/OSGiFrameworkBlock.java,v retrieving revision 1.7 diff -u -r1.7 OSGiFrameworkBlock.java --- src/org/eclipse/pde/internal/ui/launcher/OSGiFrameworkBlock.java 16 Jan 2008 17:08:30 -0000 1.7 +++ src/org/eclipse/pde/internal/ui/launcher/OSGiFrameworkBlock.java 22 Feb 2008 15:26:13 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 IBM Corporation and others. + * Copyright (c) 2005, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -90,6 +90,11 @@ fDefaultAutoStart.setItems(new String[] {Boolean.toString(true), Boolean.toString(false)}); fDefaultAutoStart.select(0); fDefaultAutoStart.addSelectionListener(fListener); + + label = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 6; + label.setLayoutData(gd); } public void initializeFrom(ILaunchConfiguration config) throws CoreException { Index: src/org/eclipse/pde/internal/ui/launcher/OSGiBundleBlock.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/OSGiBundleBlock.java,v retrieving revision 1.14 diff -u -r1.14 OSGiBundleBlock.java --- src/org/eclipse/pde/internal/ui/launcher/OSGiBundleBlock.java 12 Feb 2008 15:48:21 -0000 1.14 +++ src/org/eclipse/pde/internal/ui/launcher/OSGiBundleBlock.java 22 Feb 2008 15:26:13 -0000 @@ -7,7 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation - * Ian Bull - bug 204404 + * Ian Bull - bug 204404 and bug 207064 *******************************************************************************/ package org.eclipse.pde.internal.ui.launcher; @@ -32,8 +32,8 @@ public class OSGiBundleBlock extends AbstractPluginBlock { - private HashMap levelColumnCache = null; - private HashMap autoColumnCache = null; + private HashMap levelColumnCache = new HashMap(); + private HashMap autoColumnCache = new HashMap(); private TreeEditor levelColumnEditor = null; private TreeEditor autoColumnEditor = null; @@ -126,6 +126,7 @@ int selection = spinner.getSelection(); item.setText(1, selection == 0 ? "default" //$NON-NLS-1$ : Integer.toString(selection)); + levelColumnCache.put(item.getData(), item.getText(1)); fTab.updateLaunchConfigurationDialog(); } } @@ -141,12 +142,12 @@ if (item.getChecked()) { item.setText(2, combo.getText()); fTab.updateLaunchConfigurationDialog(); + autoColumnCache.put(item.getData(), item.getText(2)); } } }); autoColumnEditor.setEditor(combo, item, 2); } - } }); } @@ -181,11 +182,10 @@ for (int i = 0; i < selected.length; i++) { if (selected[i] instanceof IPluginModelBase) { IPluginModelBase model = (IPluginModelBase) selected[i]; - TreeItem item = (TreeItem) fPluginTreeViewer.testFindItem(model); if (model.getUnderlyingResource() == null) { - appendToBuffer(tBuffer, model, item); + appendToBuffer(tBuffer, model); } else { - appendToBuffer(wBuffer, model, item); + appendToBuffer(wBuffer, model); } } } @@ -196,18 +196,19 @@ if (fAddWorkspaceButton.getSelection()) { for (int i = 0; i < fWorkspaceModels.length; i++) { if (!fPluginTreeViewer.getChecked(fWorkspaceModels[i])) { - appendToBuffer(buffer, fWorkspaceModels[i], null); + appendToBuffer(buffer, fWorkspaceModels[i]); } } } config.setAttribute(IPDELauncherConstants.DESELECTED_WORKSPACE_PLUGINS, buffer.length() > 0 ? buffer.toString() : (String) null); } - private void appendToBuffer(StringBuffer buffer, IPluginModelBase model, TreeItem item) { + private void appendToBuffer(StringBuffer buffer, IPluginModelBase model) { if (buffer.length() > 0) - buffer.append(","); //$NON-NLS-1$ - String startLevel = item != null ? item.getText(1) : null; - String autoStart = item != null ? item.getText(2) : null; + buffer.append(","); //$NON-NLS-1$ + + String startLevel = levelColumnCache.get(model) != null ? levelColumnCache.get(model).toString() : null; + String autoStart = autoColumnCache.get(model) != null ? autoColumnCache.get(model).toString() : null; String value = BundleLauncherHelper.writeBundles(model, startLevel, autoStart); buffer.append(value); } @@ -224,6 +225,7 @@ private void initExternalPluginsState(ILaunchConfiguration configuration) throws CoreException { Map map = BundleLauncherHelper.getTargetBundleMap(configuration); Iterator iter = map.keySet().iterator(); + fPluginTreeViewer.setSubtreeChecked(fExternalPlugins, false); while (iter.hasNext()) { IPluginModelBase model = (IPluginModelBase) iter.next(); if (fPluginTreeViewer.setChecked(model, true)) { @@ -237,13 +239,13 @@ } private void resetGroup(NamedElement group) { - Widget widget = fPluginTreeViewer.testFindItem(group); - if (widget instanceof TreeItem) { - TreeItem[] items = ((TreeItem) widget).getItems(); - for (int i = 0; i < items.length; i++) { - if (!items[i].getChecked()) { - resetText(items[i]); - } + Object[] children = group.getChildren(); + if (children == null) + return; + for (int i = 0; i < children.length; i++) { + Object child = children[i]; + if (child instanceof IPluginModelBase) { + resetText((IPluginModelBase) child); } } } @@ -251,6 +253,7 @@ private void initWorkspacePluginsState(ILaunchConfiguration configuration) throws CoreException { Map map = BundleLauncherHelper.getWorkspaceBundleMap(configuration); Iterator iter = map.keySet().iterator(); + fPluginTreeViewer.setSubtreeChecked(fWorkspacePlugins, false); while (iter.hasNext()) { IPluginModelBase model = (IPluginModelBase) iter.next(); if (fPluginTreeViewer.setChecked(model, true)) { @@ -266,13 +269,17 @@ protected void handleGroupStateChanged(Object group, boolean checked) { super.handleGroupStateChanged(group, checked); - Widget item = fPluginTreeViewer.testFindItem(group); - if (item instanceof TreeItem) { - TreeItem[] items = ((TreeItem) item).getItems(); - for (int i = 0; i < items.length; i++) { - TreeItem child = items[i]; - if (child.getChecked() == (child.getText(1).length() == 0)) - resetText(items[i]); + + if (group instanceof NamedElement) { + NamedElement namedElement = (NamedElement) group; + Object[] children = namedElement.getChildren(); + if (children == null) + return; + for (int i = 0; i < children.length; i++) { + Object child = children[i]; + if (child instanceof IPluginModelBase) { + resetText((IPluginModelBase) child); + } } } } @@ -294,13 +301,17 @@ } private void updateGroup(Object group) { - Widget item = fPluginTreeViewer.testFindItem(group); - if (item instanceof TreeItem) { - TreeItem[] items = ((TreeItem) item).getItems(); - for (int i = 0; i < items.length; i++) { - TreeItem child = items[i]; - if (child.getChecked() == (child.getText(1).length() == 0)) - resetText(items[i]); + if (group instanceof NamedElement) { + NamedElement namedElement = (NamedElement) group; + Object[] children = namedElement.getChildren(); + if (children == null) { + return; + } + for (int i = 0; i < children.length; i++) { + Object child = children[i]; + if (child instanceof IPluginModelBase) { + resetText((IPluginModelBase) child); + } } } } @@ -310,38 +321,50 @@ if (widget instanceof TreeItem) { TreeItem item = (TreeItem) widget; int index = value == null ? -1 : value.indexOf(':'); - item.setText(1, index == -1 ? "" : value.substring(0, index)); //$NON-NLS-1$ + String levelValue = index == -1 ? "" : value.substring(0, index); //$NON-NLS-1$ + String autoValue = null; + item.setText(1, levelValue); if (model.isFragmentModel()) { - item.setText(2, "false"); //$NON-NLS-1$ + autoValue = "false"; //$NON-NLS-1$ + item.setText(2, autoValue); } else { - item.setText(2, index == -1 ? "" : value.substring(index + 1)); //$NON-NLS-1$ + autoValue = index == -1 ? "" : value.substring(index + 1); //$NON-NLS-1$ + item.setText(2, autoValue); } + levelColumnCache.put(model, levelValue); + autoColumnCache.put(model, autoValue); } } private void resetText(IPluginModelBase model) { + String levelText = null; + String autoText = null; Widget widget = fPluginTreeViewer.testFindItem(model); - if (widget instanceof TreeItem) { - resetText((TreeItem) widget); - } - } + if (fPluginTreeViewer.getChecked(model)) { + boolean isSystemBundle = "org.eclipse.osgi".equals(model.getPluginBase().getId()); //$NON-NLS-1$ + if (!"default".equals(levelColumnCache.get(model))) //$NON-NLS-1$ + levelText = isSystemBundle ? "" : "default"; //$NON-NLS-1$ //$NON-NLS-2$ + if (!"default".equals(autoColumnCache.get(model))) //$NON-NLS-1$ + autoText = isSystemBundle ? "" : "default"; //$NON-NLS-1$ //$NON-NLS-2$ - private void resetText(TreeItem item) { - if (item.getChecked()) { - IPluginModelBase model = (IPluginModelBase) item.getData(); - String systemBundleId = PDECore.getDefault().getModelManager().getSystemBundleId(); - boolean isSystemBundle = systemBundleId.equals(model.getPluginBase().getId()); - if (!"default".equals(item.getText(1))) //$NON-NLS-1$ - item.setText(1, isSystemBundle ? "" : "default"); //$NON-NLS-1$ //$NON-NLS-2$ - if (model.isFragmentModel()) - item.setText(2, "false"); //$NON-NLS-1$ - else if (!"default".equals(item.getText(2))) //$NON-NLS-1$ - item.setText(2, isSystemBundle ? "" : "default"); //$NON-NLS-1$ //$NON-NLS-2$ + if (model.isFragmentModel()) { + autoText = "false"; //$NON-NLS-1$ + } } else { - if (item.getText(1).length() > 0) - item.setText(1, ""); //$NON-NLS-1$ - if (item.getText(2).length() > 0) - item.setText(2, ""); //$NON-NLS-1$ + levelText = ""; //$NON-NLS-1$ + autoText = ""; //$NON-NLS-1$ + } + if (levelText != null) { + levelColumnCache.put(model, levelText); + if (widget instanceof TreeItem) { + ((TreeItem) widget).setText(1, levelText); + } + } + if (autoText != null) { + autoColumnCache.put(model, autoText); + if (widget instanceof TreeItem) { + ((TreeItem) widget).setText(2, autoText); + } } } @@ -363,9 +386,7 @@ } /** - * Refreshes the tree viewer. This caches the values of the - * level and auto column, and it clears any editors on the view. - * Finally, it sets the selection to the root node. + * Refreshes the tree viewer. It clears any editors on the view. */ protected void refreshTreeView(CheckboxTreeViewer treeView) { // Remove any selection @@ -383,34 +404,5 @@ if (autoColumnEditor != null && autoColumnEditor.getEditor() != null && !autoColumnEditor.getEditor().isDisposed()) { autoColumnEditor.getEditor().dispose(); } - - // Cache the current text - levelColumnCache = new HashMap(); - autoColumnCache = new HashMap(); - ArrayList allTreeItems = getAllTreeItems(treeView.getTree().getItems()); - for (Iterator iterator = allTreeItems.iterator(); iterator.hasNext();) { - TreeItem item = (TreeItem) iterator.next(); - levelColumnCache.put(item.getData(), item.getText(1)); - autoColumnCache.put(item.getData(), item.getText(2)); - } } - - /** - * This gets all the tree items from a tree. - * - * This method must exist in some SWT util library, so it can probably be - * removed when I find it. - * - * @param roots - */ - private ArrayList getAllTreeItems(TreeItem[] roots) { - ArrayList list = new ArrayList(); - for (int i = 0; i < roots.length; i++) { - TreeItem item = roots[i]; - list.add(item); - list.addAll(getAllTreeItems(item.getItems())); - } - return list; - } - } Index: src/org/eclipse/pde/internal/ui/launcher/AbstractPluginBlock.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/launcher/AbstractPluginBlock.java,v retrieving revision 1.28 diff -u -r1.28 AbstractPluginBlock.java --- src/org/eclipse/pde/internal/ui/launcher/AbstractPluginBlock.java 16 Jan 2008 17:08:31 -0000 1.28 +++ src/org/eclipse/pde/internal/ui/launcher/AbstractPluginBlock.java 22 Feb 2008 15:26:13 -0000 @@ -7,7 +7,7 @@ * * Contributors: * IBM Corporation - initial API and implementation - * Ian Bull - bug 204404 + * Ian Bull - bug 204404 and bug 207064 *******************************************************************************/ package org.eclipse.pde.internal.ui.launcher; @@ -27,6 +27,8 @@ import org.eclipse.pde.internal.ui.*; import org.eclipse.pde.internal.ui.elements.DefaultContentProvider; import org.eclipse.pde.internal.ui.elements.NamedElement; +import org.eclipse.pde.internal.ui.launcher.FilteredCheckboxTree.FilterableCheckboxTreeViewer; +import org.eclipse.pde.internal.ui.launcher.FilteredCheckboxTree.PreRefreshNotifier; import org.eclipse.pde.internal.ui.util.PersistablePluginObject; import org.eclipse.pde.internal.ui.util.SWTUtil; import org.eclipse.pde.internal.ui.wizards.ListUtil; @@ -42,11 +44,13 @@ import org.eclipse.swt.widgets.*; import org.eclipse.ui.*; import org.eclipse.ui.dialogs.IWorkingSetSelectionDialog; +import org.eclipse.ui.dialogs.PatternFilter; public abstract class AbstractPluginBlock { protected AbstractLauncherTab fTab; + private FilteredCheckboxTree fPluginFilteredTree; protected CheckboxTreeViewer fPluginTreeViewer; protected NamedElement fWorkspacePlugins; protected NamedElement fExternalPlugins; @@ -75,6 +79,8 @@ private Button fValidateButton; + private boolean viewerEnabled = true; + class Listener extends SelectionAdapter { private void filterAffectingControl(SelectionEvent e) { @@ -213,7 +219,7 @@ public void createControl(Composite parent, int span, int indent) { createPluginViewer(parent, span - 1, indent); - createButtonContainer(parent); + createButtonContainer(parent, fPluginFilteredTree.getTreeLocationOffset()); fIncludeOptionalButton = createButton(parent, span, indent, NLS.bind(PDEUIMessages.AdvancedLauncherTab_includeOptional, fTab.getName().toLowerCase(Locale.ENGLISH))); fAddWorkspaceButton = createButton(parent, span, indent, NLS.bind(PDEUIMessages.AdvancedLauncherTab_addNew, fTab.getName().toLowerCase(Locale.ENGLISH))); @@ -249,7 +255,32 @@ } protected void createPluginViewer(Composite composite, int span, int indent) { - fPluginTreeViewer = new CheckboxTreeViewer(composite, getTreeViewerStyle()); + + fPluginFilteredTree = new FilteredCheckboxTree(composite, getTreeViewerStyle(), new PatternFilter()); + fPluginTreeViewer = (CheckboxTreeViewer) fPluginFilteredTree.getViewer(); + ((FilterableCheckboxTreeViewer) fPluginTreeViewer).addPreRefreshNotifier(new PreRefreshNotifier() { + + boolean previousFilter = false; + + public void preRefresh(FilterableCheckboxTreeViewer viewer, boolean filtered) { + refreshTreeView(fPluginTreeViewer); + if (previousFilter != filtered) { + if (viewerEnabled) { + // Only update these buttons if the viewer is actually enabled + fWorkingSetButton.setEnabled(!filtered); + fAddRequiredButton.setEnabled(!filtered); + fDefaultsButton.setEnabled(!filtered); + } + + fPluginTreeViewer.setChecked(fWorkspacePlugins, fNumWorkspaceChecked > 0); + fPluginTreeViewer.setGrayed(fWorkspacePlugins, fNumWorkspaceChecked > 0 && fNumWorkspaceChecked < fWorkspaceModels.length); + fPluginTreeViewer.setChecked(fExternalPlugins, fNumExternalChecked > 0); + fPluginTreeViewer.setGrayed(fExternalPlugins, fNumExternalChecked > 0 && fNumExternalChecked < fExternalModels.length); + } + previousFilter = filtered; + } + }); + fPluginTreeViewer.addCheckStateListener(new ICheckStateListener() { public void checkStateChanged(CheckStateChangedEvent event) { @@ -299,13 +330,16 @@ GridData gd = new GridData(GridData.FILL_BOTH); gd.horizontalSpan = span; gd.horizontalIndent = indent; - fPluginTreeViewer.getTree().setLayoutData(gd); + fPluginFilteredTree.setLayoutData(gd); Image siteImage = PDEPlugin.getDefault().getLabelProvider().get(PDEPluginImages.DESC_SITE_OBJ); fWorkspacePlugins = new NamedElement(PDEUIMessages.AdvancedLauncherTab_workspacePlugins, siteImage); fExternalPlugins = new NamedElement(PDEUIMessages.PluginsTab_target, siteImage); + fWorkspacePlugins.setChildren(fWorkspaceModels); + fExternalPlugins.setChildren(fExternalModels); + fPluginTreeViewer.addFilter(new Filter()); } @@ -325,11 +359,11 @@ } } - private void createButtonContainer(Composite parent) { + private void createButtonContainer(Composite parent, int vOffset) { Composite composite = new Composite(parent, SWT.NONE); GridLayout layout = new GridLayout(); layout.marginHeight = layout.marginWidth = 0; - layout.marginTop = 6; + layout.marginTop = vOffset; composite.setLayout(layout); composite.setLayoutData(new GridData(GridData.FILL_VERTICAL)); @@ -382,10 +416,15 @@ fPluginTreeViewer.setSubtreeChecked(group, checked); fPluginTreeViewer.setGrayed(group, false); + Object[] checkedChildren = ((FilteredCheckboxTree.FilterableCheckboxTreeViewer) fPluginTreeViewer).getCheckedChildren(group); + int numberOfChildren = 0; + if (checkedChildren != null) + numberOfChildren = checkedChildren.length; + if (group == fWorkspacePlugins) - fNumWorkspaceChecked = checked ? fWorkspaceModels.length : 0; + fNumWorkspaceChecked = numberOfChildren; else if (group == fExternalPlugins) - fNumExternalChecked = checked ? fExternalModels.length : 0; + fNumExternalChecked = numberOfChildren; } @@ -395,7 +434,6 @@ } protected void handleFilterButton() { - refreshTreeView(fPluginTreeViewer); fPluginTreeViewer.refresh(); fPluginTreeViewer.expandAll(); } @@ -547,7 +585,10 @@ } public void enableViewer(boolean enable) { + viewerEnabled = enable; fPluginTreeViewer.getTree().setEnabled(enable); + fPluginFilteredTree.resetFilter(); + fPluginFilteredTree.getFilterControl().setEnabled(enable); fAddRequiredButton.setEnabled(enable); fDefaultsButton.setEnabled(enable); fWorkingSetButton.setEnabled(enable); Index: src/org/eclipse/pde/ui/launcher/PluginsTab.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/ui/launcher/PluginsTab.java,v retrieving revision 1.13 diff -u -r1.13 PluginsTab.java --- src/org/eclipse/pde/ui/launcher/PluginsTab.java 16 Jan 2008 17:08:34 -0000 1.13 +++ src/org/eclipse/pde/ui/launcher/PluginsTab.java 22 Feb 2008 15:26:13 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2005, 2007 IBM Corporation and others. + * Copyright (c) 2005, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -100,9 +100,16 @@ fSelectionCombo = new Combo(composite, SWT.READ_ONLY | SWT.BORDER); fSelectionCombo.setItems(new String[] {PDEUIMessages.PluginsTab_allPlugins, PDEUIMessages.PluginsTab_selectedPlugins, PDEUIMessages.PluginsTab_featureMode}); fSelectionCombo.setText(fSelectionCombo.getItem(0)); - fSelectionCombo.setLayoutData(new GridData(GridData.FILL_HORIZONTAL)); + GridData gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 2; + fSelectionCombo.setLayoutData(gd); fSelectionCombo.addSelectionListener(new Listener()); + label = new Label(composite, SWT.SEPARATOR | SWT.HORIZONTAL); + gd = new GridData(GridData.FILL_HORIZONTAL); + gd.horizontalSpan = 3; + label.setLayoutData(gd); + fPluginBlock.createControl(composite, 3, 10); setControl(composite); Index: src/org/eclipse/pde/internal/ui/elements/NamedElement.java =================================================================== RCS file: /cvsroot/eclipse/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/elements/NamedElement.java,v retrieving revision 1.7 diff -u -r1.7 NamedElement.java --- src/org/eclipse/pde/internal/ui/elements/NamedElement.java 3 May 2006 13:31:06 -0000 1.7 +++ src/org/eclipse/pde/internal/ui/elements/NamedElement.java 22 Feb 2008 15:26:13 -0000 @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2006 IBM Corporation and others. + * Copyright (c) 2000, 2008 IBM Corporation and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at @@ -19,6 +19,8 @@ private IPDEElement parent; + private Object[] children; + public NamedElement(String name) { this(name, null, null); } @@ -33,6 +35,21 @@ this.parent = parent; } + /** + * Sets the children of this element. The children are not copied + * @param children + */ + public void setChildren(Object[] children) { + this.children = children; + } + + /** + * Gets the children of this element. The children are not copied + */ + public Object[] getChildren() { + return children; + } + public Image getImage() { return image; } Index: src/org/eclipse/pde/internal/ui/launcher/FilteredCheckboxTree.java =================================================================== RCS file: src/org/eclipse/pde/internal/ui/launcher/FilteredCheckboxTree.java diff -N src/org/eclipse/pde/internal/ui/launcher/FilteredCheckboxTree.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/internal/ui/launcher/FilteredCheckboxTree.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,403 @@ +/******************************************************************************* + * Copyright (c) 2008 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * Ian Bull - bug 207064 + *******************************************************************************/ +package org.eclipse.pde.internal.ui.launcher; + +import java.util.*; +import java.util.List; +import org.eclipse.jface.viewers.*; +import org.eclipse.swt.layout.GridData; +import org.eclipse.swt.layout.GridLayout; +import org.eclipse.swt.widgets.*; +import org.eclipse.ui.dialogs.FilteredTree; +import org.eclipse.ui.dialogs.PatternFilter; + +/** + * A FilteredChecboxTree. This tree stores all the tree elements internally, and keeps the + * check state in sync. This way, even if an element is filtered, the caller can get and set the + * checked state. + * + * The internal representation is additive. That is, elements are never removed from the internal + * representation. This is OK since the PDE launch Dialog never changes the elements once + * the view is opened. If any other tree is based on this code, they may want to address this issue. + * + * This is not public because it was customized for the Launch Dialog. + * + */ +class FilteredCheckboxTree extends FilteredTree { + + /** + * The FilteredCheckboxTree Constructor. + * @param parent The parent composite where this Tree will be placed. + * @param treeStyle Tree styles + * @param filter The pattern filter that will be used to filter elements + */ + public FilteredCheckboxTree(Composite parent, int treeStyle, PatternFilter filter) { + super(parent, treeStyle, filter); + } + + /* + * (non-Javadoc) + * @see org.eclipse.ui.dialogs.FilteredTree#doCreateTreeViewer(org.eclipse.swt.widgets.Composite, int) + */ + protected TreeViewer doCreateTreeViewer(Composite parent, int style) { + return new FilterableCheckboxTreeViewer(parent, style); + } + + public void resetFilter() { + getFilterControl().setText(this.initialText); + } + + /** + * Get the number of pixels the tree viewer is from the top of the filtered + * checkbox tree viewer. This is useful if you wish to align buttons with the + * tree. + * @return the offset of the Tree from the top of the container + */ + int getTreeLocationOffset() { + GridLayout layout = (GridLayout) getLayout(); + return layout.horizontalSpacing + layout.marginTop + ((GridData) getLayoutData()).verticalIndent + getFilterControl().getSize().y + 1; + } + + /** + * Classes which implement this interface deal with notifications from the + * filtered checkbox tree viewer. The notifications are fired before a refresh + * happens. + */ + interface PreRefreshNotifier { + public void preRefresh(FilterableCheckboxTreeViewer viewer, boolean filtered); + } + + /** + * A CheckboxTreeViewer that maintains an internal representation of all the nodes. + * + */ + public class FilterableCheckboxTreeViewer extends CheckboxTreeViewer { + static final String NONE = "none"; //$NON-NLS-1$ + static final String CHECKED = "checked"; //$NON-NLS-1$ + static final String GREYED = "greyed"; //$NON-NLS-1$ + static final String CHECKED_GREYED = "checked_greyed"; //$NON-NLS-1$ + + /** + * The internal node for the FilterableCheckboxTreeViewer + */ + class FilteredCheckboxTreeItem { + Object data; // Data element + String state; // Checked State + List children = new ArrayList(); + + public FilteredCheckboxTreeItem(Object data, String state, Map itemCache, FilteredCheckboxTreeItem parent) { + this.data = data; + this.state = state; + itemCache.put(data, this); + if (parent != null) { + parent.children.add(this); + } + } + } + + /* A cache of all the nodes */ + Map itemCache = new HashMap(); + /* The preRefresh Listeners */ + List refreshingListeners = new ArrayList(); + + protected void unmapAllElements() { + itemCache = new HashMap(); + super.unmapAllElements(); + } + + /** + * FilterableCheckboxTreeViewer constructor. This creates the tree part of the filtered tree. + */ + public FilterableCheckboxTreeViewer(Composite parent, int style) { + super(parent, style); + addCheckStateListener(new ICheckStateListener() { + + public void checkStateChanged(CheckStateChangedEvent event) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(event.getElement()); + if (item != null) { + item.state = event.getChecked() ? CHECKED : NONE; + } + } + }); + } + + /** + * Allows clients to listen to the tree refresh. + */ + public void addPreRefreshNotifier(PreRefreshNotifier notifier) { + if (refreshingListeners.contains(notifier)) + return; + refreshingListeners.add(notifier); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#getChecked(java.lang.Object) + */ + public boolean getChecked(Object element) { + Widget testFindItem = getViewer().testFindItem(element); + testFindItem = null; + if (testFindItem == null) { + if (itemCache.containsKey(element)) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(element); + if (item.state.equals(CHECKED)) + return true; + if (item.state.equals(CHECKED_GREYED)) + return true; + if (item.state.equals(GREYED)) + return true; + else if (item.state.equals(NONE)) + return false; + } + } + return super.getChecked(element); + } + + public Object[] getCheckedChildren(Object element) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(element); + List checkedChildren = new ArrayList(); + if (item != null) { + List children = item.children; + Iterator iterator = children.iterator(); + while (iterator.hasNext()) { + FilteredCheckboxTreeItem child = (FilteredCheckboxTreeItem) iterator.next(); + if (child.state == CHECKED) { + checkedChildren.add(child.data); + } + } + } + return checkedChildren.toArray(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#getCheckedElements() + */ + public Object[] getCheckedElements() { + Iterator iterator = itemCache.values().iterator(); + List checkedElements = new LinkedList(); + while (iterator.hasNext()) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) iterator.next(); + Widget testFindItem = getViewer().testFindItem(item.data); + testFindItem = null; + if (testFindItem == null) { + if (item.state.equals(CHECKED) || item.state.equals(CHECKED_GREYED) || item.state.equals(GREYED)) { + checkedElements.add(item.data); + } + } else { + if (((TreeItem) testFindItem).getChecked()) { + checkedElements.add(testFindItem.getData()); + } + } + } + return checkedElements.toArray(); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#setChecked(java.lang.Object, boolean) + */ + public boolean setChecked(Object element, boolean state) { + if (itemCache.containsKey(element)) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(element); + item.state = state ? CHECKED : NONE; + } + return super.setChecked(element, state); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#setCheckedElements(java.lang.Object[]) + */ + public void setCheckedElements(Object[] elements) { + Set s = new HashSet(itemCache.keySet()); + s.removeAll(new HashSet(Arrays.asList(elements))); + for (int i = 0; i < elements.length; i++) { + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(elements[i]); + if (item != null) { + item.state = CHECKED; + } + } + for (Iterator iterator = s.iterator(); iterator.hasNext();) { + Object object = iterator.next(); + FilteredCheckboxTreeItem item = (FilteredCheckboxTreeItem) itemCache.get(object); + if (item != null) { + item.state = NONE; + } + } + super.setCheckedElements(elements); + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#setSubtreeChecked(java.lang.Object, boolean) + */ + public boolean setSubtreeChecked(Object element, boolean state) { + String newState = state ? CHECKED : NONE; + TreeItem item = (TreeItem) testFindItem(element); + FilteredCheckboxTreeItem filteredCheckboxTreeItem = (FilteredCheckboxTreeItem) itemCache.get(element); + if (item != null && filteredCheckboxTreeItem != null) { + filteredCheckboxTreeItem.state = newState; + TreeItem[] items = item.getItems(); + for (int i = 0; i < items.length; i++) { + item = items[i]; + if (item != null) { + filteredCheckboxTreeItem = (FilteredCheckboxTreeItem) itemCache.get(item.getData()); + if (filteredCheckboxTreeItem != null) { + filteredCheckboxTreeItem.state = newState; + } + } + } + } + return super.setSubtreeChecked(element, state); + } + + /* + public boolean setSubtreeChecked(Object element, boolean state) { + String newState = state ? CHECKED : NONE; + FilteredCheckboxTreeItem filteredCheckboxTreeItem = (FilteredCheckboxTreeItem) itemCache.get(element); + if (filteredCheckboxTreeItem != null) { + filteredCheckboxTreeItem.state = newState; + List children = filteredCheckboxTreeItem.children; + for (Iterator iterator = children.iterator(); iterator.hasNext();) { + FilteredCheckboxTreeItem child = (FilteredCheckboxTreeItem) iterator.next(); + child.state = newState; + } + } + return super.setSubtreeChecked(element, state); + } + */ + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.CheckboxTreeViewer#preservingSelection(java.lang.Runnable) + */ + protected void preservingSelection(Runnable updateCode) { + super.preservingSelection(updateCode); + + // Re-apply the checked state + ArrayList allTreeItems = getAllTreeItems(treeViewer.getTree().getItems()); + for (Iterator iterator = allTreeItems.iterator(); iterator.hasNext();) { + TreeItem item = (TreeItem) iterator.next(); + doApplyCheckedState(item, item.getData()); + } + } + + /* + * (non-Javadoc) + * @see org.eclipse.jface.viewers.AbstractTreeViewer#internalRefresh(java.lang.Object, boolean) + */ + protected void internalRefresh(Object element, boolean updateLabels) { + String text = FilteredCheckboxTree.this.getFilterString(); + boolean initial = initialText != null && initialText.equals(text); + boolean filtered = (text.length() > 0 && !initial); + + // Notify anybody who is listening for the refresh + for (Iterator iterator = refreshingListeners.iterator(); iterator.hasNext();) { + PreRefreshNotifier notifier = (PreRefreshNotifier) iterator.next(); + notifier.preRefresh(FilterableCheckboxTreeViewer.this, filtered); + } + saveCheckedState(); + super.internalRefresh(element, updateLabels); + treeViewer.expandAll(); + } + + /* + * Set the checked state + */ + private void doApplyCheckedState(Item item, Object element) { + // update the item first + super.doUpdateItem(item, element); + + // Update the checked state + TreeItem treeItem = (TreeItem) item; + if (itemCache.containsKey(element)) { + String state = ((FilteredCheckboxTreeItem) itemCache.get(element)).state; + if (state.equals(CHECKED_GREYED)) { + treeItem.setGrayed(true); + treeItem.setChecked(true); + } else if (state.equals(CHECKED)) { + treeItem.setChecked(true); + treeItem.setGrayed(false); + } else if (state.equals(GREYED)) { + treeItem.setGrayed(true); + treeItem.setChecked(false); + } else { + treeItem.setGrayed(false); + treeItem.setChecked(false); + } + } + } + + /* + * A helper method to get all the items in the tree + */ + private ArrayList getAllTreeItems(TreeItem[] roots) { + ArrayList list = new ArrayList(); + for (int i = 0; i < roots.length; i++) { + TreeItem item = roots[i]; + list.add(item); + list.addAll(getAllTreeItems(item.getItems())); + } + return list; + } + + /** + * Saves the checked state of all the elements in the tree + */ + private void saveCheckedState() { + TreeItem[] items = treeViewer.getTree().getItems(); + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + if (!itemCache.containsKey(item.getData())) { + new FilteredCheckboxTreeItem(item.getData(), getItemState(item), itemCache, null); + } + FilteredCheckboxTreeItem filteredCheckboxTreeItem = (FilteredCheckboxTreeItem) itemCache.get(item.getData()); + filteredCheckboxTreeItem.state = getItemState(item); + saveCheckedState(filteredCheckboxTreeItem, item); + } + } + + /** + * Saves the checked state of an item and all its children + */ + private void saveCheckedState(FilteredCheckboxTreeItem parent, TreeItem parentItem) { + TreeItem[] items = parentItem.getItems(); + for (int i = 0; i < items.length; i++) { + TreeItem item = items[i]; + if (!itemCache.containsKey(item.getData())) { + new FilteredCheckboxTreeItem(item.getData(), getItemState(item), itemCache, parent); + } + FilteredCheckboxTreeItem filteredCheckboxTreeItem = (FilteredCheckboxTreeItem) itemCache.get(item.getData()); + filteredCheckboxTreeItem.state = getItemState(item); + saveCheckedState(filteredCheckboxTreeItem, item); + } + } + + /** + * Computes the checked state from a tree item + */ + private String getItemState(TreeItem item) { + if (item.getChecked() && item.getGrayed()) { + return CHECKED_GREYED; + } else if (item.getChecked()) { + return CHECKED; + } else if (item.getGrayed()) { + return GREYED; + } else { + return NONE; + } + } + + } // end of FilterableCheckboxTreeViewer + +}