Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorBrian de Alwis2016-03-12 04:34:34 +0000
committerBrian de Alwis2016-03-12 05:53:29 +0000
commitad8a47c4bdd332042253f75caa105526cb5d267b (patch)
tree86324991555117aa2761852f93c7b07314927200 /org.eclipse.ui.intro.quicklinks
parent85deccbfb8fbf0a0e0255c6c837124008b973d1f (diff)
downloadeclipse.platform.ua-ad8a47c4bdd332042253f75caa105526cb5d267b.tar.gz
eclipse.platform.ua-ad8a47c4bdd332042253f75caa105526cb5d267b.tar.xz
eclipse.platform.ua-ad8a47c4bdd332042253f75caa105526cb5d267b.zip
Bug 466370 - Add default content for Quicklinks viewer
- Configure default content to be shown for the Quicklinks viewer for situation where there are no quicklinks definitions found. - Fixed data: URL generation. - Process definitions from product bundle first Change-Id: Ic0e5140702fb2061934ee84a9501907ad1b8a55d
Diffstat (limited to 'org.eclipse.ui.intro.quicklinks')
-rw-r--r--org.eclipse.ui.intro.quicklinks/META-INF/MANIFEST.MF11
-rw-r--r--org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/FilteringContentProvider.java51
-rw-r--r--org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/QuicklinksViewer.java327
3 files changed, 222 insertions, 167 deletions
diff --git a/org.eclipse.ui.intro.quicklinks/META-INF/MANIFEST.MF b/org.eclipse.ui.intro.quicklinks/META-INF/MANIFEST.MF
index b2af4eabf..b1d284798 100644
--- a/org.eclipse.ui.intro.quicklinks/META-INF/MANIFEST.MF
+++ b/org.eclipse.ui.intro.quicklinks/META-INF/MANIFEST.MF
@@ -6,11 +6,12 @@ Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: %provider_name
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Require-Bundle: org.eclipse.ui.intro;bundle-version="3.4.0",
- org.eclipse.ui.intro.universal;bundle-version="3.2.0",
- org.eclipse.ui.workbench,
- org.eclipse.jface,
- org.eclipse.ui.forms,
- org.eclipse.equinox.registry;bundle-version="3.6.100"
+ org.eclipse.ui.workbench;bundle-version="3.108.0",
+ org.eclipse.jface;bundle-version="3.12.0",
+ org.eclipse.ui.forms;bundle-version="3.7.0",
+ org.eclipse.equinox.registry;bundle-version="3.6.100",
+ org.eclipse.core.runtime;bundle-version="3.12.0"
Import-Package: org.osgi.framework;version="1.8.0"
Bundle-Localization: plugin
Bundle-ActivationPolicy: lazy
+Export-Package: org.eclipse.ui.intro.quicklinks;version="1.0.0"
diff --git a/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/FilteringContentProvider.java b/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/FilteringContentProvider.java
deleted file mode 100644
index f1c40cde4..000000000
--- a/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/FilteringContentProvider.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2016 Manumitting Technologies Inc 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:
- * Manumitting Technologies Inc - initial API and implementation
- *******************************************************************************/
-package org.eclipse.ui.intro.quicklinks;
-
-import java.util.Collection;
-import java.util.function.Function;
-import java.util.stream.Stream;
-
-import org.eclipse.jface.viewers.IStructuredContentProvider;
-import org.eclipse.jface.viewers.Viewer;
-
-/**
- * A structured content provider that first transforms elements with a function.
- *
- * @param <O>
- * the object type
- */
-public class FilteringContentProvider<O> implements IStructuredContentProvider {
-
- private Function<Object, O> function;
-
- public FilteringContentProvider(Function<Object, O> f) {
- this.function = f;
- }
-
- @Override
- public void dispose() {
- }
-
- @Override
- public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
- }
-
- @Override
- public Object[] getElements(Object inputElement) {
- if (inputElement instanceof Object[]) {
- return Stream.of((Object[]) inputElement).map(function).toArray();
- } else if (inputElement instanceof Collection) {
- return ((Collection<?>) inputElement).stream().map(function).toArray();
- }
- return Stream.of(inputElement).map(function).toArray();
- }
-}
diff --git a/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/QuicklinksViewer.java b/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/QuicklinksViewer.java
index 12a3edc19..42ef41bd9 100644
--- a/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/QuicklinksViewer.java
+++ b/org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro/quicklinks/QuicklinksViewer.java
@@ -20,8 +20,10 @@ import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLEncoder;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Base64;
import java.util.HashMap;
+import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -36,7 +38,10 @@ import org.eclipse.core.commands.SerializationException;
import org.eclipse.core.commands.common.NotDefinedException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IConfigurationElement;
+import org.eclipse.core.runtime.IExtension;
+import org.eclipse.core.runtime.IExtensionPoint;
import org.eclipse.core.runtime.IExtensionRegistry;
+import org.eclipse.core.runtime.Platform;
import org.eclipse.jface.resource.ImageDescriptor;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.TableViewer;
@@ -64,8 +69,11 @@ import org.osgi.framework.FrameworkUtil;
* from the command metadata, including the image icon, but can be tailored.
* These tailorings can be made optional depending on the current theme.
*
- * This is still experimental and subject to change.
+ * This implementation is still experimental and subject to change. Feedback
+ * welcome as a <a href="http://eclip.se/9f">bug report on the Eclipse Bugzilla
+ * against Platform/User Assistance</a>.
*/
+@SuppressWarnings("restriction")
public class QuicklinksViewer implements IIntroContentProvider {
/** Represents the importance of an element */
@@ -91,21 +99,49 @@ public class QuicklinksViewer implements IIntroContentProvider {
}
/** Model holding the relevant attributes of a Quicklink element */
- class Quicklink {
+ class Quicklink implements Comparable<Quicklink> {
String commandSpec;
String label;
String description;
String iconUrl;
boolean standby = true;
- Importance importance = Importance.LOW;
+ Importance importance = Importance.MEDIUM;
+ long rank;
- String bundleSymbolicName;
+ public Quicklink() {
+ }
+
+ public Quicklink(String commandSpec) {
+ this.commandSpec = commandSpec;
+ }
+
+ public Quicklink(String commandSpec, Importance importance) {
+ this.commandSpec = commandSpec;
+ this.importance = importance;
+ }
+
+ @Override
+ public int compareTo(Quicklink b) {
+ int impA = this.importance.level;
+ int impB = b.importance.level;
+ if (impA != impB) {
+ return impA - impB;
+ }
+ long diff = this.rank - b.rank;
+ if (diff > 0) {
+ return 1;
+ }
+ if (diff < 0) {
+ return -1;
+ }
+ return 0;
+ }
}
/**
* Responsible for retrieving Quicklinks and applying any icon overrides
*/
- class ModelReader implements Supplier<Stream<Quicklink>> {
+ class ModelReader implements Supplier<List<Quicklink>> {
private static final String QL_EXT_PT = "org.eclipse.ui.intro.quicklinks"; //$NON-NLS-1$
private static final String ELMT_QUICKLINK = "quicklink"; //$NON-NLS-1$
private static final String ATT_COMMAND = "command"; //$NON-NLS-1$
@@ -117,60 +153,153 @@ public class QuicklinksViewer implements IIntroContentProvider {
private static final String ELMT_OVERRIDE = "override"; //$NON-NLS-1$
private static final String ATT_THEME = "theme"; //$NON-NLS-1$
- private List<Quicklink> quicklinks = new ArrayList<>();
-
- public Stream<Quicklink> get() {
+ /** commandSpec &rarr; quicklink */
+ private Map<String, Quicklink> quicklinks = new LinkedHashMap<>();
+ /** bundle symbolic name &rarr; bundle id */
+ private Map<String, Long> bundleIds;
+ private Bundle[] bundles;
+
+ /**
+ * Return the list of configured {@link Quicklink} that can be found.
+ *
+ * @return
+ */
+ public List<Quicklink> get() {
CommandManager manager = locator.getService(CommandManager.class);
-
- for (IConfigurationElement ce : getExtensionRegistry().getConfigurationElementsFor(QL_EXT_PT)) {
- if (!ELMT_QUICKLINK.equals(ce.getName())) {
- continue;
- }
- String commandSpec = ce.getAttribute(ATT_COMMAND);
- try {
- ParameterizedCommand pc = manager.deserialize(commandSpec);
- if (pc != null && pc.getCommand().isDefined()) {
- Quicklink ql = new Quicklink();
- ql.bundleSymbolicName = ce.getContributor().getName();
- ql.commandSpec = commandSpec;
- ql.label = Optional.ofNullable(ce.getAttribute(ATT_LABEL)).orElse(pc.getCommand().getName());
- ql.description = Optional.ofNullable(ce.getAttribute(ATT_DESCRIPTION))
- .orElse(pc.getCommand().getDescription());
- ql.iconUrl = QuicklinksViewer.this.getImageURL(ce, ATT_ICON, commandSpec);
- if (ce.getAttribute(ATT_IMPORTANCE) != null) {
- ql.importance = Importance.forId(ce.getAttribute(ATT_IMPORTANCE));
+ IExtension extensions[] = getExtensions(QL_EXT_PT);
+
+ // Process definitions from the product bundle first
+ Bundle productBundle = Platform.getProduct().getDefiningBundle();
+ if(productBundle != null) {
+ for (IExtension ext : extensions) {
+ if (productBundle.getSymbolicName().equals(ext.getNamespaceIdentifier())) {
+ for (IConfigurationElement ce : ext.getConfigurationElements()) {
+ processDefinition(manager, ce);
}
- if (ce.getAttribute(ATT_STANDBY) != null) {
- ql.standby = Boolean.valueOf(ce.getAttribute(ATT_STANDBY));
- }
- quicklinks.add(ql);
}
- } catch (NotDefinedException | SerializationException e) {
- /* skip */
- System.err.printf("Skipping '%s': %s\n", commandSpec, e);
+ }
+ }
+
+ for (IExtension ext : extensions) {
+ if (productBundle == null || !productBundle.getSymbolicName().equals(ext.getNamespaceIdentifier())) {
+ for (IConfigurationElement ce : ext.getConfigurationElements()) {
+ processDefinition(manager, ce);
+ }
}
}
- for (IConfigurationElement ce : getExtensionRegistry().getConfigurationElementsFor(QL_EXT_PT)) {
- if (!ELMT_OVERRIDE.equals(ce.getName())) {
- continue;
+ for (IExtension ext : extensions) {
+ for (IConfigurationElement ce : ext.getConfigurationElements()) {
+ if (!ELMT_OVERRIDE.equals(ce.getName())) {
+ continue;
+ }
+ String theme = ce.getAttribute(ATT_THEME);
+ String commandSpecPattern = ce.getAttribute(ATT_COMMAND);
+ String icon = ce.getAttribute(ATT_ICON);
+ if (theme != null && icon != null && Objects.equals(theme, getCurrentThemeId())
+ && commandSpecPattern != null) {
+ findMatchingQuicklinks(commandSpecPattern)
+ .forEach(ql -> ql.iconUrl = getImageURL(ce, ATT_ICON, null));
+ }
}
- String theme = ce.getAttribute(ATT_THEME);
- String commandSpecPattern = ce.getAttribute(ATT_COMMAND);
- String icon = ce.getAttribute(ATT_ICON);
- if (theme != null && icon != null && Objects.equals(theme, getCurrentThemeId()) && commandSpecPattern != null) {
- findMatchingQuicklinks(commandSpecPattern)
- .forEach(ql -> ql.iconUrl = QuicklinksViewer.this.getImageURL(ce, ATT_ICON, null));
+ }
+ return new ArrayList<>(quicklinks.values());
+ }
+
+ private void processDefinition(CommandManager manager, IConfigurationElement ce) {
+ if (!ELMT_QUICKLINK.equals(ce.getName())) {
+ return;
+ }
+ String commandSpec = ce.getAttribute(ATT_COMMAND);
+ try {
+ ParameterizedCommand pc = manager.deserialize(commandSpec);
+ if (pc != null && pc.getCommand().isDefined()) {
+ Quicklink ql = new Quicklink();
+ ql.commandSpec = commandSpec;
+ ql.label = Optional.ofNullable(ce.getAttribute(ATT_LABEL)).orElse(pc.getCommand().getName());
+ ql.description = Optional.ofNullable(ce.getAttribute(ATT_DESCRIPTION))
+ .orElse(pc.getCommand().getDescription());
+ ql.iconUrl = getImageURL(ce, ATT_ICON, commandSpec);
+ ql.rank = getRank(ce.getContributor().getName());
+ if (ce.getAttribute(ATT_IMPORTANCE) != null) {
+ ql.importance = Importance.forId(ce.getAttribute(ATT_IMPORTANCE));
+ }
+ if (ce.getAttribute(ATT_STANDBY) != null) {
+ ql.standby = Boolean.valueOf(ce.getAttribute(ATT_STANDBY));
+ }
+ // discard if already seen
+ quicklinks.putIfAbsent(commandSpec, ql);
}
+ } catch (NotDefinedException | SerializationException e) {
+ /* skip */
+ System.err.printf("Skipping '%s': %s\n", commandSpec, e);
}
- return quicklinks.stream();
}
+ /**
+ * Find {@link Quicklink}s whose {@code commandSpec} matches the simple
+ * wildcard pattern in {@code commandSpecPattern}
+ *
+ * @param commandSpecPattern
+ * a simple wildcard pattern supporting *, ?
+ * @return the set of matching Quicklinks
+ */
private Stream<Quicklink> findMatchingQuicklinks(String commandSpecPattern) {
- commandSpecPattern = commandSpecPattern.replace(".", "\\.").replace("(", "\\(").replace(")", "\\)")
+ // transform simple wildcards into regexp
+ String regexp = commandSpecPattern.replace(".", "\\.").replace("(", "\\(").replace(")", "\\)")
.replace("*", ".*");
- final Pattern pattern = Pattern.compile(commandSpecPattern);
- return quicklinks.stream().filter(ql -> pattern.matcher(ql.commandSpec).matches());
+ final Pattern pattern = Pattern.compile(regexp);
+ return quicklinks.values().stream().filter(
+ ql -> commandSpecPattern.equals(ql.commandSpec) || pattern.matcher(ql.commandSpec).matches());
+ }
+
+ private IExtension[] getExtensions(String extPtId) {
+ IExtensionRegistry registry = locator.getService(IExtensionRegistry.class);
+ IExtensionPoint extPt = registry.getExtensionPoint(extPtId);
+ return extPt == null ? new IExtension[0] : extPt.getExtensions();
+ }
+
+
+ private long getRank(String bundleSymbolicName) {
+ if (bundleIds == null) {
+ Bundle bundle = FrameworkUtil.getBundle(getClass());
+ bundleIds = new HashMap<>();
+ bundles = bundle.getBundleContext().getBundles();
+ }
+ return bundleIds.computeIfAbsent(bundleSymbolicName, bsn -> {
+ for (Bundle b : bundles) {
+ if (bsn.equals(b.getSymbolicName())
+ && (b.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
+ return b.getBundleId();
+ }
+ }
+ return Long.MAX_VALUE;
+ });
+ }
+
+ /**
+ * @return URL to image, suitable for using in an external browser; may
+ * be a <code>data:</code> URL; may be null
+ */
+ private String getImageURL(IConfigurationElement ce, String attr, String commandId) {
+ String iconURL = MenuHelper.getIconURI(ce, attr);
+ if (iconURL != null) {
+ return asBrowserURL(iconURL);
+ }
+
+ if (commandId == null) {
+ return null;
+ }
+ ICommandImageService images = locator.getService(ICommandImageService.class);
+ if (images == null) {
+ return null;
+ }
+ ImageDescriptor descriptor = images.getImageDescriptor(commandId);
+ iconURL = MenuHelper.getImageUrl(descriptor);
+ if (iconURL != null) {
+ return asBrowserURL(iconURL);
+ }
+ return asDataURL(descriptor);
}
}
@@ -179,20 +308,28 @@ public class QuicklinksViewer implements IIntroContentProvider {
private IIntroContentProviderSite site;
private IServiceLocator locator;
- private Map<String, Long> bundleIds;
- private Bundle[] bundles;
+
+ private Supplier<List<Quicklink>> model;
public void init(IIntroContentProviderSite site) {
this.site = site;
// IIntroContentProviderSite should provide services.
- if (site instanceof AbstractIntroPartImplementation) {
+ if (site instanceof IServiceLocator) {
+ this.locator = (IServiceLocator) site;
+ } else if (site instanceof AbstractIntroPartImplementation) {
this.locator = ((AbstractIntroPartImplementation) site).getIntroPart().getIntroSite();
} else {
this.locator = PlatformUI.getWorkbench();
}
+ model = new ModelReader();
}
- public String getCurrentThemeId() {
+ /**
+ * Find the current Welcome/Intro identifier
+ *
+ * @return the current identifier or {@code null} if no theme
+ */
+ protected String getCurrentThemeId() {
if (site instanceof AbstractIntroPartImplementation) {
IntroTheme theme = ((AbstractIntroPartImplementation) site).getModel().getTheme();
return theme.getId();
@@ -200,10 +337,6 @@ public class QuicklinksViewer implements IIntroContentProvider {
return null;
}
- public IExtensionRegistry getExtensionRegistry() {
- return locator.getService(IExtensionRegistry.class);
- }
-
public void createContent(String id, PrintWriter out) {
// Content is already embedded within a <div id="...">
getQuicklinks().forEach(ql -> {
@@ -237,6 +370,10 @@ public class QuicklinksViewer implements IIntroContentProvider {
});
}
+ /**
+ * Transform the Eclipse Command identifier (with dots) to a CSS-compatible
+ * class
+ */
private String asCSSId(String commandSpec) {
int indexOf = commandSpec.indexOf('(');
if (indexOf > 0) {
@@ -246,30 +383,13 @@ public class QuicklinksViewer implements IIntroContentProvider {
}
/**
- * @return URL to image, suitable for using in an external browser; may be a
- * <code>data:</code> URL; may be null
+ * Rewrite or possible extract the icon at the given URL to a stable URL
+ * that can be embedded in HTML and rendered in a browser. May create
+ * temporary files that will be cleaned up on exit.
+ *
+ * @param iconURL
+ * @return stable URL
*/
- private String getImageURL(IConfigurationElement ce, String attr, String commandId) {
- String iconURL = MenuHelper.getIconURI(ce, attr);
- if (iconURL != null) {
- return asBrowserURL(iconURL);
- }
-
- if (commandId == null) {
- return null;
- }
- ICommandImageService images = locator.getService(ICommandImageService.class);
- if (images == null) {
- return null;
- }
- ImageDescriptor descriptor = images.getImageDescriptor(commandId);
- iconURL = MenuHelper.getImageUrl(descriptor);
- if (iconURL != null) {
- return asBrowserURL(iconURL);
- }
- return asDataURL(descriptor);
- }
-
private String asBrowserURL(String iconURL) {
if (iconURL.startsWith("file:") || iconURL.startsWith("http:")) {
return iconURL;
@@ -314,7 +434,7 @@ public class QuicklinksViewer implements IIntroContentProvider {
loader.save(output, SWT.IMAGE_PNG);
if (output.size() * 4 / 3 < MAX_URL_LENGTH) {
// You'd think there was a more efficient way to do this...
- return "data:image/png;base64," + Base64.getUrlEncoder().encodeToString(output.toByteArray());
+ return "data:image/png;base64," + Base64.getEncoder().encodeToString(output.toByteArray());
}
try {
File tempFile = File.createTempFile("qlink", "png");
@@ -353,42 +473,27 @@ public class QuicklinksViewer implements IIntroContentProvider {
tableViewer.setInput(getQuicklinks().toArray());
}
- private Stream<Quicklink> getQuicklinks() {
- return new ModelReader().get().sorted(this::compareQuicklinks);
- }
-
- public void dispose() {
+ private List<Quicklink> getQuicklinks() {
+ List<Quicklink> links = model.get();
+ if (links.isEmpty()) {
+ links = generateDefaultQuicklinks();
+ }
+ links.sort(Quicklink::compareTo);
+ return links;
}
- private int compareQuicklinks(Quicklink a, Quicklink b) {
- int impA = a.importance.level;
- int impB = b.importance.level;
- if (impA != impB) {
- return impA - impB;
- }
- long diff = getRank(a) - getRank(b);
- if (diff > 0) {
- return 1;
- }
- if (diff < 0) {
- return -1;
- }
- return 0;
+ /**
+ * Return the default commands to be shown if there is no other content
+ * available
+ */
+ private List<Quicklink> generateDefaultQuicklinks() {
+ return Arrays.asList(new Quicklink("org.eclipse.oomph.setup.ui.questionnaire", Importance.HIGH),
+ new Quicklink("org.eclipse.ui.cheatsheets.openCheatSheet"),
+ new Quicklink("org.eclipse.ui.newWizard"), new Quicklink("org.eclipse.ui.file.import"),
+ new Quicklink("org.eclipse.epp.mpc.ui.command.showMarketplaceWizard"),
+ new Quicklink("org.eclipse.ui.edit.text.openLocalFile", Importance.LOW));
}
- private long getRank(Quicklink ql) {
- if (bundleIds == null) {
- Bundle bundle = FrameworkUtil.getBundle(getClass());
- bundleIds = new HashMap<>();
- bundles = bundle.getBundleContext().getBundles();
- }
- return bundleIds.computeIfAbsent(ql.bundleSymbolicName, bsn -> {
- for (Bundle b : bundles) {
- if (bsn.equals(b.getSymbolicName()) && (b.getState() & (Bundle.INSTALLED | Bundle.UNINSTALLED)) == 0) {
- return b.getBundleId();
- }
- }
- return Long.MAX_VALUE;
- });
+ public void dispose() {
}
}

Back to the top