summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSascha Becher2012-02-17 13:04:01 (EST)
committer Curtis Windatt2012-02-27 14:46:21 (EST)
commita069542034321f8f065b7fc41da5b3a082d21370 (patch)
tree99f970eeb6dd6b999928d1c266476362f6ec396c
parent296ac4cc9c27770780bb5d92ec16be4f1de8522d (diff)
downloadeclipse.pde.ui-a069542034321f8f065b7fc41da5b3a082d21370.zip
eclipse.pde.ui-a069542034321f8f065b7fc41da5b3a082d21370.tar.gz
eclipse.pde.ui-a069542034321f8f065b7fc41da5b3a082d21370.tar.bz2
Bug 360894 - [patch] Extensions tree viewer should also be filtered by leaf item's attributesv20120227-1946
-rw-r--r--ui/org.eclipse.pde.ui/icons/dlcl16/filter_related.gifbin0 -> 222 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/dlcl16/search_extensions.gifbin0 -> 363 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/dlcl16/toggle_expand_state.gifbin0 -> 162 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/elcl16/filter_related.gifbin0 -> 225 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/elcl16/search_extensions.gifbin0 -> 579 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/elcl16/toggle_expand_state.gifbin0 -> 162 bytes
-rw-r--r--ui/org.eclipse.pde.ui/icons/obj16/esearch_obj.gifbin0 -> 578 bytes
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEPluginImages.java7
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/PDEUIMessages.java8
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/FilterRelatedExtensionsAction.java61
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/SearchExtensionsAction.java80
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ShowAllExtensionsAction.java130
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/actions/ToggleExpandStateAction.java181
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/AbstractPluginElementDetails.java26
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/ExtensionsSection.java435
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/editor/plugin/rows/ExtensionAttributeRow.java22
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/pderesources.properties8
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/AttributesMatch.java45
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionElementSearchOperation.java106
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ExtensionsPatternFilter.java309
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/FindExtensionsByAttributeQuery.java88
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/ManifestEditorOpener.java30
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/search/SearchResult.java9
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/AcceleratedTreeScrolling.java104
-rw-r--r--ui/org.eclipse.pde.ui/src/org/eclipse/pde/internal/ui/util/ExtensionsFilterUtil.java294
25 files changed, 1866 insertions, 77 deletions
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 class PDEPluginImages {
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 class PDEPluginImages {
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 class PDEPluginImages {
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 class PDEPluginImages {
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 class PDEPluginImages {
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 40aa25b..fdbe4a0 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 class PDEUIMessages extends NLS {
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 class PDEUIMessages extends NLS {
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 class PDEUIMessages extends NLS {
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;
@@ -2998,4 +3004,4 @@ public class PDEUIMessages extends NLS {
public static String OSGiConsole_name;
public static String OSGiConsoleFactory_title;
-} \ No newline at end of file
+}
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..28409f9
--- /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..40ae21b
--- /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..879d027
--- /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 @@ package org.eclipse.pde.internal.ui.editor.plugin;
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 @@ public abstract class AbstractPluginElementDetails extends PDEDetails {
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.ischema.*;
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,7 +131,7 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
}
}
- class ExtensionLabelProvider extends LabelProvider {
+ class ExtensionLabelProvider extends LabelProvider implements IFontProvider {
public String getText(Object obj) {
return resolveObjectName(obj);
}
@@ -120,6 +139,13 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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;
+ }
}
public ExtensionsSection(PDEFormPage page, Composite parent) {
@@ -221,9 +247,11 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
}
}
});
+ // 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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
* @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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
}
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 class ExtensionsSection extends TreeSection implements IModelChangedListe
}
}
+ 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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
private void handleSelectAll() {
fExtensionTree.getTree().selectAll();
+ updateButtons(fFilteredTree.getViewer().getSelection());
}
private ArrayList getEditorWizards(IStructuredSelection selection) {
@@ -643,13 +759,62 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
// 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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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,7 +1018,10 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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() {
@@ -844,6 +1029,16 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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) {
StringBuffer output = new StringBuffer();
for (int i = 0; i < input.length(); i++) {
@@ -857,6 +1052,24 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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)
* @see org.eclipse.pde.internal.ui.editor.StructuredViewerSection#canPaste(java.lang.Object, java.lang.Object[])
*/
@@ -1081,6 +1294,18 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
}
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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 @@ public class ExtensionsSection extends TreeSection implements IModelChangedListe
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.IControlHoverContentProvider;
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 abstract class ExtensionAttributeRow implements IControlHoverContentProvi
}
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 747a638..c1562a3 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_paste = &Paste
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_references = Find Re&ferences
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 @@ ExportWizard_status_noantfile = An Ant build file must be specified.
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
@@ -2395,4 +2401,4 @@ SearchRepositoriesForIUProposal_message=Search repositories for ''{0}''
SearchRepositoriesForIUProposal_description=Opens the artifact search dialog to search for ''{0}'' to add to your target
OSGiConsole_name=Host OSGi Console [{0}]
-OSGiConsoleFactory_title=WARNING: This console is connected to the current running instance of Eclipse! \ No newline at end of file
+OSGiConsoleFactory_title=WARNING: This console is connected to the current running instance of Eclipse!
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..62f6df3
--- /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..ba0e021
--- /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..b6d0ed2
--- /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.core.plugin.*;
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 @@ public class ManifestEditorOpener {
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 @@ import org.eclipse.ui.texteditor.ITextEditor;
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 @@ public class SearchResult extends AbstractTextSearchResult implements IEditorMat
* @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