Community
Participate
Working Groups
Build Identifier: Build id: 20110916-0149 The IImageProvider interface makes a couple of assumptions that could be more flexible. One is that images come from the same plugin that provides the IImageProvider. I have a use case where a graphiti shape provided by my editor is customized into a large number of specialized shapes by plugins. One thing that these plugins may contribute is an icon displayed on the shape. I'd like to be able to provide an extension point to my plugin that gathers all of the images and supplies them to graphiti. That would eliminate a dependency between my client plugins and graphiti. It would eliminate code duplication for supplying images in all of my client plugins. To do that, I'd need to be able to tell graphiti which plugin the image was in rather than it telling me. I could override getPluginId() to ignore the value set by createImageProvider() and just return what I want. I may do that for now if it works. But, that seems like an abuse of the interface contract. The other assumption, maybe it's a requirement for reasons that I'm unaware of, is that images must be sourced from a plugin. Some of the specialized shapes in my editor are specialized by the user at run time. I'd like to be able to add, delete or modify an image in the provider map from a user supplied image that is available from a plain old file. Reproducible: Always
Turns out getPluginId() is final, so overriding it won't work. The lack of atomicity between getPluginId() and getImageFilePath() made it highly implementation dependent anyway.
As I try to work my way around this, I've realized that not only do the plugins that extend my editor have to depend on Graphiti to extend the image provider extension point, but my editor which extends diagramTypeProviders has to include a reference to the id of the image providers that they extend. That means that my editor plugin has to have prior knowledge of the things that extend it. Which breaks my use case. I need to be able to register something like an IImageProvider that gets passed an id and is responsible for returning the Image.
I have a similar requirement. One possible solution is to add a documentTypeExtension extension point that allows other plugins to contribute functionality within the scope of a specific document type. I propose this extension point mirror documentTypeProvider in all aspects except the actual provider part. This would allow the existing parse code to be reused. I'm effectively using a HACKY version of this whereby additional plugins redeclare documentTypeProvider but do not provide an implementation. This allows their image providers to be loaded when that specific document type is loaded. It would be nice if there were a safer way to do this. Best, Rob
If you could support general URLs including platform:/plugin and platform:/resource instead of just paths relative to a plugin, I think most of these use cases would be handled. E.g. if you create your own extension point that lets other plugins provide new images, you can add a platform:/plugin/<bundleId> prefix to provided plugin-relative path. This means the implementation may need a more powerful ImageDescriptor, in general reading from a URL. Here's one: package org.ptolemy.graphiti.editor.jface; import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; import java.net.URI; import java.net.URL; import org.eclipse.jface.resource.ImageDescriptor; import org.eclipse.jface.resource.ImageRegistry; import org.eclipse.jface.viewers.LabelProvider; import org.eclipse.swt.graphics.ImageData; /** * ImageDescriptor that downloads the image data from a URL * Typically used by a LabelProvider with an ImageRegistry to provide icons. * @see LabelProvider * @see ImageRegistry * @author hal * */ public class URLImageDescriptor extends ImageDescriptor { private URL url; public URLImageDescriptor(URL url) { this.url = url; } public URLImageDescriptor(String urlString) throws MalformedURLException { this(new URL(urlString)); } public URLImageDescriptor(URI uri) throws MalformedURLException { this(uri.toURL()); } @Override public String toString() { return "[URLImageDescriptor @ " + getUrl() + "]"; } public URL getUrl() { return url; } @Override public ImageData getImageData() { try { return getImageData(getUrl()); } catch (Exception e) { return null; } } public ImageData getImageData(URL url) { InputStream inputStream = null; try { inputStream = url.openStream(); return new ImageData(inputStream); } catch (MalformedURLException e) { throw new IllegalArgumentException(e); } catch (IOException e) { throw new IllegalArgumentException(e); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { } } } } }
An alternative way to add images from other plugins could be to use PlatformGraphicsAlgorithms as described in http://www.eclipse.org/forums/index.php/mv/msg/389722/934103/#msg_934103
(In reply to comment #5) > An alternative way to add images from other plugins could be to use > PlatformGraphicsAlgorithms as described in > http://www.eclipse.org/forums/index.php/mv/msg/389722/934103/#msg_934103 Wouldn't that require the plugin providing the editor to also provide an extension point for registering image providers? The plugin providing the images won't be providing a DiagramTypeProvider. Some background: My specific usecase relates to adding support for a custom task into the BPMN2 modeller. To support this today, I provide a documentTypeProvider extension that defines no class. This works, but it's definitely a hack. My suggestion about create a documentTypeExtension, was to create an interface for adding features to an existing document type provider, without overriding the type provider itself (i.e. use all the existing code that processes the type provider, but igonore class and name attributes).
Just to a bit more specific about how to use PlatformGraphicsAlgorithms. I created a IGraphicsAlgorithmRendererFactory that retrieved the image from the id using ImageDescriptor's createFromURL method (jface has an internal ImageDescriptor class for URLs, and this is how you access it). From this I created an ImageFigure that I then wrapped in a Shape implementing IGraphicsAlgorithmRenderer. Since you can use platform:/plugin/ urls, this supports getting images from any plugin. Here's the essential code: public IGraphicsAlgorithmRenderer createGraphicsAlgorithmRenderer(IRendererContext rendererContext) { String imageKey = rendererContext.getPlatformGraphicsAlgorithm().getId(); ImageRegistry imageRegistry = Activator.getDefault().getImageRegistry(); Image image = imageRegistry.get(imageKey); if (image == null) { imageRegistry.put(imageKey, ImageDescriptor.createFromURL(new URL(imageKey))); image = imageRegistry.get(imageKey); } IFigure figure = null; if (image != null) { figure = new ImageFigure(image); else { figure = new Label(id); } return new GenericShapeRenderer(figure); } public class GenericShapeRenderer extends Shape implements IGraphicsAlgorithmRenderer { public GenericShapeRenderer(IFigure... childFigures) { for (IFigure childFigure : childFigures) { add(childFigure); } } @Override public void setBounds(Rectangle rect) { super.setBounds(rect); for (Object figure : getChildren()) { if (figure instanceof IFigure) { ((IFigure) figure).setSize(rect.width, rect.height); } } } @Override protected void fillShape(Graphics graphics) { } @Override protected void outlineShape(Graphics graphics) { } }
(In reply to comment #4) > If you could support general URLs including platform:/plugin and > platform:/resource instead of just paths relative to a plugin, I think most > of these use cases would be handled. E.g. if you create your own extension > point that lets other plugins provide new images, you can add a > platform:/plugin/<bundleId> prefix to provided plugin-relative path. > > This means the implementation may need a more powerful ImageDescriptor, in > general reading from a URL. Here's one: > The current implementation, without any change, already supports arbitrary URLs, including platform:/plugin, platform:/resource, and Bundle.getEntry urls. You can try any of this options: URI myURI = URI.createPlatformPluginURI("plugin-id/icons/toad.png", true); addImageFilePath("Toad", myURI.toString()); URI myURI2 = URI.createPlatformResourceURI("/Test/Frog.png", true); addImageFilePath("Frog", myURI2.toString()); Bundle bundle = Platform.getBundle("plugin-in"); addImageFilePath("bird", bundle.getEntry("icons/bird.jpg").toString()); IResource res = ResourcesPlugin.getWorkspace().getRoot().findMember("/Test/mouse.png"); addImageFilePath("mouse", res.getLocationURI().toString()); Please note that URI in the two first examples is org.eclipse.emf.common.util.URI, from the EMF Framework, not the java.net one.
Bug 396247 has been filled to review the relation between DTPs and ImageProviders. Further improvements will be tracked there. If the workaround of comment #8 doesn't cover your use cases, please comment in the new bug.
With Felix comment on this I would close this with worksforme rather than fixed, as no changes have been done.