diff options
author | Brian de Alwis | 2016-03-12 04:34:34 +0000 |
---|---|---|
committer | Brian de Alwis | 2016-03-12 05:53:29 +0000 |
commit | ad8a47c4bdd332042253f75caa105526cb5d267b (patch) | |
tree | 86324991555117aa2761852f93c7b07314927200 /org.eclipse.ui.intro.quicklinks/src/org/eclipse/ui/intro | |
parent | 85deccbfb8fbf0a0e0255c6c837124008b973d1f (diff) | |
download | eclipse.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/src/org/eclipse/ui/intro')
2 files changed, 216 insertions, 162 deletions
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 → quicklink */ + private Map<String, Quicklink> quicklinks = new LinkedHashMap<>(); + /** bundle symbolic name → 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() { } } |