Copyright © 2001, 2002 Object Technology International, Inc.
 Eclipse Corner Article

Using Images in the Eclipse UI

Summary
Managing images in a large graphical application can be a daunting task. Since modern operating systems such as Windows® only support a small number of images in memory at once, an application’s icons and background images must be carefully managed and sometimes shared between widgets. This article describes the image management facilities provided by the Eclipse Platform, along with some best practice guidelines to keep in mind when writing your own Eclipse UI plug-ins. We assume the reader already has a basic understanding of Eclipse, the UI extension points defined by the Eclipse Platform, and the Standard Widget Toolkit (SWT).

By John Arthorne, OTI
April 20, 2001; updated September 12, 2002 for Eclipse 2.0

The Usual Suspects

We begin by describing the interesting classes to know about when writing UI plug-ins that define their own images. Just about anybody writing substantial UI components for a plug-in needs to understand these basic image management objects.

As a general rule of thumb, ImageDescriptors should be used wherever possible in your UI code.  However, in places where you are creating real widgets, such as subclasses of org.eclipse.swt.widgets.Widget, or org.eclipse.jface.window.Window, you need to be manipulating SWT images.  Generally, the code that creates the widget should also create the Images to go in it, and the code that disposes the widget should also dispose of its images.  An exception to this rule is if you are using Images that are shared between multiple widgets.  Image sharing should be approached with caution, because as we discussed earlier, sharing makes it difficult to figure out when to dispose of the images.  org.eclipse.jface.viewers.ILabelProvider (described later), and org.eclipse.jface.resource.ImageRegistry, are two mechanisms provided by JFace that help to achieve image sharing in a controlled manner.

Defining Images Through Your plugin.xml File

The simplest and most frequent way images get added to a UI plug-in is via their plugin.xml file.  Many UI extension points allow an image filename to be provided directly in your extension definition, namely: For all of these element types, the way you specify images is the same.  There is an attribute called "icon", whose value must be an image filename relative to your plug-in's install location.  To give a quick example, here is an XML definition of an import wizard, taken from the extension point documentation:
<extension 
   point="org.eclipse.ui.import"> 
   <wizard 
      id="com.xyz.ImportWizard1"      
      name="XYZ Web Scraper"      
      class="com.xyz.imports.ImportWizard1"      
      icon="./images/import1.gif">      
   </wizard> 
</extension>    

Here, the wizard is given an image called "import1.gif", found in a subdirectory of the plug-in's base directory called "images".  See the documentation for each of the above extension points for more details and examples of their use.

There are many advantages to specifying images directly in your XML file, over defining images programmatically in your UI code:

Adding Images to Custom UI

You have begun experimenting with platform extension points, maybe contributing some actions to popup menus or toolbars, or maybe you've even defined your own custom view. In many of these cases, specifying an image file in the XML file for your plug-in is all that is required, and the Platform takes care of the rest.  However, when you move on to writing more advanced UI components, you may need to define images from within your code.  The following example demonstrates the simplest way to define images within UI code. In this example we have a UI plug-in called MyPlugin, and some action called MyAction. We would like to associate an image with MyAction.

Step 1: Create an image using your favorite image editing program. For action icons, the recommended format is a 16x16 pixel image in the GIF format. For this example, we'll call the image "my_action.gif", and place it in a subdirectory of the plug-in base directory called "images".

Step 2: Define an ImageDescriptor in your MyAction class. It's a good idea to make this field static so that it can be shared across instances of the action.

public class MyAction extends Action {
  private static ImageDescriptor image;
  static {
    URL url = null;
    try {
    url = new URL(MyPlugin.getInstance().getDescriptor().getInstallURL(),
                  "images/my_action.gif");
    } catch (MalformedURLException e) {
    }
    image = ImageDescriptor.createFromURL(url);
  }
	  ... other methods and fields defined here ...
}
  

Because plug-ins can be stored anywhere, we use URLs to describe their location. The image location will always be the same relative to the plug-in location, so we use that as the base of the image's URL. We then use the factory method defined on the ImageDescriptor class to create our descriptor. The method ImageDescriptor#createFromURL() will handle the case where a null URL is passed to it, so we don't need to handle the possible URL exception. Images that could not be loaded are visible in the UI as small red squares. If you see a red square next to your action in the popup menu, it probably means you got the image location wrong, or the image is missing.

Step 3: Set the ImageDescriptor as the action's image in the constructor:

public MyAction() { 
    super("My Action", image);
}

That's it! Now, when you startup the Eclipse UI and find your action in a menu or toolbar, your image will appear next to it. The same approach can be used to define images within custom views and wizards.  Note that since you only had to deal with ImageDescriptors in this example, no disposal was required.  In this case the platform is responsible for creating, managing, and disposing any SWT Image it creates from the image descriptor you supplied.

Viewers and Label Providers

Adding images to actions and view titles is fairly simple in Eclipse. In these cases, the platform handles the creation and management of the real, underlying SWT image. When you start to add your own objects to viewers, things get a little bit more complicated. This is mainly because many objects in viewers tend to have the same icon. For example, all files in the Navigator have the same icon, as do all public Java fields in the Java Development Tooling (JDT) views. To make this efficient, we want to reuse the same SWT image object for all these items. This is accomplished by creating a label provider for your viewer.

You can think of label providers as containing a pool of images that lasts for the lifetime of a particular viewer.  They allow the same SWT Images to be reused throughout the time that viewer's widget is open.  Note that ILabelProviders don't provide a way to share the same Image across multiple viewers, which is fine because there are rarely more than a couple of viewers open at any given time that could share images anyway.  Generally, sharing images across multiple viewers introduces a great deal of complexity for very little gain.

To give you an idea of how ILabelProviders work, it is instructive to step through the lifetime of an ILabelProvider instance.

Now that you have an idea of how label providers are created and used, let's take a look at how to implement one for your viewer.  ILabelProvider defines the following method for creating images:
public Image getImage(Object object);

This method is called by the viewer framework for each item in the viewer. As a silly example, let's say you have a fruit view that wants to display various fruits. Your label provider might look as follows:

public class FruitLabelProvider extends LabelProvider {
    private Image appleImage = new Image(…);
    private Image kiwiImage = new Image(…);
    public Image getImage(Object object) {
        if (object.getClass() == Apple.class) {
            return appleImage;
        }
        if (object.getClass() == Kiwi.class) {
            return kiwiImage;
        }
        return null;
    }
    public String getLabel(Object o) {
        //return appropriate labels for the various fruits
    }
    public void dispose() {
        appleImage.dispose();
        appleImage = null;
        kiwiImage.dispose();
        kiwiImage = null;
    }
}

Some interesting things to note about this label provider:

The ImageRegistry

The ImageRegistry is only intended to be used for Images that appear frequently (and in large numbers) in the UI. Since these high-use images need to be shared, they cannot be disposed by those using them. For this reason, the ImageRegistry is provided to automatically manage these images, and to dispose of them when the plug-in is shutdown. Many of the images used by a plug-in should not be placed in the registry, since OS limitations on the number of images in memory can easily be reached. Lower-frequency images should instead be managed by having a table of ImageDescriptor instances in your plug-in, and to create new images each time they are needed from the information in the table of descriptors.

The easiest way to use an ImageRegistry in your plug-in is to make your Plugin class a subclass of org.eclipse.ui.plugin.AbstractUIPlugin.  This class creates an ImageRegistry automatically (one per plug-in instance), if requested using the getImageRegistry() method.  This registry can then be populated with ImageDescriptors in your plug-in by overriding the initializeImageRegistry(ImageRegistry) method.  The registry will then lazily generate and manage SWT images as they are requested.  The registry knows how to dispose of itself when the UI shuts down.

It is possible to create instances of ImageRegistry manually, without using the AbstractUIPlugin's mechanism. However, this should be done with caution.  An ImageRegistry, once created, cannot be closed until the whole UI shuts down.  They should only be created and used if you genuinely have need for images that last for the whole lifetime of your plug-in.

Using Global Images Provided by Other Plug-ins

Some plug-ins may expose their global images so that other plug-ins can make use of them.  Not only does this improve efficiency by allowing a single Image instance to be used across several plug-ins, it also helps to provided a consistent and integrated feel to the workbench.  If a file, folder or bookmark looked different from plug-in to plug-in it would result in a confusing experience for end-users.

The technique used for making Images available in a plug-in's API may vary between plug-ins.  The Platform UI provides an interface called org.eclipse.ui.ISharedImages, accessible from the workbench.  This interface supplies images and image descriptors corresponding to a set of keys defined in ISharedImages.  See the JavaDoc for that class for descriptions of all the available methods.  To give an example, say you want to obtain the image for a file object.  You would invoke the following:

PlatformUI.getWorkbench().getSharedImages().getImage(ISharedImages.IMG_OBJ_FILE);

The workbench is also accessible from AbstractUIPlugin, which your plug-in class will typically subclass.  Other plug-ins use a similar approach for sharing their images.  For example, the Java Development Tooling supplies org.eclipse.jdt.ui.SharedImages in its API.

Important: Images provided by these APIs are shared, which means you must not dispose of them.  Again, following the general rule, since you didn't create the image, it's not your responsibility to dispose of it.

If your plug-in defines images that may be useful to others, you may want to use a similar approach to make them available in your API.  Keep in mind that the number of SWT Images shared by your plug-in should be small, since these images will have to stay around for the lifetime of your plug-in.

Where Else Can I Look For Information About Images?

If this article has left you scratching your head, don't worry.  Eclipse has many layers of complexity, and it can take some time to wrap your head around the issues involved.  The best thing to do is start with one of the examples, such as the readme example, and see what it's doing.  The extension point documentation is an excellent place to learn about the various ways the base UI can be extended.  Also, the entire Platform has extensive JavaDoc that describes each class and method in detail, which makes it easy to just dive in and starting learning how it all works.  And of course, the Eclipse Corner forums can be used to pose questions and get answers from experienced Eclipse developers.

Java and all Java-based trademarks and logos are trademarks or registered trademarks of Sun Microsystems, Inc. in the United States, other countries, or both.

Microsoft, Windows, Windows NT, and the Windows logo are trademarks of Microsoft Corporation in the United States, other countries, or both.