Download
Getting Started
Members
Projects
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
More
Community
Marketplace
Events
Planet Eclipse
Newsletter
Videos
Participate
Report a Bug
Forums
Mailing Lists
Wiki
IRC
How to Contribute
Working Groups
Automotive
Internet of Things
LocationTech
Long-Term Support
PolarSys
Science
OpenMDM
Toggle navigation
Bugzilla – Attachment 211150 Details for
Bug 360894
[patch] Extensions tree viewer should also be filtered by leaf item's attributes
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
Log In
[x]
|
Terms of Use
|
Copyright Agent
[patch]
Extensions page enhancements
Bug 360894.patch (text/plain), 101.67 KB, created by
Sascha Becher
on 2012-02-16 18:29:12 EST
(
hide
)
Description:
Extensions page enhancements
Filename:
MIME Type:
Creator:
Sascha Becher
Created:
2012-02-16 18:29:12 EST
Size:
101.67 KB
patch
obsolete
>From 0c99b50a598b6741db7e13f6606b557d692cc1fc Thu, 16 Feb 2012 22:04:36 +0100 >From: zour <s.becher@qualitype.de> >Date: Thu, 16 Feb 2012 21:59:48 +0100 >Subject: [PATCH] Bug 360894 - Extensions tree viewer should also be filtered by leaf item's attributes > >diff --git a/ui/org.eclipse.pde.ui/icons/dlcl16/filter_related.gif b/ui/org.eclipse.pde.ui/icons/dlcl16/filter_related.gif >new file mode 100644 >index 0000000..f07bf77 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/dlcl16/filter_related.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/dlcl16/search_extensions.gif b/ui/org.eclipse.pde.ui/icons/dlcl16/search_extensions.gif >new file mode 100644 >index 0000000..8f7cbab >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/dlcl16/search_extensions.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/dlcl16/toggle_expand_state.gif b/ui/org.eclipse.pde.ui/icons/dlcl16/toggle_expand_state.gif >new file mode 100644 >index 0000000..e868b95 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/dlcl16/toggle_expand_state.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/elcl16/filter_related.gif b/ui/org.eclipse.pde.ui/icons/elcl16/filter_related.gif >new file mode 100644 >index 0000000..b983f27 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/elcl16/filter_related.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/elcl16/search_extensions.gif b/ui/org.eclipse.pde.ui/icons/elcl16/search_extensions.gif >new file mode 100644 >index 0000000..6d1094e >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/elcl16/search_extensions.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/elcl16/toggle_expand_state.gif b/ui/org.eclipse.pde.ui/icons/elcl16/toggle_expand_state.gif >new file mode 100644 >index 0000000..f61d0b9 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/elcl16/toggle_expand_state.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/icons/obj16/esearch_obj.gif b/ui/org.eclipse.pde.ui/icons/obj16/esearch_obj.gif >new file mode 100644 >index 0000000..32860cb >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/icons/obj16/esearch_obj.gif >Binary files differ >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java >index 88c9ab2..c22b465 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java >@@ -153,6 +153,7 @@ > public static final ImageDescriptor DESC_INFO_ST_OBJ = create(PATH_OBJ, "info_st_obj.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_CATEGORY_OBJ = create(PATH_OBJ, "category_obj.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_PSEARCH_OBJ = create(PATH_OBJ, "psearch_obj.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_ESEARCH_OBJ = create(PATH_OBJ, "esearch_obj.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_SITE_OBJ = create(PATH_OBJ, "site_obj.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_JUNIT_MAIN_TAB = create(PATH_OBJ, "test.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_OUTPUT_FOLDER_OBJ = create(PATH_OBJ, "output_folder_attrib.gif"); //$NON-NLS-1$ >@@ -232,6 +233,7 @@ > public static final ImageDescriptor DESC_VERTICAL = create(PATH_LCL, "th_vertical.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_COLLAPSE_ALL = create(PATH_LCL, "collapseall.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_COLLAPSE_ALL_MINI = create(PATH_LCL, "collapse_all_mini.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_TOGGLE_EXPAND_STATE = create(PATH_LCL, "toggle_expand_state.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_HELP = create(PATH_LCL, "help.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_LINK_WITH_EDITOR = create(PATH_LCL, "synced.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_CALLEES = create(PATH_LCL, "ch_callees.gif"); //$NON-NLS-1$ >@@ -242,6 +244,8 @@ > public static final ImageDescriptor DESC_HISTORY_LIST = create(PATH_LCL, "history_list.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_CLEAR = create(PATH_LCL, "clear.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_FILTER = create(PATH_LCL, "filter_ps.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_FILTER_RELATED = create(PATH_LCL, "filter_related.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_SEARCH_EXTENSIONS = create(PATH_LCL, "search_extensions.gif"); //$NON-NLS-1$ > > public static final ImageDescriptor DESC_ADD_ATT_DISABLED = create(PATH_LCL_DISABLED, "add_att.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_ALPHAB_SORT_CO_DISABLED = create(PATH_LCL_DISABLED, "alphab_sort_co.gif"); //$NON-NLS-1$ >@@ -255,6 +259,7 @@ > public static final ImageDescriptor DESC_HORIZONTAL_DISABLED = create(PATH_LCL_DISABLED, "th_horizontal.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_VERTICAL_DISABLED = create(PATH_LCL_DISABLED, "th_vertical.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_COLLAPSE_ALL_DISABLED = create(PATH_LCL_DISABLED, "collapseall.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_TOGGLE_EXPAND_STATE_DISABLED = create(PATH_LCL_DISABLED, "toggle_expand_state.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_LINK_WITH_EDITOR_DISABLED = create(PATH_LCL_DISABLED, "synced.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_CALLEES_DISABLED = create(PATH_LCL_DISABLED, "ch_callees.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_CALLERS_DISABLED = create(PATH_LCL_DISABLED, "ch_callers.gif"); //$NON-NLS-1$ >@@ -264,6 +269,8 @@ > public static final ImageDescriptor DESC_HISTORY_LIST_DISABLED = create(PATH_LCL_DISABLED, "history_list.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_DCLEAR = create(PATH_LCL_DISABLED, "clear.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_FILTER_DISABLED = create(PATH_LCL_DISABLED, "filter_ps.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_FILTER_RELATED_DISABLED = create(PATH_LCL_DISABLED, "filter_related.gif"); //$NON-NLS-1$ >+ public static final ImageDescriptor DESC_SEARCH_EXTENSIONS_DISABLED = create(PATH_LCL_DISABLED, "search_extensions.gif"); //$NON-NLS-1$ > > public static final ImageDescriptor DESC_RUN_EXC = create(PATH_OBJ, "run_exc.gif"); //$NON-NLS-1$ > public static final ImageDescriptor DESC_DEBUG_EXC = create(PATH_OBJ, "debug_exc.gif"); //$NON-NLS-1$ >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java >index cfae1ea7..978d594 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java >@@ -54,6 +54,8 @@ > > public static String DependencyPropertiesDialog_exportGroupText; > >+ public static String ExtensionsPage_toggleExpandState; >+ public static String ExtensionsPage_searchWithExtensionsFilter; > public static String ExternalizeStringsOperation_editNames_addComment; > > public static String ExternalizeStringsOperation_editNames_insertProperty; >@@ -1425,6 +1427,8 @@ > public static String EditorActions_revert; > public static String Actions_open_label; > public static String Actions_delete_label; >+ public static String Actions_filter_relatedPluginElements; >+ public static String Actions_search_relatedPluginElements; > public static String Actions_synchronizeVersions_label; > > public static String Menus_new_label; >@@ -1501,6 +1505,8 @@ > public static String SearchAction_Declaration; > public static String ShowDescriptionAction_label; > public static String ShowDescriptionAction_title; >+ public static String ShowAllExtensionsAction_label; >+ public static String HideUnfilteredExtensionsAction_label; > public static String ShowSampleAction_installing; > public static String ShowSampleAction_title; > public static String ShowSampleAction_msgDesc; >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/FilterRelatedExtensionsAction.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/FilterRelatedExtensionsAction.java >new file mode 100644 >index 0000000..40e2721 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/FilterRelatedExtensionsAction.java >@@ -0,0 +1,61 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.editor.actions; >+ >+import org.eclipse.jface.action.Action; >+import org.eclipse.jface.viewers.IStructuredSelection; >+import org.eclipse.jface.viewers.TreeViewer; >+import org.eclipse.pde.internal.ui.PDEPluginImages; >+import org.eclipse.pde.internal.ui.PDEUIMessages; >+import org.eclipse.pde.internal.ui.editor.plugin.ExtensionsSection; >+import org.eclipse.pde.internal.ui.editor.plugin.FormFilteredTree; >+import org.eclipse.pde.internal.ui.util.ExtensionsFilterUtil; >+import org.eclipse.swt.widgets.Text; >+import org.eclipse.ui.PlatformUI; >+import org.eclipse.ui.actions.ActionFactory; >+import org.eclipse.ui.keys.IBindingService; >+ >+/** >+ * Set the search pattern text to all values found by attribute list {@link ExtensionsFilterUtil#RELATED_ATTRIBUTES} >+ * >+ * @author Sascha Becher >+ */ >+public class FilterRelatedExtensionsAction extends Action { >+ >+ protected ExtensionsSection fSection; >+ protected TreeViewer fExtensionTree; >+ protected FormFilteredTree fFilteredTree; >+ >+ public FilterRelatedExtensionsAction(TreeViewer treeViewer, FormFilteredTree filteredTree, ExtensionsSection section, boolean inContextMenu) { >+ setImageDescriptor(PDEPluginImages.DESC_FILTER_RELATED); >+ setDisabledImageDescriptor(PDEPluginImages.DESC_FILTER_RELATED_DISABLED); >+ String filterBinding = ((IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class)).getBestActiveBindingFormattedFor(ActionFactory.FIND.getCommandId()); >+ String title = PDEUIMessages.Actions_filter_relatedPluginElements + ((filterBinding != null) ? "\t" + filterBinding : ""); //$NON-NLS-1$ //$NON-NLS-2$ >+ String toolTip = PDEUIMessages.Actions_filter_relatedPluginElements + ((filterBinding != null) ? " (" + filterBinding + ")" : ""); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ if (inContextMenu) { >+ setText(title); >+ } >+ setToolTipText(toolTip); >+ fSection = section; >+ fExtensionTree = treeViewer; >+ fFilteredTree = filteredTree; >+ } >+ >+ public void run() { >+ String filterPattern = ExtensionsFilterUtil.getFilterRelatedPattern((IStructuredSelection) fExtensionTree.getSelection()); >+ Text filterControl = fFilteredTree.getFilterControl(); >+ if (filterControl != null && filterPattern.length() > 0) { >+ fSection.setBypassFilterDelay(true); // force immediate job run >+ filterControl.setText(filterPattern); >+ } >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/SearchExtensionsAction.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/SearchExtensionsAction.java >new file mode 100644 >index 0000000..aec84c0 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/SearchExtensionsAction.java >@@ -0,0 +1,80 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.editor.actions; >+ >+import org.eclipse.jface.action.Action; >+import org.eclipse.jface.viewers.ISelection; >+import org.eclipse.jface.viewers.IStructuredSelection; >+import org.eclipse.pde.internal.core.search.PluginSearchInput; >+import org.eclipse.pde.internal.core.search.PluginSearchScope; >+import org.eclipse.pde.internal.ui.PDEPluginImages; >+import org.eclipse.pde.internal.ui.editor.plugin.FormFilteredTree; >+import org.eclipse.pde.internal.ui.search.FindExtensionsByAttributeQuery; >+import org.eclipse.pde.internal.ui.util.ExtensionsFilterUtil; >+import org.eclipse.search.ui.ISearchQuery; >+import org.eclipse.search.ui.NewSearchUI; >+ >+/** >+ * Search in workspace plugins for occurences of either the current filter text or filter related attributes >+ * using the ExtensionsPatternFilter search behaviour. >+ * >+ * @author Sascha Becher >+ */ >+public class SearchExtensionsAction extends Action { >+ >+ protected FormFilteredTree fFilteredTree; >+ >+ private IStructuredSelection fSelection; >+ private String fFilterRelatedText; >+ >+ public SearchExtensionsAction(FormFilteredTree filteredTree, String actionText) { >+ this(filteredTree.getViewer().getSelection(), actionText); >+ fFilteredTree = filteredTree; >+ } >+ >+ public SearchExtensionsAction(ISelection selection, String actionText) { >+ setImageDescriptor(PDEPluginImages.DESC_SEARCH_EXTENSIONS); >+ setDisabledImageDescriptor(PDEPluginImages.DESC_SEARCH_EXTENSIONS_DISABLED); >+ setText(actionText); >+ if (selection != null && selection instanceof IStructuredSelection) { >+ fSelection = (IStructuredSelection) selection; >+ } >+ } >+ >+ public void run() { >+ if (fSelection != null) { >+ this.fFilterRelatedText = ExtensionsFilterUtil.getFilterRelatedPattern(fSelection); >+ NewSearchUI.activateSearchResultView(); >+ NewSearchUI.runQueryInBackground(createSearchQuery()); >+ } >+ } >+ >+ protected ISearchQuery createSearchQuery() { >+ PluginSearchInput input = new PluginSearchInput(); >+ input.setSearchElement(PluginSearchInput.ELEMENT_PLUGIN); >+ input.setSearchLimit(PluginSearchInput.LIMIT_ALL); >+ input.setSearchString(getFilterText()); >+ input.setSearchScope(new PluginSearchScope(PluginSearchScope.SCOPE_WORKSPACE, PluginSearchScope.EXTERNAL_SCOPE_ALL, null)); >+ input.setCaseSensitive(false); >+ return new FindExtensionsByAttributeQuery(input); >+ } >+ >+ private String getFilterText() { >+ if (fFilterRelatedText != null && fFilterRelatedText.length() > 0) { >+ return fFilterRelatedText; >+ } >+ if (fFilteredTree != null) { >+ return fFilteredTree.getFilterControl().getText(); >+ } >+ return new String(); >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ShowAllExtensionsAction.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ShowAllExtensionsAction.java >new file mode 100644 >index 0000000..25b6a58 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ShowAllExtensionsAction.java >@@ -0,0 +1,130 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.editor.actions; >+ >+import java.util.*; >+import org.eclipse.jface.action.Action; >+import org.eclipse.jface.viewers.ISelection; >+import org.eclipse.pde.core.IBaseModel; >+import org.eclipse.pde.core.plugin.*; >+import org.eclipse.pde.internal.core.text.plugin.PluginNode; >+import org.eclipse.pde.internal.ui.PDEUIMessages; >+import org.eclipse.pde.internal.ui.editor.plugin.FormFilteredTree; >+import org.eclipse.pde.internal.ui.search.ExtensionsPatternFilter; >+import org.eclipse.swt.widgets.TreeItem; >+ >+/** >+ * Reveals all extensions when the tree is in filter mode. >+ * The purpose is convenience. When the Filter Related action shows that >+ * a certain item that should have been found with the search is missing, >+ * it is convenient to bring up the required extension to add the >+ * missing element without loosing the focus on the search result. >+ * Once all extensions are revealed this action hides them again except >+ * for those extensions that received new elements. >+ * >+ * @author Sascha Becher >+ */ >+public class ShowAllExtensionsAction extends Action { >+ >+ private static int SHOW_ALL = 0; >+ private static int HIDE_UNFILTERED = 1; >+ >+ private int mode; >+ private FormFilteredTree fFilteredTree; >+ private IPluginModelBase fModel; >+ private ExtensionsPatternFilter fPatternFilter; >+ >+ public ShowAllExtensionsAction(IBaseModel model, FormFilteredTree filteredTree, ExtensionsPatternFilter patternFilter) { >+ fModel = (IPluginModelBase) model; >+ fFilteredTree = filteredTree; >+ fPatternFilter = patternFilter; >+ mode = getRequiredChange(); >+ setText(mode == SHOW_ALL ? PDEUIMessages.ShowAllExtensionsAction_label : PDEUIMessages.HideUnfilteredExtensionsAction_label); >+ } >+ >+ public void run() { >+ if (mode == SHOW_ALL) { >+ IPluginExtension[] extensions = fModel.getExtensions().getExtensions(); >+ try { >+ ISelection selection = fFilteredTree.getViewer().getSelection(); >+ Object[] expanded = fFilteredTree.getViewer().getVisibleExpandedElements(); >+ fFilteredTree.setRedraw(false); >+ for (int i = 0; i < extensions.length; i++) { >+ fPatternFilter.addElement(extensions[i]); >+ } >+ fFilteredTree.update(); >+ fFilteredTree.redraw(); >+ fFilteredTree.getViewer().refresh(); >+ >+ TreeItem[] treeItems = fFilteredTree.getViewer().getTree().getItems(); >+ for (int i = 0; i < treeItems.length; i++) { >+ TreeItem treeItem = treeItems[i]; >+ if (treeItem != null && !treeItem.getExpanded()) { >+ treeItem.setExpanded(true); >+ } >+ } >+ fFilteredTree.getViewer().refresh(); >+ fFilteredTree.getViewer().setExpandedElements(expanded); >+ fFilteredTree.getViewer().setSelection(selection); >+ } finally { >+ fFilteredTree.setRedraw(true); >+ } >+ } else if (mode == HIDE_UNFILTERED) { >+ List unfiltered = getUnfilteredExtensions(fPatternFilter.getMatchingLeafs()); >+ for (Iterator iterator = unfiltered.iterator(); iterator.hasNext();) { >+ fPatternFilter.removeElement(iterator.next()); >+ } >+ } >+ fFilteredTree.getViewer().refresh(); >+ } >+ >+ public int getRequiredChange() { >+ boolean visible = true; >+ IPluginExtension[] extensions = fModel.getExtensions().getExtensions(); >+ for (int i = 0; i < extensions.length; i++) { >+ IPluginExtension iPluginExtension = extensions[i]; >+ visible &= fPatternFilter.containsElement(iPluginExtension); >+ } >+ if (visible) { >+ return HIDE_UNFILTERED; >+ } >+ return SHOW_ALL; >+ } >+ >+ private List getUnfilteredExtensions(Collection matchingLeafs) { >+ List unfilteredExtensions = new ArrayList(); >+ try { >+ fFilteredTree.getViewer().setExpandPreCheckFilters(true); >+ IPluginExtension[] extensions = fModel.getPluginBase().getExtensions(); >+ for (int i = 0; i < extensions.length; i++) { >+ IPluginExtension iPluginExtension = extensions[i]; >+ boolean found = false; >+ for (Iterator it = matchingLeafs.iterator(); it.hasNext();) { >+ IPluginObject element = ((IPluginObject) it.next()); >+ while (element.getParent() != null && !(element.getParent() instanceof PluginNode)) { >+ element = element.getParent(); >+ } >+ if (element.equals(iPluginExtension) || fFilteredTree.getViewer().isExpandable(iPluginExtension)) { >+ found = true; >+ break; >+ } >+ } >+ if (!found) { >+ unfilteredExtensions.add(iPluginExtension); >+ } >+ } >+ } finally { >+ fFilteredTree.getViewer().setExpandPreCheckFilters(false); >+ } >+ return unfilteredExtensions; >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ToggleExpandStateAction.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ToggleExpandStateAction.java >new file mode 100644 >index 0000000..aac421e >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ToggleExpandStateAction.java >@@ -0,0 +1,181 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.editor.actions; >+ >+import java.util.Iterator; >+import org.eclipse.jface.action.Action; >+import org.eclipse.jface.viewers.*; >+import org.eclipse.pde.core.plugin.IPluginParent; >+import org.eclipse.pde.internal.core.text.plugin.PluginExtensionNode; >+import org.eclipse.pde.internal.core.text.plugin.PluginParentNode; >+import org.eclipse.pde.internal.ui.PDEPluginImages; >+import org.eclipse.pde.internal.ui.PDEUIMessages; >+import org.eclipse.pde.internal.ui.editor.plugin.FormFilteredTree; >+import org.eclipse.pde.internal.ui.search.ExtensionsPatternFilter; >+import org.eclipse.swt.widgets.TreeItem; >+ >+/** >+ * Expands and collapses selected tree nodes in the extension elements tree viewer upon their expand state. >+ * >+ * @author Sascha Becher >+ */ >+public class ToggleExpandStateAction extends Action { >+ >+ public static int NEEDS_EXPAND = 1; >+ public static int NEEDS_COLLAPSE = 2; >+ >+ protected TreeViewer fExtensionTree; >+ protected FormFilteredTree fFilteredTree; >+ >+ public ToggleExpandStateAction(FormFilteredTree filteredTree, TreeViewer treeViewer) { >+ setImageDescriptor(PDEPluginImages.DESC_TOGGLE_EXPAND_STATE); >+ setDisabledImageDescriptor(PDEPluginImages.DESC_TOGGLE_EXPAND_STATE_DISABLED); >+ setText(PDEUIMessages.ExtensionsPage_toggleExpandState); >+ fExtensionTree = treeViewer; >+ fFilteredTree = filteredTree; >+ } >+ >+ public void run() { >+ StructuredSelection selection = (StructuredSelection) fExtensionTree.getSelection(); >+ if (fExtensionTree.getTree().getSelectionCount() > 0) { >+ TreeItem[] items = fExtensionTree.getTree().getSelection(); >+ try { >+ fFilteredTree.setRedraw(false); >+ int state = getStateChangeRequired(items); >+ toggleExpandState(state, selection); >+ } finally { >+ fFilteredTree.setRedraw(true); >+ fExtensionTree.refresh(); >+ } >+ } >+ } >+ >+ public void toggleExpandState(int state, StructuredSelection selection) { >+ TreeItem[] items = fExtensionTree.getTree().getSelection(); >+ if (state == NEEDS_EXPAND) { // expand sub tree >+ traverseChildrenAndSetExpanded(items); // load non-expanded children >+ fExtensionTree.refresh(); >+ expandChildrenElements(selection.toArray(), true); >+ fExtensionTree.setSelection(selection, false); >+ } else { // collapse sub tree >+ for (Iterator iterator = selection.iterator(); iterator.hasNext();) { >+ fExtensionTree.setExpandedState(iterator.next(), false); >+ } >+ } >+ } >+ >+ public int getStateChangeRequired(TreeItem[] selection) { >+ return (traverseStateChangeRequired(selection)) ? NEEDS_EXPAND : NEEDS_COLLAPSE; >+ } >+ >+ /** >+ * @param items items to be traversed >+ * @return <code>true</code> if at least one of the tree items could be expanded but is not. Otherwise <code>false</code> is returned. >+ */ >+ protected boolean traverseStateChangeRequired(TreeItem[] items) { >+ if (items != null) { >+ for (int i = 0; i < items.length; i++) { >+ TreeItem treeItem = items[i]; >+ TreeItem[] children = treeItem.getItems(); >+ if (children.length > 0) { >+ if (treeItem.getExpanded()) { >+ if (traverseStateChangeRequired(children)) { >+ return true; >+ } >+ } else { >+ return true; >+ } >+ } >+ } >+ } >+ return false; >+ } >+ >+ /** >+ * Expands subtrees of given items. Items of type <code>PluginExtensionNode</code> that have multiple children to expand >+ * will only be expanded to the that level. Further expanding is required to reveal the whole subtree. This is for reasons of >+ * convenience. >+ * >+ * @param items tree items to be expand with their children >+ */ >+ private void traverseChildrenAndSetExpanded(TreeItem[] items) { >+ for (int i = 0; i < items.length; i++) { >+ TreeItem treeItem = items[i]; >+ TreeItem[] children = treeItem.getItems(); >+ int extensionsChildCount = getExtensionsChildCount((IPluginParent) treeItem.getData()); >+ boolean furtherExpanding = !(extensionsChildCount > 1 && (!treeItem.getExpanded())); >+ treeItem.setExpanded(furtherExpanding); >+ if (furtherExpanding) { >+ traverseChildrenAndSetExpanded(children); >+ } >+ } >+ } >+ >+ private int getExtensionsChildCount(IPluginParent leafData) { >+ int extensionsChildCount = 0; >+ if (leafData != null && leafData instanceof PluginExtensionNode) { >+ if (!fFilteredTree.isFiltered()) { >+ return leafData.getChildCount(); >+ } >+ ExtensionsPatternFilter filter = (ExtensionsPatternFilter) fFilteredTree.getPatternFilter(); >+ for (int j = 0; j < leafData.getChildCount(); j++) { >+ if (filter.containsElement(leafData.getChildren()[j])) { >+ extensionsChildCount++; >+ } >+ } >+ } >+ return extensionsChildCount; >+ } >+ >+ /** >+ * @param children list of elements to be expand with their children >+ */ >+ private void expandChildrenElements(Object[] children, boolean fullExpand) { >+ for (int i = 0; i < children.length; i++) { >+ Object child = children[i]; >+ if (child instanceof PluginParentNode) { >+ PluginParentNode node = (PluginParentNode) child; >+ if (node.getChildCount() > 0 && fullExpand) { >+ boolean furtherExpanding = !(node instanceof PluginExtensionNode && !fExtensionTree.getExpandedState(node)); >+ expandChildrenElements(node.getChildren(), furtherExpanding); >+ } else { >+ fExtensionTree.expandToLevel(node, 0); >+ } >+ } >+ } >+ } >+ >+ /** >+ * Determines whether the selected leafs are expandable >+ * >+ * @param selection selection to test each item with >+ * @return whether the selection can be expanded >+ */ >+ public static boolean isExpandable(IStructuredSelection selection) { >+ boolean isExpandable = false; >+ if (selection != null) { >+ if (!selection.isEmpty()) { >+ for (Iterator iterator = selection.iterator(); iterator.hasNext();) { >+ Object element = iterator.next(); >+ if (element instanceof PluginParentNode) { >+ PluginParentNode node = (PluginParentNode) element; >+ if (node.getChildCount() > 0) { >+ isExpandable = true; >+ break; >+ } >+ } >+ } >+ } >+ } >+ return isExpandable; >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/AbstractPluginElementDetails.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/AbstractPluginElementDetails.java >index aad91fc..bee2428 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/AbstractPluginElementDetails.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/AbstractPluginElementDetails.java >@@ -13,6 +13,9 @@ > > import org.eclipse.pde.internal.ui.editor.PDEDetails; > import org.eclipse.pde.internal.ui.editor.PDESection; >+import org.eclipse.swt.custom.CCombo; >+import org.eclipse.swt.widgets.*; >+import org.eclipse.ui.actions.ActionFactory; > > public abstract class AbstractPluginElementDetails extends PDEDetails { > >@@ -26,4 +29,25 @@ > return fMasterSection; > } > >-} >+ public boolean doGlobalAction(String actionId) { >+ // TODO reveal the keybinding Ctrl+F to the user, ideally by showing the action in the context menu >+ if (actionId.equals(ActionFactory.FIND.getId())) { >+ if (fMasterSection != null && fMasterSection instanceof ExtensionsSection) { >+ final Control focusControl = Display.getCurrent().getFocusControl(); // getPage().getLastFocusControl(); >+ String filterText = (focusControl instanceof Text) ? ((Text) focusControl).getText() : (focusControl instanceof CCombo) ? ((CCombo) focusControl).getText() : null; >+ if (filterText != null) { >+ // add value of the currently focused attribute text to the filter >+ ((ExtensionsSection) fMasterSection).addAttributeToFilter(filterText, true); >+ Display.getCurrent().asyncExec(new Runnable() { >+ public void run() { >+ // bugfix: after tree refresh bring focus back to the element details form >+ getPage().updateFormSelection(); >+ } >+ }); >+ } >+ } >+ } >+ return super.doGlobalAction(actionId); >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExtensionsSection.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExtensionsSection.java >index 2c9b627..46b9e7b 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExtensionsSection.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExtensionsSection.java >@@ -1,5 +1,5 @@ > /******************************************************************************* >- * Copyright (c) 2000, 2011 IBM Corporation and others. >+ * Copyright (c) 2000, 2012 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,14 +8,19 @@ > * Contributors: > * IBM Corporation - initial API and implementation > * Peter Friese <peter.friese@gentleware.com> - bug 194529, bug 196867 >+ * Sascha Becher <s.becher@qualitype.com> - bug 360894 > *******************************************************************************/ > package org.eclipse.pde.internal.ui.editor.plugin; > > import java.util.*; > import org.eclipse.core.resources.IProject; > import org.eclipse.core.runtime.*; >+import org.eclipse.core.runtime.Path; >+import org.eclipse.core.runtime.jobs.IJobChangeEvent; >+import org.eclipse.core.runtime.jobs.JobChangeAdapter; > import org.eclipse.jface.action.*; > import org.eclipse.jface.dialogs.IMessageProvider; >+import org.eclipse.jface.resource.JFaceResources; > import org.eclipse.jface.util.IPropertyChangeListener; > import org.eclipse.jface.util.PropertyChangeEvent; > import org.eclipse.jface.viewers.*; >@@ -30,54 +35,68 @@ > import org.eclipse.pde.internal.core.schema.SchemaRegistry; > import org.eclipse.pde.internal.core.text.IDocumentElementNode; > import org.eclipse.pde.internal.core.text.plugin.PluginBaseNode; >+import org.eclipse.pde.internal.core.text.plugin.PluginExtensionNode; > import org.eclipse.pde.internal.ui.*; > import org.eclipse.pde.internal.ui.editor.*; >-import org.eclipse.pde.internal.ui.editor.actions.CollapseAction; >-import org.eclipse.pde.internal.ui.editor.actions.SortAction; >+import org.eclipse.pde.internal.ui.editor.actions.*; > import org.eclipse.pde.internal.ui.editor.contentassist.XMLElementProposalComputer; > import org.eclipse.pde.internal.ui.elements.DefaultContentProvider; > import org.eclipse.pde.internal.ui.parts.TreePart; >+import org.eclipse.pde.internal.ui.search.ExtensionsPatternFilter; > import org.eclipse.pde.internal.ui.search.PluginSearchActionGroup; >-import org.eclipse.pde.internal.ui.util.SWTUtil; >+import org.eclipse.pde.internal.ui.util.*; > import org.eclipse.pde.internal.ui.wizards.extension.ExtensionEditorWizard; > import org.eclipse.pde.internal.ui.wizards.extension.NewExtensionWizard; > import org.eclipse.pde.ui.IExtensionEditorWizard; > import org.eclipse.swt.SWT; > import org.eclipse.swt.custom.BusyIndicator; >+import org.eclipse.swt.dnd.Clipboard; > import org.eclipse.swt.events.*; >-import org.eclipse.swt.graphics.Cursor; >-import org.eclipse.swt.graphics.Image; >+import org.eclipse.swt.graphics.*; > import org.eclipse.swt.internal.BidiUtil; > import org.eclipse.swt.widgets.*; > import org.eclipse.ui.actions.ActionContext; > import org.eclipse.ui.actions.ActionFactory; >-import org.eclipse.ui.dialogs.PatternFilter; > import org.eclipse.ui.forms.widgets.FormToolkit; > import org.eclipse.ui.forms.widgets.Section; >+import org.eclipse.ui.progress.WorkbenchJob; > > public class ExtensionsSection extends TreeSection implements IModelChangedListener, IPropertyChangeListener { >+ private static final int REFRESHJOB_DELAY_TIME = 1200; // milliseconds to wait >+ private static final int ACCELERATED_SCROLLING = 15; // lines to skip > private static final int BUTTON_MOVE_DOWN = 4; > private static final int BUTTON_MOVE_UP = 3; > private static final int BUTTON_EDIT = 2; > private static final int BUTTON_REMOVE = 1; >+ private static final int BUTTON_ADD = 0; > private TreeViewer fExtensionTree; > private Image fExtensionImage; > private Image fGenericElementImage; > private FormFilteredTree fFilteredTree; >+ private ExtensionsPatternFilter fPatternFilter; > private SchemaRegistry fSchemaRegistry; > private Hashtable fEditorWizards; > private SortAction fSortAction; > private CollapseAction fCollapseAction; >+ private ToggleExpandStateAction fExpandAction; >+ private FilterRelatedExtensionsAction fFilterRelatedAction; >+ private SearchExtensionsAction fSearchAction; >+ private boolean fBypassFilterDelay = false; > >- private static final int BUTTON_ADD = 0; >- >- private static final String[] COMMON_LABEL_PROPERTIES = {"label", //$NON-NLS-1$ >- "name", //$NON-NLS-1$ >- "id", //$NON-NLS-1$ >- "commandId", //$NON-NLS-1$ >- "activityId"}; //$NON-NLS-1$ >+ /** >+ * <code>label, name, class, id, commandId, property, activityId, attribute, value</code> >+ * <br> >+ * While adding elements to the array at the end is possible without concern, changing >+ * previous elements requires to refactor occurences with indexed access to the array. >+ */ >+ // TODO common label properties might be configured through preferences >+ public static final String[] COMMON_LABEL_ATTRIBUTES = {"label", //$NON-NLS-1$ >+ "name", "locationURI", "class", "id", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ >+ "commandId", "property", "activityId", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ "attribute", "value"}; //$NON-NLS-1$ //$NON-NLS-2$ > > private static final String[] VALID_IMAGE_TYPES = {"png", "bmp", "ico", "gif", "jpg", "tiff"}; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ >+ private static final String MENU_NEW_ID = "NewMenu"; //$NON-NLS-1$ > > class ExtensionContentProvider extends DefaultContentProvider implements ITreeContentProvider { > public Object[] getChildren(Object parent) { >@@ -112,13 +131,20 @@ > } > } > >- class ExtensionLabelProvider extends LabelProvider { >+ class ExtensionLabelProvider extends LabelProvider implements IFontProvider { > public String getText(Object obj) { > return resolveObjectName(obj); > } > > public Image getImage(Object obj) { > return resolveObjectImage(obj); >+ } >+ >+ public Font getFont(Object element) { >+ if (fFilteredTree.isFiltered() && fPatternFilter.getMatchingLeafs().contains(element)) { >+ return JFaceResources.getFontRegistry().getBold(JFaceResources.DIALOG_FONT); >+ } >+ return null; > } > } > >@@ -221,9 +247,11 @@ > section.setText(PDEUIMessages.ManifestEditor_DetailExtension_title); > initialize((IPluginModelBase) getPage().getModel()); > createSectionToolbar(section, toolkit); >+ // accelerated tree scrolling enabled >+ fFilteredTree.addMouseWheelListener(new AcceleratedTreeScrolling(fExtensionTree.getTree(), ACCELERATED_SCROLLING)); > // Create the adapted listener for the filter entry field > fFilteredTree.createUIListenerEntryFilter(this); >- Text filterText = fFilteredTree.getFilterControl(); >+ final Text filterText = fFilteredTree.getFilterControl(); > if (filterText != null) { > filterText.addModifyListener(new ModifyListener() { > public void modifyText(ModifyEvent e) { >@@ -252,9 +280,33 @@ > } > } > }); >+ // Add action to filter tree with some of the selection's attributes >+ fFilterRelatedAction = new FilterRelatedExtensionsAction(fExtensionTree, fFilteredTree, this, false); >+ toolBarManager.add(fFilterRelatedAction); >+ // Add action to search all workspace plugins with current filtering applied to the tree viewer >+ fSearchAction = new SearchExtensionsAction(fFilteredTree, PDEUIMessages.ExtensionsPage_searchWithExtensionsFilter); >+ toolBarManager.add(fSearchAction); >+ // Add separator >+ Separator separator = new Separator(); >+ toolBarManager.add(separator); > // Add sort action to the tool bar >- fSortAction = new SortAction(fExtensionTree, PDEUIMessages.ExtensionsPage_sortAlpha, null, null, this); >+ fSortAction = new SortAction(fExtensionTree, PDEUIMessages.ExtensionsPage_sortAlpha, null, null, this) { >+ public void run() { >+ Object[] expanded = fFilteredTree.getViewer().getVisibleExpandedElements(); >+ try { >+ fFilteredTree.setRedraw(false); >+ super.run(); >+ // bugfix: retain tree expand state after sort action >+ fFilteredTree.getViewer().setExpandedElements(expanded); >+ } finally { >+ fFilteredTree.setRedraw(true); >+ } >+ } >+ }; > toolBarManager.add(fSortAction); >+ // Add expand selected leafs action to the toolbar >+ fExpandAction = new ToggleExpandStateAction(fFilteredTree, fExtensionTree); >+ toolBarManager.add(fExpandAction); > // Add collapse action to the tool bar > fCollapseAction = new CollapseAction(fExtensionTree, PDEUIMessages.ExtensionsPage_collapseAll); > toolBarManager.add(fCollapseAction); >@@ -309,11 +361,13 @@ > * @see org.eclipse.pde.internal.ui.editor.PDESection#doGlobalAction(java.lang.String) > */ > public boolean doGlobalAction(String actionId) { >- >+ if (actionId.equals(ActionFactory.FIND.getId()) && fFilterRelatedAction != null) { >+ fFilterRelatedAction.run(); >+ return true; >+ } > if (!isEditable()) { > return false; > } >- > if (actionId.equals(ActionFactory.DELETE.getId())) { > handleDelete(); > return true; >@@ -348,13 +402,14 @@ > > protected void fillContextMenu(IMenuManager manager) { > ISelection selection = fExtensionTree.getSelection(); >- IStructuredSelection ssel = (IStructuredSelection) selection; >+ final IStructuredSelection ssel = (IStructuredSelection) selection; > if (ssel.size() == 1) { > Object object = ssel.getFirstElement(); > if (object instanceof IPluginParent) { > IPluginParent parent = (IPluginParent) object; > if (parent.getModel().getUnderlyingResource() != null) { >- fillContextMenu(getPage(), parent, manager); >+ boolean removeEnabled = !fFilteredTree.isFiltered() || isRemoveEnabled(ssel); >+ fillContextMenu(getPage(), parent, manager, false, removeEnabled); > manager.add(new Separator()); > } > } >@@ -366,6 +421,7 @@ > manager.add(new Separator()); > } > } else if (ssel.size() > 1) { >+ boolean removeEnabled = !fFilteredTree.isFiltered() || isRemoveEnabled(ssel); > // multiple > Action delAction = new Action() { > public void run() { >@@ -375,14 +431,34 @@ > delAction.setText(PDEUIMessages.Actions_delete_label); > manager.add(delAction); > manager.add(new Separator()); >- delAction.setEnabled(isEditable()); >+ delAction.setEnabled(isEditable() && removeEnabled); >+ } >+ if (ssel.size() > 0) { >+ if (ExtensionsFilterUtil.isFilterRelatedEnabled(ssel)) { >+ FilterRelatedExtensionsAction filterRelatedAction = new FilterRelatedExtensionsAction(fExtensionTree, fFilteredTree, this, true); >+ manager.add(filterRelatedAction); >+ SearchExtensionsAction searchRelatedAction = new SearchExtensionsAction(ssel, PDEUIMessages.Actions_search_relatedPluginElements); >+ manager.add(searchRelatedAction); >+ manager.add(new Separator()); >+ } > } > manager.add(new Separator()); >+ >+ if (fFilteredTree.isFiltered()) { >+ // Add action to reveal all extensions when the tree is in filter mode >+ ShowAllExtensionsAction fShowAllAction = new ShowAllExtensionsAction(getPage().getModel(), fFilteredTree, fPatternFilter); >+ if (manager.find(MENU_NEW_ID) != null) { >+ manager.insertAfter(MENU_NEW_ID, fShowAllAction); >+ } else { >+ manager.add(fShowAllAction); >+ manager.add(new Separator()); >+ } >+ } > if (ssel.size() < 2) { // only cut things when the selection is one > getPage().getPDEEditor().getContributor().addClipboardActions(manager); > } > getPage().getPDEEditor().getContributor().contextMenuAboutToShow(manager, false); >- >+ this.fFilteredTree.update(); > } > > static IMenuManager fillContextMenu(PDEFormPage page, final IPluginParent parent, IMenuManager manager) { >@@ -394,7 +470,7 @@ > } > > static IMenuManager fillContextMenu(PDEFormPage page, final IPluginParent parent, IMenuManager manager, boolean addSiblingItems, boolean fullMenu) { >- MenuManager menu = new MenuManager(PDEUIMessages.Menus_new_label); >+ MenuManager menu = new MenuManager(PDEUIMessages.Menus_new_label, MENU_NEW_ID); > IPluginExtension extension = getExtension(parent); > ISchema schema = getSchema(extension); > if (schema == null) { >@@ -493,6 +569,45 @@ > } > } > >+ public FormFilteredTree getFormFilteredTree() { >+ return fFilteredTree; >+ } >+ >+ /** >+ * Adds another value to filter text and a preceding separator character if necessary. >+ * Empty values as well as <code>true</code> and <code>false</code> are omitted. >+ * >+ * @param attributeValue Value to be trimmed and added to the filter text >+ * @param clearFilterText When <code>true</code> the filter text is replaced with the attribute value, appended otherwise. >+ */ >+ public void addAttributeToFilter(String attributeValue, boolean clearFilterText) { >+ Text filterControl = fFilteredTree.getFilterControl(); >+ if (filterControl != null && attributeValue != null) { >+ String trimmedValue = attributeValue.trim(); >+ if (trimmedValue.length() > 0 && ExtensionsFilterUtil.isNotBoolean(trimmedValue)) { >+ if (trimmedValue.startsWith("%")) {//$NON-NLS-1$ >+ IPluginModelBase model = getPluginModelBase(); >+ trimmedValue = ((model != null) ? model.getResourceString(trimmedValue) : trimmedValue).replaceAll("\"", ""); //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ String filterPattern; >+ if (clearFilterText) { >+ filterPattern = trimmedValue; >+ } else { >+ filterPattern = filterControl.getText(); >+ if (filterPattern.length() > 0 && !filterPattern.endsWith("/")) { //$NON-NLS-1$ >+ filterPattern += "/"; //$NON-NLS-1$ >+ } >+ filterPattern += trimmedValue; >+ } >+ if (filterPattern.indexOf('/') != -1) { // quote when >+ filterPattern = "\"" + filterPattern + "\""; //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ setBypassFilterDelay(true); // force immediate job run >+ filterControl.setText(filterPattern); >+ } >+ } >+ } >+ > private void handleNew() { > final IProject project = getPage().getPDEEditor().getCommonProject(); > BusyIndicator.showWhile(fExtensionTree.getTree().getDisplay(), new Runnable() { >@@ -555,6 +670,7 @@ > > private void handleSelectAll() { > fExtensionTree.getTree().selectAll(); >+ updateButtons(fFilteredTree.getViewer().getSelection()); > } > > private ArrayList getEditorWizards(IStructuredSelection selection) { >@@ -643,13 +759,62 @@ > // The model changed but the editor is still open, we should try to retain expansion, selection will be retained on its own > Object[] expanded = fExtensionTree.getExpandedElements(); > IPluginModelBase model = (IPluginModelBase) getPage().getModel(); >- fExtensionTree.getControl().setRedraw(false); >- fExtensionTree.setInput(model.getPluginBase()); >- fExtensionTree.setExpandedElements(expanded); >- fExtensionTree.getControl().setRedraw(true); >- reportMissingExtensionPointSchemas(model.getPluginBase()); >- getManagedForm().fireSelectionChanged(ExtensionsSection.this, fExtensionTree.getSelection()); >- super.refresh(); >+ int[] indexPath = getTreeIndexPath(fExtensionTree.getTree()); >+ try { >+ fExtensionTree.getControl().setRedraw(false); >+ fExtensionTree.setInput(model.getPluginBase()); >+ fExtensionTree.setExpandedElements(expanded); >+ >+ reportMissingExtensionPointSchemas(model.getPluginBase()); >+ getManagedForm().fireSelectionChanged(ExtensionsSection.this, fExtensionTree.getSelection()); >+ super.refresh(); >+ >+ if (indexPath != null) { >+ // fix for Bug 371066 >+ revealTopItem(fExtensionTree.getTree(), indexPath); >+ } >+ } finally { >+ fExtensionTree.getControl().setRedraw(true); >+ } >+ } >+ >+ private static int[] getTreeIndexPath(Tree tree) { >+ int[] indexPath = null; >+ if (tree != null) { >+ TreeItem item = tree.getTopItem(); >+ int count = 1; >+ while (item != null && (item = item.getParentItem()) != null) { >+ count++; >+ } >+ indexPath = new int[count]; >+ int index = 0; >+ item = tree.getTopItem(); >+ while (item != null && index < count) { >+ TreeItem parent = item.getParentItem(); >+ if (parent != null) { >+ indexPath[index++] = parent.indexOf(item); >+ } else { >+ indexPath[index++] = tree.indexOf(item); >+ } >+ item = parent; >+ } >+ } >+ return indexPath; >+ } >+ >+ private static void revealTopItem(Tree tree, int[] indexPath) { >+ TreeItem itemFound = null; >+ for (int i = indexPath.length - 1; i >= 0; i--) { >+ int index = indexPath[i]; >+ if (itemFound != null) { >+ itemFound = (itemFound.getItemCount() > index) ? itemFound.getItem(indexPath[i]) : null; >+ } else if (i == indexPath.length - 1) { >+ itemFound = (tree.getItemCount() > index) ? tree.getItem(indexPath[i]) : null; >+ } >+ } >+ if (itemFound != null) { >+ tree.setTopItem(itemFound); >+ } > } > > public void modelChanged(IModelChangedEvent event) { >@@ -669,6 +834,18 @@ > IPluginObject pobj = (IPluginObject) changeObject; > IPluginObject parent = changeObject instanceof IPluginExtension ? ((IPluginModelBase) getPage().getModel()).getPluginBase() : pobj.getParent(); > if (event.getChangeType() == IModelChangedEvent.INSERT) { >+ // enables adding extensions while tree is filtered >+ if (fFilteredTree.isFiltered()) { >+ Object[] inserted = event.getChangedObjects(); >+ for (int i = 0; i < inserted.length; i++) { >+ fPatternFilter.addElement(inserted[i]); >+ } >+ if (inserted.length == 1) { >+ fFilteredTree.getViewer().setSelection(new StructuredSelection(inserted[0])); >+ } >+ } >+ >+ // > fExtensionTree.refresh(parent); > if (changeObject instanceof IPluginExtension) { > IPluginExtension ext = (IPluginExtension) changeObject; >@@ -809,9 +986,9 @@ > if (labelAtt == null) { > // try some hard-coded attributes that > // are used frequently >- for (int i = 0; i < COMMON_LABEL_PROPERTIES.length; i++) { >- labelAtt = element.getAttribute(COMMON_LABEL_PROPERTIES[i]); >- if (labelAtt != null) >+ for (int i = 0; i < COMMON_LABEL_ATTRIBUTES.length; i++) { >+ labelAtt = element.getAttribute(COMMON_LABEL_ATTRIBUTES[i]); >+ if (labelAtt != null && labelAtt.getValue().length() > 0) > break; > } > if (labelAtt == null) { >@@ -821,9 +998,14 @@ > labelAtt = element.getAttributes()[0]; > } > } >- if (labelAtt != null && labelAtt.getValue() != null) >+ if (labelAtt != null && labelAtt.getValue() != null) { > fullName = stripShortcuts(labelAtt.getValue()); >+ if (labelAtt.getName().equals(COMMON_LABEL_ATTRIBUTES[3])) { // remove package from handler class >+ fullName = fullName.substring(fullName.lastIndexOf('.') + 1, fullName.length()); >+ } >+ } > fullName = element.getResourceString(fullName); >+ > if (fullNames) > return fullName != null ? fullName : baseName; > if (fullName == null) >@@ -836,12 +1018,25 @@ > return fullName + " \u200f(\u200e" + baseName + ")"; //$NON-NLS-1$ //$NON-NLS-2$ > return fullName + " (" + baseName + ')'; //$NON-NLS-1$ > } >- return obj.toString(); >+ if (obj != null) { >+ return obj.toString(); >+ } >+ return new String(); > } > > public void setFocus() { > if (fExtensionTree != null) > fExtensionTree.getTree().setFocus(); >+ } >+ >+ /** >+ * Temporarily bypasses default {@link FormFilteredTree#getRefreshJobDelay()} for several actions to immediatly start tree >+ * filtering. Only the next job to call <code>getRefreshJobDelay()</code> will be affected and reset this value. >+ * >+ * @param bypassFilterDelay <code>true</code> bypasses the refresh job delay by overriding it with <code>0</code> >+ */ >+ public void setBypassFilterDelay(boolean bypassFilterDelay) { >+ fBypassFilterDelay = bypassFilterDelay; > } > > public static String stripShortcuts(String input) { >@@ -855,6 +1050,24 @@ > output.append(c); > } > return output.toString(); >+ } >+ >+ public boolean canCopy(ISelection selection) { >+ // Partial fix for Bug 360079, enables Ctrl+C in filter text if plugin model is editable >+ if (fFilteredTree.getFilterControl().isFocusControl() && !selection.isEmpty()) { >+ return true; >+ } >+ // TODO enable copy also when plugin model is not editable >+ return super.canCopy(selection); >+ } >+ >+ public boolean canPaste(Clipboard clipboard) { >+ // Partial fix for Bug 360079, enables Ctrl+V in filter text if plugin model is editable >+ if (fFilteredTree.getFilterControl().isFocusControl()) { >+ return true; >+ } >+ // TODO enable paste also when plugin model is not editable >+ return super.canPaste(clipboard); > } > > /* (non-Javadoc) >@@ -1081,6 +1294,18 @@ > } > > private void updateButtons(Object item) { >+ if (fExpandAction != null) { >+ fExpandAction.setEnabled(ToggleExpandStateAction.isExpandable((IStructuredSelection) fExtensionTree.getSelection())); >+ } >+ if (fFilterRelatedAction != null) { >+ fFilterRelatedAction.setEnabled(ExtensionsFilterUtil.isFilterRelatedEnabled((IStructuredSelection) fExtensionTree.getSelection())); >+ } >+ if (fSearchAction != null) { >+ Text filterControl = fFilteredTree.getFilterControl(); >+ boolean searchEnabled = filterControl != null && filterControl.getText().length() > 0; >+ fSearchAction.setEnabled(searchEnabled); >+ } >+ > if (getPage().getModel().isEditable() == false) > return; > boolean sorted = fSortAction != null && fSortAction.isChecked(); >@@ -1089,44 +1314,42 @@ > getTreePart().setButtonEnabled(BUTTON_MOVE_DOWN, false); > return; > } >+ IStructuredSelection selection = (item instanceof IStructuredSelection) ? (IStructuredSelection) item : null; > > boolean filtered = fFilteredTree.isFiltered(); > boolean addEnabled = true; >- boolean removeEnabled = false; >+ boolean removeEnabled = true; > boolean upEnabled = false; > boolean downEnabled = false; > >- if (item != null) { >- removeEnabled = true; >- } > if (filtered) { > // Fix for bug 194529 and bug 194828 >- addEnabled = false; >+ // Update: adding during filtering enabled by additional filter capability >+ addEnabled = true; > upEnabled = false; > downEnabled = false; >+ removeEnabled = isRemoveEnabled(selection); > } else { >- if (item instanceof IStructuredSelection) { >- if (((IStructuredSelection) item).size() == 1) { >- Object selected = ((IStructuredSelection) item).getFirstElement(); >- if (selected instanceof IPluginElement) { >- IPluginElement element = (IPluginElement) selected; >- IPluginParent parent = (IPluginParent) element.getParent(); >- // check up >- int index = parent.getIndexOf(element); >- if (index > 0) >- upEnabled = true; >- if (index < parent.getChildCount() - 1) >- downEnabled = true; >- } else if (selected instanceof IPluginExtension) { >- IPluginExtension extension = (IPluginExtension) selected; >- IExtensions extensions = (IExtensions) extension.getParent(); >- int index = extensions.getIndexOf(extension); >- int size = extensions.getExtensions().length; >- if (index > 0) >- upEnabled = true; >- if (index < size - 1) >- downEnabled = true; >- } >+ if (selection != null && selection.size() == 1) { >+ Object selected = selection.getFirstElement(); >+ if (selected instanceof IPluginElement) { >+ IPluginElement element = (IPluginElement) selected; >+ IPluginParent parent = (IPluginParent) element.getParent(); >+ // check up >+ int index = parent.getIndexOf(element); >+ if (index > 0) >+ upEnabled = true; >+ if (index < parent.getChildCount() - 1) >+ downEnabled = true; >+ } else if (selected instanceof IPluginExtension) { >+ IPluginExtension extension = (IPluginExtension) selected; >+ IExtensions extensions = (IExtensions) extension.getParent(); >+ int index = extensions.getIndexOf(extension); >+ int size = extensions.getExtensions().length; >+ if (index > 0) >+ upEnabled = true; >+ if (index < size - 1) >+ downEnabled = true; > } > } > } >@@ -1136,11 +1359,95 @@ > getTreePart().setButtonEnabled(BUTTON_MOVE_DOWN, downEnabled); > } > >+ /** >+ * Since filtering potentially hides children of extensions, removing them when they still have children is intransparent. >+ * Needs to be called only when the tree is filtered. >+ * >+ * @param selection selection to be tested >+ * @return whether removing the selected elements is enabled >+ */ >+ boolean isRemoveEnabled(IStructuredSelection selection) { >+ if (selection != null) { >+ for (Iterator iterator = selection.iterator(); iterator.hasNext();) { >+ Object element = iterator.next(); >+ if (element instanceof PluginExtensionNode) { >+ return ((PluginExtensionNode) element).getChildCount() == 0; >+ } >+ } >+ } >+ return true; >+ } >+ > /* (non-Javadoc) > * @see org.eclipse.pde.internal.ui.editor.TreeSection#createTreeViewer(org.eclipse.swt.widgets.Composite, int) > */ > protected TreeViewer createTreeViewer(Composite parent, int style) { >- fFilteredTree = new FormFilteredTree(parent, style, new PatternFilter()); >+ fPatternFilter = new ExtensionsPatternFilter(); >+ fFilteredTree = new FormFilteredTree(parent, style, fPatternFilter) { >+ protected WorkbenchJob doCreateRefreshJob() { >+ final WorkbenchJob job = super.doCreateRefreshJob(); >+ job.addJobChangeListener(new JobChangeAdapter() { >+ private ISelection selection; >+ private boolean aboutToRunPassed = false; >+ >+ public void scheduled(IJobChangeEvent event) { >+ ((ExtensionsPatternFilter) fFilteredTree.getPatternFilter()).clearMatchingLeafs(); >+ selection = fExtensionTree.getSelection(); >+ } >+ >+ public void aboutToRun(IJobChangeEvent event) { >+ aboutToRunPassed = true; >+ } >+ >+ /* >+ * Restores selection after tree refresh and expands tree up to matching leafs only >+ */ >+ public void done(IJobChangeEvent event) { >+ if (aboutToRunPassed) { // restoring is only required if the job actually ran >+ try { >+ fFilteredTree.setRedraw(false); >+ ExtensionsPatternFilter extensionsPatternFilter = ((ExtensionsPatternFilter) fFilteredTree.getPatternFilter()); >+ fExtensionTree.collapseAll(); >+ Object[] leafs = extensionsPatternFilter.getMatchingLeafsAsArray(); >+ for (int i = 0; i < leafs.length; i++) { >+ fExtensionTree.expandToLevel(leafs[i], 0); >+ } >+ if (selection != null && !(selection.isEmpty())) { >+ fExtensionTree.setSelection(selection, true); >+ } >+ } finally { >+ fFilteredTree.setRedraw(true); >+ } >+ } >+ } >+ }); >+ return job; >+ } >+ >+ protected long getRefreshJobDelay() { >+ // Prolonged job delay time is required because of the attribute search being more costly in nature. >+ // This can block input to the filter text severly. Thus it shouldn't happen when typing slowly. >+ // The delay of 1500ms is bypassed by some actions that use the filter text to initiate searches or clear the text. >+ long delay = (fBypassFilterDelay) ? 0 : REFRESHJOB_DELAY_TIME; >+ setBypassFilterDelay(false); // reset afterwards >+ return delay; >+ } >+ >+ protected void clearText() { >+ // bugfix: additional notification with textChanged() would cause a needless 2nd refresh job run >+ // which in turn would have a longer delay time than the 1st run. >+ setFilterText(""); //$NON-NLS-1$ >+ } >+ >+ protected void textChanged() { >+ String filterText = getFilterString(); >+ if (filterText != null && filterText.length() == 0) { >+ // clearing the filter text doesn't require a refresh job delay >+ setBypassFilterDelay(true); >+ } >+ super.textChanged(); >+ } >+ }; > parent.setData("filtered", Boolean.TRUE); //$NON-NLS-1$ > return fFilteredTree.getViewer(); > } >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/rows/ExtensionAttributeRow.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/rows/ExtensionAttributeRow.java >index 72e8557..e2fdbd0 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/rows/ExtensionAttributeRow.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/rows/ExtensionAttributeRow.java >@@ -21,9 +21,12 @@ > import org.eclipse.pde.internal.ui.editor.text.PDETextHover; > import org.eclipse.swt.SWT; > import org.eclipse.swt.widgets.*; >+import org.eclipse.ui.PlatformUI; >+import org.eclipse.ui.actions.ActionFactory; > import org.eclipse.ui.forms.IFormColors; > import org.eclipse.ui.forms.widgets.FormToolkit; > import org.eclipse.ui.forms.widgets.Hyperlink; >+import org.eclipse.ui.keys.IBindingService; > > public abstract class ExtensionAttributeRow implements IControlHoverContentProvider { > protected IContextPart part; >@@ -100,17 +103,28 @@ > } > > public String getHoverContent(Control c) { >- if (c instanceof Label || c instanceof Hyperlink) >- return getDescription(); >+ if (c instanceof Label || c instanceof Hyperlink) { >+ // reveal keybinding for shortcut to filtering >+ String filterBinding = ((IBindingService) PlatformUI.getWorkbench().getAdapter(IBindingService.class)).getBestActiveBindingFormattedFor(ActionFactory.FIND.getCommandId()); >+ String findKeybinding = (getValue().length() > 0) ? "<br><br>Press " + filterBinding + " within text to filter for this attribute." : ""; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ String description = getDescription().trim(); // prettify help text >+ if (description.length() > 0) { >+ String first = String.valueOf(description.charAt(0)); // always make first letter uppercase >+ return getDescription().replaceFirst(first, first.toUpperCase()) + findKeybinding; >+ } >+ return description; >+ } > if (c instanceof Text) { > String text = ((Text) c).getText(); > ISchemaAttribute sAtt = getAttribute(); > String translated = null; >- if (input != null && sAtt != null && sAtt.isTranslatable() && text.startsWith("%")) //$NON-NLS-1$ >+ if (input != null && sAtt != null && sAtt.isTranslatable() && text.startsWith("%")) { //$NON-NLS-1$ > translated = input.getResourceString(text); >- if (!text.equals(translated)) >+ } >+ if (!text.equals(translated)) { > return translated; > } >+ } > return null; > } > >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties >index 36ab48f..2e77bce 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties >@@ -1189,6 +1189,8 @@ > EditorActions_revert = Re&vert > Actions_open_label = &Open > Actions_delete_label = &Delete >+Actions_filter_relatedPluginElements=&Filter Related >+Actions_search_relatedPluginElements=Search &Related > Actions_synchronizeVersions_label = S&ynchronize Versions... > > Menus_new_label = &New >@@ -1275,6 +1277,8 @@ > SearchAction_Declaration = Find Declaratio&n > ShowDescriptionAction_label = Sho&w Description > ShowDescriptionAction_title=Extension Point Description >+ShowAllExtensionsAction_label=Show all extensions >+HideUnfilteredExtensionsAction_label=Hide unfiltered extensions > DefaultJUnitWorkspaceBlock_name=JUnit workspace location > DefinitionPage_0=Definition > DefinitionPage_1=Target Definition >@@ -1536,6 +1540,8 @@ > ExtensionsPage_title=Extensions > ExtensionsPage_tabName=Extensions > ExtensionsPage_sortAlpha=Sort the Extensions alphabetically >+ExtensionsPage_toggleExpandState=Toggle Expand State >+ExtensionsPage_searchWithExtensionsFilter=Extension Element Search > ExtensionDetails_title=Extension Details > ExtensionDetails_desc=Set the properties of the selected extension. Required fields are denoted by "*". > ExtensionDetails_id=ID >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/AttributesMatch.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/AttributesMatch.java >new file mode 100644 >index 0000000..abf9831 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/AttributesMatch.java >@@ -0,0 +1,45 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.search; >+ >+import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor; >+import org.eclipse.search.ui.text.Match; >+ >+/** >+ * An extension to {@link Match} in order to present matching plugins which resulted >+ * in a search queried from the extensions page of the {@link ManifestEditor} >+ * >+ * @author Sascha Becher >+ */ >+public class AttributesMatch extends Match { >+ >+ /** >+ * A constant expressing that the {@link Match} resulted in a search queried from >+ * the extensions page of the {@link ManifestEditor} >+ */ >+ public static final int UNIT_ATTRIBUTE_SEARCH_PATTERN = 3; >+ >+ protected String searchPattern; >+ >+ public AttributesMatch(Object element, String searchPattern) { >+ super(element, UNIT_LINE, 0, 0); >+ this.searchPattern = searchPattern; >+ } >+ >+ public String getSearchPattern() { >+ return searchPattern; >+ } >+ >+ public int getBaseUnit() { >+ return UNIT_ATTRIBUTE_SEARCH_PATTERN; >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionElementSearchOperation.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionElementSearchOperation.java >new file mode 100644 >index 0000000..6100824 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionElementSearchOperation.java >@@ -0,0 +1,106 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.search; >+ >+import java.util.ArrayList; >+import org.eclipse.core.runtime.IProgressMonitor; >+import org.eclipse.pde.core.plugin.*; >+import org.eclipse.pde.internal.core.plugin.PluginExtension; >+import org.eclipse.pde.internal.core.plugin.PluginParent; >+import org.eclipse.pde.internal.core.search.ISearchResultCollector; >+import org.eclipse.pde.internal.core.search.PluginSearchInput; >+ >+/** >+ * Search operation for finding extension elements within a plugin using the {@link ExtensionsPatternFilter}. >+ * >+ * @author Sascha Becher >+ */ >+public class ExtensionElementSearchOperation { >+ >+ protected PluginSearchInput fInput; >+ private ISearchResultCollector fCollector; >+ >+ public ExtensionElementSearchOperation(PluginSearchInput input, ISearchResultCollector collector) { >+ this.fInput = input; >+ this.fCollector = collector; >+ } >+ >+ public void execute(IProgressMonitor monitor) { >+ IPluginModelBase[] entries = fInput.getSearchScope().getMatchingModels(); >+ monitor.beginTask("", entries.length); //$NON-NLS-1$ >+ >+ try { >+ for (int i = 0; i < entries.length; i++) { >+ IPluginModelBase candidate = entries[i]; >+ visit(candidate); >+ monitor.worked(1); >+ } >+ } finally { >+ monitor.done(); >+ } >+ } >+ >+ private void visit(IPluginModelBase model) { >+ ArrayList matches = findMatch(model); >+ for (int i = 0; i < matches.size(); i++) { >+ fCollector.accept(matches.get(i)); >+ } >+ } >+ >+ private ArrayList findMatch(IPluginModelBase model) { >+ ArrayList result = new ArrayList(); >+ int searchLimit = fInput.getSearchLimit(); >+ if (fInput.getSearchElement() == PluginSearchInput.ELEMENT_PLUGIN) { >+ if (searchLimit != PluginSearchInput.LIMIT_REFERENCES) >+ findPluginDeclaration(model, result); >+ if (searchLimit != PluginSearchInput.LIMIT_DECLARATIONS) >+ findPluginReferences(model, result); >+ } >+ return result; >+ } >+ >+ private void findPluginDeclaration(IPluginModelBase model, ArrayList result) { >+ IPluginBase pluginBase = model.getPluginBase(); >+ ExtensionsPatternFilter filter = new ExtensionsPatternFilter(); >+ filter.setPattern(fInput.getSearchString()); >+ IPluginExtension[] extensions = pluginBase.getExtensions(); >+ for (int i = 0; i < extensions.length; i++) { >+ PluginExtension pluginExtension = (PluginExtension) extensions[i]; >+ boolean foundAny = traversePluginElements(pluginExtension, filter); >+ if (foundAny && pluginBase instanceof IPlugin) { >+ result.add(pluginBase); >+ return; >+ } >+ } >+ } >+ >+ private boolean traversePluginElements(PluginParent pluginParent, ExtensionsPatternFilter filter) { >+ IPluginObject[] pluginObjects = pluginParent.getChildren(); >+ if (pluginObjects != null) { >+ for (int i = 0; i < pluginObjects.length; i++) { >+ boolean foundAny = traversePluginElements((PluginParent) pluginObjects[i], filter); >+ if (foundAny) { >+ return true; >+ } >+ } >+ } >+ return filter.isLeafMatch(null, pluginParent); >+ } >+ >+ private void findPluginReferences(IPluginModelBase model, ArrayList result) { >+ IPluginBase pluginBase = model.getPluginBase(); >+ IPluginImport[] imports = pluginBase.getImports(); >+ for (int i = 0; i < imports.length; i++) { >+ findPluginDeclaration(imports[i].getPluginModel(), result); >+ } >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionsPatternFilter.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionsPatternFilter.java >new file mode 100644 >index 0000000..dd46b14 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionsPatternFilter.java >@@ -0,0 +1,309 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.search; >+ >+import java.util.*; >+import java.util.regex.Matcher; >+import java.util.regex.Pattern; >+import org.eclipse.jface.viewers.Viewer; >+import org.eclipse.pde.core.plugin.*; >+import org.eclipse.pde.internal.core.bundle.BundlePlugin; >+import org.eclipse.pde.internal.ui.util.ExtensionsFilterUtil; >+import org.eclipse.ui.dialogs.PatternFilter; >+ >+/** >+ * An extended filtering capability for the filtered tree of ExtensionsPage. The >+ * search criteria is splitted by / first. The resulting values are used to >+ * perform a search on all node's values. All elements fitting at least one of >+ * the split values will be displayed. This extensions does not compromise the >+ * default filtering behaviour of the tree while providing the ability to >+ * highlight related items such as commands along with their command images, >+ * handlers, menu entries and activities. >+ * >+ * @see org.eclipse.ui.dialogs.FilteredTree >+ * @since 3.8 >+ * >+ */ >+public class ExtensionsPatternFilter extends PatternFilter { >+ >+ /** >+ * Limits the maximum number of attributes handled by the filter >+ */ >+ public static final int ATTRIBUTE_LIMIT = 30; >+ >+ protected String fSearchPattern; >+ >+ protected Set fAttributes = new HashSet(); >+ protected final Set fMatchingLeafs = new HashSet(); >+ protected final Set fFoundAnyElementsCache = new HashSet(); >+ >+ /** >+ * Check if the leaf element is a match with the filter text. The >+ * default behavior checks that the label of the element is a match. >+ * >+ * Subclasses should override this method. >+ * >+ * @param viewer >+ * the viewer that contains the element >+ * @param element >+ * the tree element to check >+ * @return true if the given element's label matches the filter text >+ */ >+ protected boolean isLeafMatch(Viewer viewer, Object element) { >+ // match label; default behaviour >+ if (viewer != null && super.isLeafMatch(viewer, element)) { >+ return true; >+ } >+ >+ // match all splitted attribute's values of IPluginElement against splitted filter patterns >+ if (element instanceof IPluginElement) { >+ return doIsLeafMatch((IPluginElement) element); >+ } >+ return false; >+ } >+ >+ protected boolean doIsLeafMatch(IPluginElement pluginElement) { >+ List syntheticAttributes = ExtensionsFilterUtil.handlePropertyTester(pluginElement); >+ if (fAttributes != null && fAttributes.size() > 0) { >+ int attributeNumber = 0; >+ for (Iterator iterator = fAttributes.iterator(); iterator.hasNext();) { >+ String valuePattern = (String) iterator.next(); >+ if (attributeNumber < fAttributes.size() && attributeNumber < ATTRIBUTE_LIMIT) { >+ boolean quoted = isQuoted(valuePattern); >+ if (valuePattern != null && valuePattern.length() > 0) { >+ int attributeCount = pluginElement.getAttributeCount(); >+ IPluginAttribute[] pluginAttributes = pluginElement.getAttributes(); >+ >+ for (int i = 0; i < attributeCount; i++) { >+ IPluginAttribute attributeElement = pluginAttributes[i]; >+ if (attributeElement != null && attributeElement.getValue() != null) { >+ String[] attributes = getAttributeSplit(attributeElement.getValue(), quoted); >+ if (attributes != null) { >+ List attributeList = new ArrayList(Arrays.asList(attributes)); >+ attributeList.addAll(syntheticAttributes); >+ if (matchWithAttributes(pluginElement, valuePattern, attributeList, quoted)) { >+ return true; >+ } >+ } >+ } >+ } >+ if (valuePattern.equalsIgnoreCase(pluginElement.getName())) { >+ return true; >+ } >+ } >+ } >+ attributeNumber++; >+ } >+ } >+ return false; >+ } >+ >+ private boolean matchWithAttributes(IPluginElement pluginElement, String valuePattern, List attributeList, boolean quoted) { >+ for (int k = 0; k < attributeList.size(); k++) { >+ String attribute = (String) attributeList.get(k); >+ if (attribute != null && attribute.length() > 0) { >+ if (!attribute.startsWith("%")) { //$NON-NLS-1$ >+ int delimiterPosition = attribute.indexOf('?'); // strip right of '?' >+ if (delimiterPosition != -1) { >+ attribute = attribute.substring(0, delimiterPosition); >+ } >+ } else { >+ String resourceValue = pluginElement.getResourceString(attribute); >+ attribute = (resourceValue != null && resourceValue.length() > 0) ? resourceValue : attribute; >+ } >+ String pattern = valuePattern.toLowerCase(); >+ if (quoted) { >+ pattern = pattern.substring(1, pattern.length() - 1); >+ } >+ if (attribute.toLowerCase().equals(pattern)) { >+ return true; >+ } >+ } >+ } >+ return false; >+ } >+ >+ private static boolean isQuoted(String value) { >+ return value.startsWith("\"") && value.endsWith("\""); //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ >+ private static String[] getAttributeSplit(String text, boolean quoted) { >+ if (text.length() < 2) { >+ return null; >+ } >+ if (!quoted) { >+ return text.replaceAll("/{1,}", "/").split("/"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ } >+ return new String[] {text}; >+ } >+ >+ public boolean isElementVisible(Viewer viewer, Object element) { >+ if (fFoundAnyElementsCache.contains(element)) { >+ return true; >+ } >+ return isLeafMatch(viewer, element); >+ } >+ >+ public Object[] filter(Viewer viewer, Object parent, Object[] elements) { >+ if (parent != null && parent instanceof BundlePlugin) { >+ if (fFoundAnyElementsCache.size() == 0 && fSearchPattern != null && fSearchPattern.length() > 0) { >+ BundlePlugin pluginPlugin = (BundlePlugin) parent; >+ doFilter(viewer, pluginPlugin, pluginPlugin.getExtensions(), false); >+ } >+ } >+ if (fFoundAnyElementsCache.size() > 0) { >+ List found = new ArrayList(); >+ for (int i = 0; i < elements.length; i++) { >+ if (fFoundAnyElementsCache.contains(elements[i])) { >+ found.add(elements[i]); >+ } >+ } >+ return found.toArray(); >+ } >+ return super.filter(viewer, parent, elements); >+ } >+ >+ protected boolean doFilter(Viewer viewer, Object parent, IPluginObject[] children, boolean addChildren) { >+ boolean isParentMatch = fFoundAnyElementsCache.contains(parent) ? true : false; >+ >+ // find leaf matches >+ boolean isAnyLeafMatch = false; >+ for (int j = 0; j < children.length; j++) { >+ IPluginObject iPluginObject = children[j]; >+ boolean isChildMatch = true; >+ if (!isParentMatch || children.length > 0) { >+ isChildMatch = this.isLeafMatch(viewer, iPluginObject); >+ isAnyLeafMatch |= isChildMatch; >+ if (isChildMatch) { >+ fMatchingLeafs.add(iPluginObject); >+ } >+ } >+ if (isChildMatch || addChildren) { >+ fFoundAnyElementsCache.add(iPluginObject); >+ } >+ } >+ >+ // traverse children when available >+ boolean isAnyChildMatch = false; >+ for (int i = 0; i < children.length; i++) { >+ IPluginObject iPluginObject = children[i]; >+ if (iPluginObject instanceof IPluginParent) { >+ IPluginParent pluginElement = (IPluginParent) iPluginObject; >+ if (pluginElement.getChildren().length > 0) { >+ boolean isChildrenMatch = doFilter(viewer, pluginElement, pluginElement.getChildren(), addChildren | fMatchingLeafs.contains(pluginElement)); >+ isAnyChildMatch |= isChildrenMatch; >+ if (isChildrenMatch) { >+ fFoundAnyElementsCache.add(pluginElement); >+ } >+ } >+ } >+ } >+ return isAnyChildMatch | isAnyLeafMatch; >+ } >+ >+ /** >+ * Splits a string at the occurrences of <code>/</code>. Any quoted parts of the <code>filterText</code> >+ * are not to be splitted but remain as a whole along with the quotation. >+ * >+ * @param filterText text to split >+ * @return split array >+ */ >+ protected String[] splitWithQuoting(String filterText) { >+ // remove multiple separators >+ String text = filterText.replaceAll("/{1,}", "/"); //$NON-NLS-1$//$NON-NLS-2$ >+ boolean containsQuoting = text.indexOf('\"') != -1; >+ if (containsQuoting) { >+ // remove multiple quotes >+ text = text.replaceAll("\"{1,}", "\""); //$NON-NLS-1$//$NON-NLS-2$ >+ // treat quoted text as a whole, thus enables searching for file paths >+ if (text.replaceAll("[^\"]", "").length() % 2 == 0) { //$NON-NLS-1$//$NON-NLS-2$ >+ List patterns = new ArrayList(); >+ List matchList = new ArrayList(); >+ Pattern regex = Pattern.compile("[^\\s\"']+|\"[^\"]*\"|'[^']*'"); //$NON-NLS-1$ >+ Matcher regexMatcher = regex.matcher(text); >+ while (regexMatcher.find()) { >+ matchList.add(regexMatcher.group()); >+ } >+ for (int i = 0; i < matchList.size(); i++) { >+ String element = (String) matchList.get(i); >+ if (isQuoted(element)) { >+ patterns.add(element); >+ } else { >+ String[] elements = element.split("/"); //$NON-NLS-1$ >+ for (int k = 0; k < elements.length; k++) { >+ String splitted = elements[k]; >+ if (splitted.length() > 0) { >+ patterns.add(splitted); >+ } >+ } >+ } >+ } >+ return (String[]) patterns.toArray(new String[0]); >+ } // filter text must have erroneous quoting, replacing all >+ text = text.replaceAll("[\"]", ""); //$NON-NLS-1$ //$NON-NLS-2$ >+ } >+ return text.split("/"); //$NON-NLS-1$ >+ } >+ >+ /** >+ * Enables the filter to temporarily display arbitrary elements >+ * >+ * @param element >+ */ >+ public boolean addElement(Object element) { >+ return fFoundAnyElementsCache.add(element); >+ } >+ >+ /** >+ * Removes elements from the filter >+ * >+ * @param element >+ */ >+ public boolean removeElement(Object element) { >+ return fFoundAnyElementsCache.remove(element); >+ } >+ >+ /* >+ * The pattern string for which this filter should select >+ * elements in the viewer. >+ * >+ * @see org.eclipse.ui.dialogs.PatternFilter#setPattern(java.lang.String) >+ */ >+ public final void setPattern(String patternString) { >+ super.setPattern(patternString); >+ fSearchPattern = patternString; >+ String[] patterns = (patternString != null) ? splitWithQuoting(patternString) : new String[] {}; >+ fAttributes.clear(); >+ fAttributes.addAll(Arrays.asList(patterns)); >+ fFoundAnyElementsCache.clear(); >+ } >+ >+ public String getPattern() { >+ return fSearchPattern; >+ } >+ >+ public void clearMatchingLeafs() { >+ fMatchingLeafs.clear(); >+ } >+ >+ public Object[] getMatchingLeafsAsArray() { >+ return fMatchingLeafs.toArray(); >+ } >+ >+ public Set getMatchingLeafs() { >+ return fMatchingLeafs; >+ } >+ >+ public boolean containsElement(Object element) { >+ return fFoundAnyElementsCache.contains(element); >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/FindExtensionsByAttributeQuery.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/FindExtensionsByAttributeQuery.java >new file mode 100644 >index 0000000..610253f >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/FindExtensionsByAttributeQuery.java >@@ -0,0 +1,88 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.search; >+ >+import org.eclipse.core.runtime.*; >+import org.eclipse.pde.core.plugin.IPlugin; >+import org.eclipse.pde.internal.core.search.ISearchResultCollector; >+import org.eclipse.pde.internal.core.search.PluginSearchInput; >+import org.eclipse.search.ui.ISearchQuery; >+import org.eclipse.search.ui.ISearchResult; >+import org.eclipse.search.ui.text.AbstractTextSearchResult; >+ >+/** >+ * @author Sascha Becher >+ */ >+public class FindExtensionsByAttributeQuery implements ISearchQuery { >+ >+ private SearchResult fSearchResult; >+ >+ private PluginSearchInput fSearchInput; >+ >+ public FindExtensionsByAttributeQuery(PluginSearchInput input) { >+ fSearchInput = input; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.search.ui.ISearchQuery#run(org.eclipse.core.runtime.IProgressMonitor) >+ */ >+ public IStatus run(IProgressMonitor monitor) throws OperationCanceledException { >+ final AbstractTextSearchResult result = (AbstractTextSearchResult) getSearchResult(); >+ result.removeAll(); >+ ISearchResultCollector collector = new ISearchResultCollector() { >+ public void accept(Object match) { >+ if (match instanceof IPlugin) { >+ IPlugin plugin = (IPlugin) match; >+ result.addMatch(new AttributesMatch(plugin, fSearchInput.getSearchString())); >+ } >+ } >+ }; >+ ExtensionElementSearchOperation op = new ExtensionElementSearchOperation(fSearchInput, collector); >+ op.execute(monitor); >+ monitor.done(); >+ return Status.OK_STATUS; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.search.ui.ISearchQuery#getLabel() >+ */ >+ public String getLabel() { >+ return fSearchInput.getSearchString(); >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.search.ui.ISearchQuery#canRerun() >+ */ >+ public boolean canRerun() { >+ return true; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.search.ui.ISearchQuery#canRunInBackground() >+ */ >+ public boolean canRunInBackground() { >+ return true; >+ } >+ >+ /* (non-Javadoc) >+ * @see org.eclipse.search.ui.ISearchQuery#getSearchResult() >+ */ >+ public ISearchResult getSearchResult() { >+ if (fSearchResult == null) >+ fSearchResult = new SearchResult(this); >+ return fSearchResult; >+ } >+ >+ public PluginSearchInput getPluginSearchInput() { >+ return fSearchInput; >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ManifestEditorOpener.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ManifestEditorOpener.java >index ded4863..7557067 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ManifestEditorOpener.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ManifestEditorOpener.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 > *******************************************************************************/ > package org.eclipse.pde.internal.ui.search; > >@@ -16,10 +17,14 @@ > import org.eclipse.pde.internal.core.ibundle.IBundlePluginModelBase; > import org.eclipse.pde.internal.core.ibundle.IManifestHeader; > import org.eclipse.pde.internal.core.text.plugin.PluginObjectNode; >-import org.eclipse.pde.internal.ui.editor.plugin.ManifestEditor; >+import org.eclipse.pde.internal.ui.editor.plugin.*; > import org.eclipse.search.ui.text.Match; >+import org.eclipse.swt.widgets.Text; > import org.eclipse.ui.IEditorPart; > import org.eclipse.ui.PartInitException; >+import org.eclipse.ui.forms.IFormPart; >+import org.eclipse.ui.forms.IManagedForm; >+import org.eclipse.ui.forms.editor.IFormPage; > import org.osgi.framework.Constants; > > // TODO this needs a rewrite >@@ -30,10 +35,25 @@ > editorPart = ManifestEditor.open(match.getElement(), true); > if (editorPart != null && editorPart instanceof ManifestEditor) { > ManifestEditor editor = (ManifestEditor) editorPart; >- IDocument doc = editor.getDocument(match); >- if (doc != null) { >- Match exact = findExactMatch(doc, match, editor); >- editor.openToSourcePage(match.getElement(), exact.getOffset(), exact.getLength()); >+ if (match.getBaseUnit() != AttributesMatch.UNIT_ATTRIBUTE_SEARCH_PATTERN) { >+ IDocument doc = editor.getDocument(match); >+ if (doc != null) { >+ Match exact = findExactMatch(doc, match, editor); >+ editor.openToSourcePage(match.getElement(), exact.getOffset(), exact.getLength()); >+ } >+ } else { // open to extensions page and initialize filter with search result's pattern text >+ IFormPage page = editor.setActivePage(ExtensionsPage.PAGE_ID); >+ IManagedForm form = page.getManagedForm(); >+ IFormPart parts[] = form.getParts(); >+ if (parts != null && parts.length > 0) { >+ ExtensionsSection section = (ExtensionsSection) parts[0]; >+ String searchPattern = ((AttributesMatch) match).getSearchPattern(); >+ Text filterText = section.getFormFilteredTree().getFilterControl(); >+ if (!filterText.getText().equals(searchPattern)) { >+ section.setBypassFilterDelay(true); // force immediate job run >+ filterText.setText(searchPattern); >+ } >+ } > } > } > return editorPart; >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/SearchResult.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/SearchResult.java >index f09c4ef..cd9f099 100644 >--- a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/SearchResult.java >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/SearchResult.java >@@ -7,6 +7,7 @@ > * > * Contributors: > * IBM Corporation - initial API and implementation >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 > *******************************************************************************/ > package org.eclipse.pde.internal.ui.search; > >@@ -29,9 +30,15 @@ > > public class SearchResult extends AbstractTextSearchResult implements IEditorMatchAdapter { > protected ISearchQuery fQuery; >+ private final ImageDescriptor fImage; > > public SearchResult(ISearchQuery query) { > fQuery = query; >+ if (fQuery instanceof FindExtensionsByAttributeQuery) { >+ fImage = PDEPluginImages.DESC_ESEARCH_OBJ; >+ } else { >+ fImage = PDEPluginImages.DESC_PSEARCH_OBJ; >+ } > } > > /* (non-Javadoc) >@@ -60,7 +67,7 @@ > * @see org.eclipse.search.ui.ISearchResult#getImageDescriptor() > */ > public ImageDescriptor getImageDescriptor() { >- return PDEPluginImages.DESC_PSEARCH_OBJ; >+ return fImage; > } > > /* (non-Javadoc) >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/AcceleratedTreeScrolling.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/AcceleratedTreeScrolling.java >new file mode 100644 >index 0000000..652079d >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/AcceleratedTreeScrolling.java >@@ -0,0 +1,104 @@ >+/******************************************************************************* >+ * Copyright (c) 2011, 2012 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: >+ * Sascha Becher <s.becher@qualitype.de> - bug 360894 >+ *******************************************************************************/ >+package org.eclipse.pde.internal.ui.util; >+ >+import org.eclipse.swt.SWT; >+import org.eclipse.swt.events.MouseEvent; >+import org.eclipse.swt.events.MouseWheelListener; >+import org.eclipse.swt.widgets.Tree; >+import org.eclipse.swt.widgets.TreeItem; >+ >+/** >+ * Accelerated tree scrolling with the mouse wheel during which MOD1 (Ctrl) key modifier has been pressed >+ * >+ * @author Sascha Becher >+ */ >+public class AcceleratedTreeScrolling implements MouseWheelListener { >+ >+ private Tree fTree; >+ private int fSkipLines; >+ >+ /** >+ * Accelerated mouse wheel scrolling during which the MOD1 (Ctrl) key modifier has been pressed >+ * >+ * @param tree tree to scroll >+ * @param skipLines lines to scroll >+ */ >+ public AcceleratedTreeScrolling(Tree tree, int skipLines) { >+ fTree = tree; >+ fSkipLines = (skipLines > 1) ? skipLines : 2; >+ } >+ >+ public void mouseScrolled(MouseEvent e) { >+ // scroll only when MOD1 is pressed >+ if ((e.stateMask & SWT.MOD1) == SWT.MOD1 && fTree != null) { >+ TreeItem item = fTree.getTopItem(); >+ if (item != null) { >+ TreeItem nextItem = item; >+ for (int i = 0; i < fSkipLines; i++) { >+ TreeItem foundItem = null; >+ if (e.count < 0) // determines scrolling direction >+ foundItem = NextItem(fTree, nextItem); >+ else >+ foundItem = PreviousItem(fTree, nextItem); >+ if (foundItem == null) { >+ break; >+ } >+ nextItem = foundItem; >+ } >+ fTree.setTopItem(nextItem); >+ } >+ } >+ } >+ >+ TreeItem PreviousItem(Tree tree, TreeItem item) { >+ if (item == null) >+ return null; >+ TreeItem childItem = item; >+ TreeItem parentItem = childItem.getParentItem(); >+ int index = parentItem == null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); >+ if (index == 0) { >+ return parentItem; >+ } >+ TreeItem nextItem = parentItem == null ? tree.getItem(index - 1) : parentItem.getItem(index - 1); >+ int count = nextItem.getItemCount(); >+ while (count > 0 && nextItem.getExpanded()) { >+ nextItem = nextItem.getItem(count - 1); >+ count = nextItem.getItemCount(); >+ } >+ return nextItem; >+ } >+ >+ TreeItem NextItem(Tree tree, TreeItem item) { >+ if (item == null) >+ return null; >+ if (item.getExpanded()) { >+ return item.getItem(0); >+ } >+ TreeItem childItem = item; >+ TreeItem parentItem = childItem.getParentItem(); >+ int index = parentItem == null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); >+ int count = parentItem == null ? tree.getItemCount() : parentItem.getItemCount(); >+ while (true) { >+ if (index + 1 < count) { >+ return parentItem == null ? tree.getItem(index + 1) : parentItem.getItem(index + 1); >+ } >+ if (parentItem == null) { >+ return null; >+ } >+ childItem = parentItem; >+ parentItem = childItem.getParentItem(); >+ index = parentItem == null ? tree.indexOf(childItem) : parentItem.indexOf(childItem); >+ count = parentItem == null ? tree.getItemCount() : parentItem.getItemCount(); >+ } >+ } >+ >+} >\ No newline at end of file >diff --git a/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/ExtensionsFilterUtil.java b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/ExtensionsFilterUtil.java >new file mode 100644 >index 0000000..75a5a95 >--- /dev/null >+++ b/ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/ExtensionsFilterUtil.java >@@ -0,0 +1,294 @@ >+package org.eclipse.pde.internal.ui.util; >+ >+import java.util.*; >+import org.eclipse.jface.viewers.IStructuredSelection; >+import org.eclipse.pde.core.plugin.*; >+import org.eclipse.pde.internal.core.text.plugin.PluginNode; >+import org.eclipse.pde.internal.ui.search.ExtensionsPatternFilter; >+ >+public class ExtensionsFilterUtil { >+ >+ /** >+ * <code>id, class, commandId, pattern, locationURI, defaultHandler, variable >+ * property, contentTypeId, path, plugin, perspective, targetID</code> >+ */ >+ // TODO related attributes might be configured through preferences >+ public static final String[] RELATED_ATTRIBUTES = {"id", //$NON-NLS-1$ >+ "class", "commandId", "pattern", "locationURI", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ >+ "defaultHandler", "variable", "property", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ >+ "contentTypeId", "path", "plugin", "perspective", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ >+ "targetID"}; //$NON-NLS-1$ >+ >+ public static final String ATTRIBUTE_ACTIVITYID = "activityId"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_CATEGORY = "category"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_CATEGORYID = "categoryId"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_COMMANDID = "commandId"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_DEFAULTHANDLER = "defaultHandler"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_ID = "id"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_NAME = "name"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_NAMESPACE = "namespace"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_PROPERTIES = "properties"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_PATTERN = "pattern"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_REQUIREDACTIVITYID = "requiredActivityId"; //$NON-NLS-1$ >+ public static final String ATTRIBUTE_VALUE = "value"; //$NON-NLS-1$ >+ >+ public static final String ELEMENT_ACATEGORY = "org.eclipse.ui.activities.category"; //$NON-NLS-1$ >+ public static final String ELEMENT_ACTIVITY = "org.eclipse.ui.activities.activity"; //$NON-NLS-1$ >+ public static final String ELEMENT_AR_BINDING = "org.eclipse.ui.activities.activityRequirementBinding"; //$NON-NLS-1$ >+ public static final String ELEMENT_CA_BINDING = "org.eclipse.ui.activities.categoryActivityBinding"; //$NON-NLS-1$ >+ public static final String ELEMENT_COMMAND = "org.eclipse.ui.commands.command"; //$NON-NLS-1$ >+ public static final String ELEMENT_EQUALS = "org.eclipse.ui.handlers.equals"; //$NON-NLS-1$ >+ public static final String ELEMENT_HELP_TOC = "org.eclipse.help.toc.toc"; //$NON-NLS-1$ >+ public static final String ELEMENT_INSTANCEOF = "org.eclipse.ui.handlers.instanceof"; //$NON-NLS-1$ >+ public static final String ELEMENT_MENU_COMMAND = "org.eclipse.ui.menus.command"; //$NON-NLS-1$ >+ public static final String ELEMENT_PATTERNBINDING = "org.eclipse.ui.activities.activityPatternBinding"; //$NON-NLS-1$ >+ public static final String ELEMENT_PROPERTYTESTER = "org.eclipse.core.expressions.propertyTesters.propertyTester"; //$NON-NLS-1$ >+ >+ public static final String[] HIGH_PRIORITY_ELEMENTS = new String[] {ELEMENT_COMMAND, ELEMENT_MENU_COMMAND, ELEMENT_INSTANCEOF, ELEMENT_EQUALS}; >+ public static final String[] LOW_PRIORITY_ELEMENTS = new String[] {ELEMENT_CA_BINDING, ELEMENT_AR_BINDING, ELEMENT_HELP_TOC}; >+ >+ public static final Map CUSTOM_RELATIONS; >+ >+ static { >+ CUSTOM_RELATIONS = new HashMap(); >+ CUSTOM_RELATIONS.put(ELEMENT_COMMAND, new String[] {ATTRIBUTE_ID, ATTRIBUTE_DEFAULTHANDLER}); >+ CUSTOM_RELATIONS.put(ELEMENT_INSTANCEOF, new String[] {ATTRIBUTE_VALUE}); >+ CUSTOM_RELATIONS.put(ELEMENT_EQUALS, new String[] {ATTRIBUTE_VALUE}); >+ CUSTOM_RELATIONS.put(ELEMENT_MENU_COMMAND, new String[] {ATTRIBUTE_COMMANDID, ATTRIBUTE_ID}); >+ CUSTOM_RELATIONS.put(ELEMENT_CA_BINDING, new String[] {ATTRIBUTE_ACTIVITYID, ATTRIBUTE_CATEGORYID}); >+ CUSTOM_RELATIONS.put(ELEMENT_AR_BINDING, new String[] {ATTRIBUTE_REQUIREDACTIVITYID, ATTRIBUTE_ACTIVITYID}); >+ CUSTOM_RELATIONS.put(ELEMENT_HELP_TOC, new String[] {ATTRIBUTE_CATEGORY}); >+ } >+ >+ public static boolean add(Set pattern, IPluginElement pluginElement, String attributeName) { >+ IPluginAttribute attribute = pluginElement.getAttribute(attributeName); >+ if (attribute != null) { >+ return add(pattern, attribute.getValue()); >+ } >+ return false; >+ } >+ >+ public static boolean add(Set pattern, String value) { >+ if (value != null && value.length() > 0) { >+ String trimmed = value.trim(); >+ if (isNotBoolean(trimmed)) { >+ return pattern.add(trimmed); >+ } >+ } >+ return false; >+ } >+ >+ public static void addAll(Set pattern, IPluginElement pluginElement, String elementName) { >+ Object attributes = CUSTOM_RELATIONS.get(elementName); >+ if (attributes != null) { >+ String[] attributesArray = (String[]) attributes; >+ for (int i = 0; i < attributesArray.length; i++) { >+ add(pattern, pluginElement, attributesArray[i]); >+ } >+ } >+ } >+ >+ /** >+ * Get unique plugin element name. This will work only with >+ * editable plugin models, otherwise <code>null</code> is returned. >+ * >+ * @param pluginElement the element to get the unique name from >+ * @return extensionpoint name concatenated with the element name >+ */ >+ public static String getElementPath(IPluginElement pluginElement) { >+ IPluginObject element = pluginElement; >+ while (element.getParent() != null && !(element.getParent() instanceof PluginNode)) { >+ element = element.getParent(); >+ } >+ if (element instanceof IPluginExtension) { >+ IPluginExtension extension = (IPluginExtension) element; >+ return extension.getPoint() + '.' + pluginElement.getName(); >+ } >+ return null; >+ } >+ >+ /** >+ * Obtains common attributes from selected plugin element to filter tree for; >+ * attribute values are concatenated with a slash and set as filter text >+ * >+ * @param selection selected items to filter for related plugin elements >+ */ >+ public static String getFilterRelatedPattern(IStructuredSelection selection) { >+ Iterator it = selection.iterator(); >+ Set filterPatterns = new HashSet(); >+ while (it.hasNext()) { >+ Object treeElement = it.next(); >+ if (treeElement instanceof IPluginElement) { >+ IPluginElement pluginElement = (IPluginElement) treeElement; >+ Set customAttributes = getCustomRelations(pluginElement); >+ if (customAttributes.size() == 0) { >+ for (int i = 0; i < RELATED_ATTRIBUTES.length; i++) { >+ String property = RELATED_ATTRIBUTES[i]; >+ IPluginAttribute attribute = pluginElement.getAttribute(property); >+ if (attribute != null && attribute.getValue() != null && attribute.getValue().length() > 0) { >+ String value = attribute.getValue(); >+ if (!value.startsWith("%")) { //$NON-NLS-1$ >+ int delimiterPosition = value.indexOf('?'); // split before '?' and right after last '=' >+ if (delimiterPosition == -1) { >+ if (!value.equalsIgnoreCase("true") && !value.equalsIgnoreCase("false")) { //$NON-NLS-1$ //$NON-NLS-2$ >+ filterPatterns.add(value); >+ } >+ } else { >+ filterPatterns.add(value.substring(0, delimiterPosition)); >+ int position = value.lastIndexOf('='); >+ if (position != -1) { >+ filterPatterns.add(value.substring(position + 1, value.length())); >+ } >+ } >+ } else { >+ String resourceValue = pluginElement.getResourceString(value); >+ if ((resourceValue != null && resourceValue.length() > 0)) { >+ filterPatterns.add(resourceValue); >+ } >+ } >+ } >+ } >+ } else { >+ filterPatterns.addAll(customAttributes); >+ } >+ } >+ } >+ StringBuffer patternBuffer = new StringBuffer(); >+ int attributeCount = 0; >+ for (Iterator iterator = filterPatterns.iterator(); iterator.hasNext();) { >+ attributeCount++; >+ Object pattern = iterator.next(); >+ if (attributeCount < ExtensionsPatternFilter.ATTRIBUTE_LIMIT) { >+ if (pattern != null) { >+ patternBuffer.append(pattern); >+ patternBuffer.append('/'); >+ } >+ } >+ } >+ >+ String filterPattern = patternBuffer.toString(); >+ if (filterPattern.endsWith("/")) { //$NON-NLS-1$ >+ filterPattern = filterPattern.substring(0, filterPattern.length() - 1); >+ } >+ return filterPattern; >+ } >+ >+ public static Set getCustomRelations(IPluginElement pluginElement) { >+ Set customAttributes = new TreeSet(); >+ String elementName = (pluginElement != null) ? getElementPath(pluginElement) : null; >+ if (elementName == null) { >+ return customAttributes; >+ } else if (addMatchingElements(customAttributes, pluginElement, elementName, HIGH_PRIORITY_ELEMENTS)) { >+ } else if (ELEMENT_ACTIVITY.equalsIgnoreCase(elementName) || ELEMENT_ACATEGORY.equals(elementName)) { >+ if (!add(customAttributes, pluginElement, ATTRIBUTE_ID)) { >+ add(customAttributes, pluginElement, ATTRIBUTE_NAME); >+ } >+ } else if (ELEMENT_PROPERTYTESTER.equalsIgnoreCase(elementName)) { >+ customAttributes = handlePropertyTester(customAttributes, pluginElement); >+ add(customAttributes, pluginElement, ATTRIBUTE_ID); >+ } else if (ELEMENT_PATTERNBINDING.equals(elementName)) { >+ add(customAttributes, pluginElement, ATTRIBUTE_ACTIVITYID); >+ String attributeValue = pluginElement.getAttribute(ATTRIBUTE_PATTERN).getValue(); >+ if (attributeValue.length() > 0) { >+ int lastSeparator = attributeValue.lastIndexOf('/'); >+ if (lastSeparator > 0 && attributeValue.length() > lastSeparator + 1) { >+ customAttributes.add(attributeValue.substring(lastSeparator + 1, attributeValue.length())); >+ } >+ } >+ } else { >+ addMatchingElements(customAttributes, pluginElement, elementName, LOW_PRIORITY_ELEMENTS); >+ } >+ return customAttributes; >+ } >+ >+ private static boolean addMatchingElements(Set customAttributes, IPluginElement pluginElement, String elementName, final String[] elements) { >+ boolean elementMatch = false; >+ for (int i = 0; i < elements.length; i++) { >+ if (elements[i].equals(elementName)) { >+ addAll(customAttributes, pluginElement, elements[i]); >+ elementMatch = true; >+ } >+ } >+ return elementMatch; >+ } >+ >+ private static Set handlePropertyTester(Set customAttributes, IPluginElement pluginElement) { >+ String namespace = pluginElement.getAttribute(ATTRIBUTE_NAMESPACE).getValue(); >+ String properties = pluginElement.getAttribute(ATTRIBUTE_PROPERTIES).getValue(); >+ if (namespace.length() > 0) { >+ String[] propertiesArray = properties.split(","); //$NON-NLS-1$ >+ for (int i = 0; i < propertiesArray.length; i++) { >+ String property = propertiesArray[i].trim(); >+ if (property.length() > 0) { >+ customAttributes.add(namespace + '.' + property); >+ } >+ } >+ if (propertiesArray.length == 0) { >+ customAttributes.add(namespace); >+ } >+ } >+ return customAttributes; >+ } >+ >+ public static List handlePropertyTester(IPluginElement pluginElement) { >+ List propertyTestAttributes = new ArrayList(); >+ String elementName = getElementPath(pluginElement); >+ boolean elementMatch = false; >+ if (elementName == null) { >+ // workaround for non-editable plugins of the target platform >+ if (ELEMENT_PROPERTYTESTER.endsWith(pluginElement.getName())) { >+ elementMatch = true; >+ } >+ } else if (ELEMENT_PROPERTYTESTER.equalsIgnoreCase(elementName)) { >+ elementMatch = true; >+ } >+ if (elementMatch) { >+ Set attributes = handlePropertyTester(new HashSet(), pluginElement); >+ for (Iterator iterator = attributes.iterator(); iterator.hasNext();) { >+ propertyTestAttributes.add(iterator.next()); >+ } >+ } >+ return propertyTestAttributes; >+ } >+ >+ public static boolean isFilterRelatedEnabled(IPluginElement pluginElement) { >+ for (int i = 0; i < RELATED_ATTRIBUTES.length; i++) { >+ String property = RELATED_ATTRIBUTES[i]; >+ IPluginAttribute attribute = pluginElement.getAttribute(property); >+ if (attribute != null) { >+ return true; >+ } >+ } >+ // test for custom relations >+ Object attributes = CUSTOM_RELATIONS.get(getElementPath(pluginElement)); >+ if (attributes != null) { >+ String[] attributesArray = (String[]) attributes; >+ for (int i = 0; i < attributesArray.length; i++) { >+ IPluginAttribute attribute = pluginElement.getAttribute(attributesArray[i]); >+ if (attribute != null) { >+ return true; >+ } >+ } >+ } >+ return false; >+ } >+ >+ public static boolean isFilterRelatedEnabled(IStructuredSelection structuredSelection) { >+ boolean createFilterRelatedAction = false; >+ if (structuredSelection != null && !structuredSelection.isEmpty()) { >+ Iterator it = structuredSelection.iterator(); >+ while (it.hasNext()) { >+ Object treeElement = it.next(); >+ if (treeElement instanceof IPluginElement) { >+ createFilterRelatedAction |= isFilterRelatedEnabled((IPluginElement) treeElement); >+ } >+ } >+ } >+ return createFilterRelatedAction; >+ } >+ >+ public static boolean isNotBoolean(String bool) { >+ return !bool.equalsIgnoreCase(Boolean.TRUE.toString()) && !bool.equalsIgnoreCase(Boolean.FALSE.toString()); >+ } >+ >+} >\ No newline at end of file
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Flags:
curtis.windatt.public
:
iplog+
curtis.windatt.public
:
review+
Actions:
View
|
Diff
Attachments on
bug 360894
:
205156
|
205157
|
209727
|
209729
| 211150 |
211250