Eclipse Plug-in Development:Beginner's Guide(Second Edition)
上QQ阅读APP看书,第一时间看更新

Time for action – using Images in JFace

The TimeZoneLabelProvider can return an SWT Image. Although they can be loaded dynamically as in the previous chapter, JFace provides a number of resource registries that can be used to manage a set of resources for the application. Standard registries include the ImageRegistry, FontRegistry, and ColorRegistry classes. The purpose of a resource registry is to maintain a list of Resource instances and ensure that they are correctly disposed when they are no longer needed.

JFace has a set of these global registries; but there are specific ones, for example, those used by the IDE to maintain folder and file type icons. These use resource descriptors as a lightweight handle for the resource, and a means to acquire an instance of the resource based on that descriptor. The returned resource is owned by the registry, and as such, should not be disposed by clients that acquire or use them.

Standard images can be acquired by injecting an ISharedImages instance from the org.eclipse.ui package.

  1. In the TimeZoneTreeView class, add an injected field of type ISharedImages:
    @Inject
    private ISharedImages images;
  2. Update the call in the create method where the TimeZoneLabelProvider is instantiated to pass the shared images as an argument:
    treeViewer.setLabelProvider(new TimeZoneLabelProvider(images));
  3. In the TimeZoneLabelProvider class, create a constructor that takes the ISharedImages instance and save it as a private final field:
    private final ISharedImages images;
    public TimeZoneLabelProvider(ISharedImages images) {
      this.images = images;
    }
  4. Add a method called getImage that uses the images to provide the folder icon:
    public Image getImage(Object element) {
      if (element instanceof Map.Entry) {
        return images.getImage(ISharedImages.IMG_OBJ_FOLDER);
      } else {
        return super.getImage(element);
      }
    }
  5. Run the Eclipse instance, and open the Time Zone Tree View. A folder icon will be shown for each of the time zones in the view. The Image doesn't need to be disposed because it's owned by the provider of the ISharedImages instance (the images are disposed when the PlatformUI shuts down):
  6. To use a different image, either the global ImageRegistry from JFaceRegistry can be used, or one can be created. Although using the global one will work, it means that effectively the Image never gets disposed, since the JFaceRegistry will last for the lifetime of the Eclipse instance.

    Instead, create a LocalResourceManager instance based on the JFaceResources resource manager, which is tied to the lifetime of the parent. When the parent control is disposed, the images will be disposed automatically. These should be added to the create method of TimeZoneTreeView:

    public void create(Composite parent) {
      ResourceManager rm = JFaceResources.getResources();
      LocalResourceManager lrm = new LocalResourceManager(rm, parent);
  7. Using the LocalResourceManger, create an ImageRegistry and add an ImageDescriptor from a URL using the createFromURL method:
    ImageRegistry ir = new ImageRegistry(lrm);
    URL sample = getClass().getResource("/icons/sample.gif");
    ir.put("sample", ImageDescriptor.createFromURL(sample));
  8. Now that the ImageRegistry is populated, it must be connected up to the LabelProvider so that it can show the right image on demand. Pass the image registry into the constructor of the TimeZoneLabelProvider:
    treeViewer.setLabelProvider(new TimeZoneLabelProvider(images, ir));
    
  9. Modify the constructor in the TimeZoneLabelProvider to store the ImageRegistry, and use it to acquire the image in the getImage call:
    private final ImageRegistry images:
    private final ImageRegistry ir;
    public TimeZoneLabelProvider(ISharedImages images, ImageRegistry ir) {
      this.ir = ir;
    }
    public Image getImage(Object element) {
      if (element instanceof Map.Entry) {
        return images.getImage(ISharedImages.IMG_OBJ_FOLDER);
      } else if (element instanceof ZoneId) {
        return ir.get("sample");
      } else {
        return super.getImage(element);
      }
    }
  10. Now, when opening the view, the sample gif is used as a zone icon:

What just happened?

To start with, standard images (injected from the PlatformUI plug-in) were used, with pre-defined descriptors from ISharedImages. The names of the descriptors begin with IMG, and then follow a pre-defined pattern:

  • etool: Enabled toolbar icons
  • dtool: Disabled toolbar icons
  • elcl: Enabled local toolbar icons
  • dlcl: Disabled local toolbar icons
  • dec: Decorator
  • obj and objs: Objects such as file and folder icons

The ISharedImages instance was injected into the part since the @Inject annotation was present on the field. When E4 instantiates the view, it ensures that all the @Inject dependencies are satisfied.

Note

What is injection?

JSR 330 standardized dependency injection annotations in the javax.inject package, the most common of which is @Inject. Dependency injection systems provide a Don't call us, we'll call you approach to wiring together dependent components, and this separates the component's implementation from knowing how to satisfy its own dependencies.

By annotating a field or method with @Inject, when the class is instantiated by the dependency injection framework (E4 in this case), it will try and satisfy all the required dependencies (since this is achieved with introspection, the fields don't have to be exposed with setters or getters). It is the job of the dependency injection framework to know how to acquire or create the required dependencies; all the client code is concerned with is that there is a dependency injected into the code.

The net effect in this case is that when the TimeZoneTreeViewer is instantiated, the dependency injection mechanism will automatically insert an appropriate ISharedImages instance that has been acquired from elsewhere.

To use custom images instead, an ImageRegistry was created, backed by a LocalResourceManager. When a Control is passed into the constructor, it registers itself as a DisposeListener—so that when the control is disposed, so are the associated images. This also makes the code cleaner, because the ImageRegistry can be passed into the TimeZoneContentProvider.

Finally, the ImageRegistry was initialized with a set of ImageDescriptor objects—in this case, the icons/sample.gif that came from the new plug-in project wizard. The same key is used when both initializing and accessing the image. Some Eclipse projects follow a convention of having an ISharedImages interface with a set of constants. Other plug-ins have a similar set of images, such as JDT UI, which adds icons for packages, classes, methods, and fields.