The Wayback Machine - https://web.archive.org/web/20091221030013/http://www.ibm.com:80/developerworks/opensource/library/os-ecgui2/
Skip to main content

Using the Eclipse GUI outside the Eclipse Workbench, Part 2: Using the JFace image registry

Adding icons to a simple file explorer application

Adrian Emmenis (van@vanemmenis.com), Independent consultant
Photo of author
A. O. Van Emmenis is an independent consultant, specializing in Java/J2EE training and consulting, based in Cambridge, UK. Van has worked in the software industry for 20 years or so. He first started working with objects using Smalltalk in the CAD industry and now works mainly in Java. He is particularly interested in Agile methods and GUI design. You can contact Van at van@vanemmenis.com.

Summary:  In this article, A.O. Van Emmenis continues with the example started in Part 1 of this "Using the Eclipse GUI outside the Eclipse Workbench" series. He cleans up the content and label providers and shows how to use sorting and filtering on JFace viewers. He shows how to add a status line to the window, add icons to both viewers, and save system resources by using the JFace image registry.

View more content in this series

Date:  14 Feb 2003
Level:  Intermediate
Activity:  5242 views
Comments:  

In Part 1 of this series, I started an example that subclassed the JFace application window and used a tree viewer and a table viewer to display folders and files. This time, we'll tidy up some loose ends with the previous example and add a status line to the window. The big change we're going to make is to add icons to the viewers and learn about the JFace image registry. Finally, we'll use sorting and filtering on the viewers to give us a more authentic "file explorer" style.

Installation notes

Download the source code from Part 1, but take my system setup into account:

  • Windows® 2000
  • Eclipse, stable build M3 (15 Nov 2002)
  • Eclipse installed in C:\eclipse-2.1.0

I will leave you to do any swizzling of names and file separators in what follows, so the programs work correctly on your system.

Build/Run instructions

You need these JAR files on your classpath:

                
C:\eclipse-2.1.0\plugins\org.eclipse.jface_2.1.0\jface.jar
C:\eclipse-2.1.0\plugins\org.eclipse.runtime_2.1.0\runtime.jar
C:\eclipse-2.1.0\plugins\org.eclipse.swt.win32_2.1.0\ws\win32\swt.jar
C:\eclipse-2.1.0\plugins\org.eclipse.ui.workbench_2.1.0\workbench.jar
C:\eclipse-2.1.0\plugins\org.eclipse.core.runtime_2.1.0\runtime.jar

Ensure that the Java™ VM picks up the correct shared libraries for the GUI you are using at run time by running it with the following argument:

  -Djava.library.path=C:\eclipse-2.1.0\plugins\org.eclipse.swt.win32_2.1.0\os\win32\x86\
		

Finally, run the programs from the folder that contains the icons folder, so that the examples can find the GIFs containing the icons.


Picking up the example from Part 1

At the end of the last article, our Explorer application looked like Figure 1.


Figure 1. Explorer (version 4)
Figure 1.  Explorer (version 4)

Folders and files are displayed in the left pane using a tree viewer. When you select a folder in the left pane, the files it contains are shown in a table viewer in the right pane. Let's start our tidying by setting the window title.


Setting a window title

This is another case where JFace does not try to hide Standard Widget Toolkit (SWT) from you. You must get at the underlying SWT Shell widget and set its title.

We ask a JFace window for its SWT shell using getShell(), and we set the title of a shell using setText(). So, inside the Explorer createContents() method, we're going to say something like this: getShell().setText("JFace File Explorer");.

We have one column in our table view. Let's add another that shows the size of the file in bytes. We'll right-justify the text in the column. This code is similar to the first column, so I won't show it here.

Now that we have two columns, we'll set the selection style of the table viewer to FULL, which means that the whole row will be highlighted when selected.

new TableViewer(sash_form, SWT.BORDER | SWT.FULL_SELECTION);
            


The status line

A status line is actually an SWT composite control looked after by the class StatusLineManager that can contain other controls. Typically, these will be read-only text controls showing status information, but you can also ask for a temporary progress monitor to be displayed there. The status line knows it has a standard message and sometimes an error message to display. The ApplicationWindow method setMessage(String) is actually just a shortcut for getStatusLineManager().setMessage(String).


Adding a status line to an application window

The status line is another optional component of an application window. We must ask the window to create a status line before it creates the SWT widgets by using addStatusLine().

Let's use the status line to display how many items we have selected in the table view. Every time the selection changes in the table view, we'll update the status line and show how many items are selected, as shown in Listing 1.


Listing 1. Explorer — Setting the text in the status line
                
tbv.addSelectionChangedListener(new ISelectionChangedListener()
{
  public void selectionChanged(SelectionChangedEvent event)
  {
    IStructuredSelection selection =
      (IStructuredSelection) event.getSelection();

    setStatus("Number of items selected is " + selection.size());
  }
});

By default, a table viewer is set to single select mode. We'll change it to multiselect by adding it as another bit (literally) of the style argument when we create the table viewer: new TableViewer(sash_form, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);.

Putting it all together gives us Listing 2.


Listing 2. Explorer (version 5)
                
import java.io.*;

import org.eclipse.jface.viewers.*;
import org.eclipse.jface.window.*;
import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.widgets.*;

public class Explorer extends ApplicationWindow
{
  public Explorer()
  {
    super(null);
    addStatusLine();
  }

  protected Control createContents(Composite parent)
  {
    getShell().setText("JFace File Explorer");
    SashForm sash_form = new SashForm(parent, SWT.HORIZONTAL | SWT.NULL);

    TreeViewer tv = new TreeViewer(sash_form);
    tv.setContentProvider(new FileTreeContentProvider());
    tv.setLabelProvider(new FileTreeLabelProvider());
    tv.setInput(new File("C:\\"));

    final TableViewer tbv =
      new TableViewer(sash_form, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
    tbv.setContentProvider(new FileTableContentProvider());
    tbv.setLabelProvider(new FileTableLabelProvider());

    TableColumn column = new TableColumn(tbv.getTable(), SWT.LEFT);
    column.setText("Name");
    column.setWidth(200);

    column = new TableColumn(tbv.getTable(), SWT.RIGHT);
    column.setText("Size");
    column.setWidth(100);

    tbv.getTable().setHeaderVisible(true);

    tv.addSelectionChangedListener(new ISelectionChangedListener()
    {
      public void selectionChanged(SelectionChangedEvent event)
      {
        IStructuredSelection selection =
          (IStructuredSelection) event.getSelection();

        Object selected_file = selection.getFirstElement();
        tbv.setInput(selected_file);
      }
    });

    tbv.addSelectionChangedListener(new ISelectionChangedListener()
    {
      public void selectionChanged(SelectionChangedEvent event)
      {
        IStructuredSelection selection =
          (IStructuredSelection) event.getSelection();

        setStatus("Number of items selected is " + selection.size());
      }
    });

    return sash_form;
  }

  public static void main(String[] args)
  {
    Explorer w = new Explorer();
    w.setBlockOnOpen(true);
    w.open();
    Display.getCurrent().dispose();
  }
}

If we run this, we get Figure 2.


Figure 2. Explorer (version 5)
Figure 2.  Explorer (version 5)

As you can see, the size column isn't right at the moment. We'll fix that soon.


Icons and images

In SWT, the object used to represent an icon is Image. When you are directly creating an SWT widget that can display an image, you can set it using setImage(Image).

There is an issue with images: They are a limited resource. They are relatively heavyweight objects with references to external OS resources (Figure 3). On some operating systems, there is a strict limit on the number of images you can have at any one time, so you must be aware of this restriction when using them.


Figure 3. Image descriptors, images, and resources in the operating system
Figure 3. Image    descriptors, images and resources in the  OS

Eventually, resources used by Images are freed when your application exits, but even then, there may be issues on some operating systems with shared libraries causing resources to be retained. For various reasons, you cannot rely on the Java garbage collector to clean these things up for you. For a full explanation of the SWT, see Resources.

The good news is that images can be shared between widgets, and SWT has provided Image with a dispose() method, which frees the resources used by an image.

Because widgets can share images, SWT has made a design decision that it won't dispose of them automatically when a widget no longer exists (in other words, when a window is closed). However, JFace gives you some help by telling you when large UI objects like windows and viewers can have their images disposed of, and it is then up to you to dispose of any images you have created.

In practice, most applications will obtain their icons from files or a database containing data in some standard format like GIF or JPG. To do this, JFace provides ImageDescriptor, which is a lightweight object that doesn't store the image itself, but can create a particular image on demand. This class has a number of subclasses that can construct images from different sources.

In our case, we are storing our icons in GIFs in an icons folder, so we'll use this factory method in ImageDescriptor:

public static ImageDescriptor createFromURL(URL url)

A TableViewer gets an element's icon in much the same way that it gets the element's text: It asks the label provider. What we have to do is implement this method from ITableLabelProvider:

public Image getColumnImage(Object element, int columnIndex)

To help us manage the sharing and disposal of the images, we're going to use an ImageRegistry.


The JFace ImageRegistry

Managing a set of shared, expensive resources is a classical software engineering problem. JFace provides the classical solution: a central shared registry that caches images and image descriptors.

The idea is that your code obtains the image descriptors it needs and adds them to the image registry, indexing each one with a key. When you want to get an image, you fetch it from the registry using its key.

An image registry will take care of disposing of its images when the top-level display is disposed of. If you need to dispose of images more often than that, you might want to create several image registries and dispose of the images directly as required. For more details, read the article on the Eclipse Web site on using images.

Our little example will only be using a handful of icons, so we'll use a single image registry. Let's look at the table viewer first.

Adding icons to the file TableViewer

To create an image descriptor from a file called icons\file.gif we use this: image_descriptor = ImageDescriptor.createFromURL(new URL("icons/file.gif"));. Having gotten the image descriptor, we can store it in the image registry. In this example, we are using the string "file" as the key: image_registry.put("file", image_descriptor);. Then, to get that image again, we can say image = image_registry.get("file");.

The constructor for URLs throws a checked exception. Well, all the URLs we'll be using are going to be hard-coded, so we'll wrap the creation of the URL in some utility code to turn the checked exception into a run-time exception. Also, we want to share these images throughout our code, so we need to create the image registry somewhere central and make it globally accessible.

Time to invent a utility class, as shown in Listing 3.


Listing 3. Util (version 1)
                
import java.net.*;

import org.eclipse.jface.resource.*;

public class Util
{
  private static ImageRegistry image_registry;

  public static URL newURL(String url_name)
  {
    try
    {
      return new URL(url_name);
    }
    catch (MalformedURLException e)
    {
      throw new RuntimeException("Malformed URL " + url_name, e);
    }
  }

  public static ImageRegistry getImageRegistry()
  {
    if (image_registry == null)
    {
      image_registry = new ImageRegistry();
      image_registry.put(
        "folder",
        ImageDescriptor.createFromURL(newURL("file:icons/folder.gif")));
      image_registry.put(
        "file",
        ImageDescriptor.createFromURL(newURL("file:icons/file.gif")));
    }
    return image_registry;
  }
}

The getImageRegistry() method lazily creates the image registry and adds two image descriptors. We need to change FileTableLabelProvider to return the correct image, depending on whether the element is a file or a folder, as shown in Listing 4.


Listing 4. FileTableLabelProvider (version 2)
                
import java.io.*;

import org.eclipse.jface.viewers.*;
import org.eclipse.swt.graphics.*;

public class FileTableLabelProvider implements ITableLabelProvider
{
  public String getColumnText(Object element, int column_index)
  {
    if (column_index == 0)
    {
      return ((File) element).getName();
    }

    if (column_index == 1)
    {
      return "" + ((File) element).length();
    }

    return "";
  }

  public void addListener(ILabelProviderListener ilabelproviderlistener)
  {
  }

  public void dispose()
  {
  }

  public boolean isLabelProperty(Object obj, String s)
  {
    return false;
  }

  public void removeListener(ILabelProviderListener ilabelproviderlistener)
  {
  }

  public Image getColumnImage(Object element, int column_index)
  {

    if (column_index != 0)
    {
      return null;
    }

    if (((File) element).isDirectory())
    {
      return Util.getImageRegistry().get("folder");
    }
    else
    {
      return Util.getImageRegistry().get("file");
    }
  }
}

Since the table now has two columns (we added size earlier), we also have to adjust getColumnText(Object,int) to do the right thing. This gives us Figure 4.


Figure 4. Explorer (version 6)
Figure 4.  Explorer (version 6)

It is starting to look quite colorful — at least in the table viewer. Let's upgrade FileTreeLabelProvider to use images, as shown in Listing 5.


Listing 5. FileTreeLabelProvider (version 2)
                
import java.io.*;

import org.eclipse.jface.viewers.*;
import org.eclipse.swt.graphics.*;

public class FileTreeLabelProvider extends LabelProvider
{
  public String getText(Object element)
  {
    return ((File) element).getName();
  }

  public Image getImage(Object element)
  {
    if (((File) element).isDirectory())
    {
      return Util.getImageRegistry().get("folder");
    }
    else
    {
      return Util.getImageRegistry().get("file");
    }
  }
}

This gives us Figure 5.


Figure 5. Explorer (version 7)
Figure 5.  Explorer (version 7)

Now that we can easily see the difference between files and folders, we can also see that in the table view, the default sorting algorithm has ordered them alphabetically, but mixed the folders and files together. You may also notice that, in the tree view, we are seeing both folders and files. Let's fix this.


Sorting and filtering

To sort the items in a viewer, we use a ViewerSorter. ViewerSorter is a class designed to be subclassed. The viewer uses a viewer sorter to sort its elements in two stages. First, it asks for the element's category. This returns an integer, and it sorts the elements into category groups in ascending order of their category numbers: public int category(Object element). Then, within each category, it sorts using the compare() method of ViewerSorter. This method is similar to the compare() method in the standard Java class Comparator: public int compare(Viewer viewer, Object element1, Object element2). The default is to use the string returned by the label provider and to sort ignoring the case.

Let's just implement the category in the table viewer, as shown in Listing 6.


Listing 6. FileSorter (version 1)
                
import java.io.*;

import org.eclipse.jface.viewers.*;

public class FileSorter extends ViewerSorter
{
  public int category(Object element)
  {
    return ((File) element).isDirectory() ? 0 : 1;
  }
}

This will sort folders before files. In the tree viewer, we want to display only the folders. We can do this by using a ViewerFilter (see Resources). Just like sorters, filters are designed to be subclassed. We need to implement the select() method, which looks at an element and returns true if it is to be displayed. In this case, we want to allow only folders to get through the filter, so here's our AllowOnlyFoldersFilter() method in Listing 7.


Listing 7. AllowOnlyFoldersFilter (version 1)
                
import java.io.*;

import org.eclipse.jface.viewers.*;

public class AllowOnlyFoldersFilter extends ViewerFilter
{
  public boolean select(Viewer viewer, Object parent, Object element)
  {
    return ((File) element).isDirectory();
  }
}

Note that the element we are filtering is the third argument. We are given the viewer and the parent — just in case we need to access them.

Now, we just need to attach instances of these classes to the viewers, as shown in Listing 8.


Listing 8. Explorer (version 8)
                
import java.io.*;

import org.eclipse.jface.action.*;
import org.eclipse.jface.viewers.*;
import org.eclipse.jface.window.*;
import org.eclipse.swt.*;
import org.eclipse.swt.custom.*;
import org.eclipse.swt.widgets.*;

public class Explorer extends ApplicationWindow
{
  ...
  protected Control createContents(Composite parent)
  {
    getShell().setText("JFace File Explorer");
    SashForm sash_form = new SashForm(parent, SWT.HORIZONTAL | SWT.NULL);

    TreeViewer tv = new TreeViewer(sash_form);
    tv.setContentProvider(new FileTreeContentProvider());
    tv.setLabelProvider(new FileTreeLabelProvider());
    tv.setInput(new File("C:\\"));
    tv.addFilter(new AllowOnlyFoldersFilter());

    final TableViewer tbv =
      new TableViewer(sash_form, SWT.BORDER | SWT.FULL_SELECTION | SWT.MULTI);
    tbv.setContentProvider(new FileTableContentProvider());
    tbv.setLabelProvider(new FileTableLabelProvider());
    tbv.setSorter(new FileSorter());
     ...
  }

Note that, as the names of the methods suggest, a viewer can have several filters at a time but only one sorter.

Running Explorer now us gives Figure 6.


Figure 6. Explorer (version 8)
Figure 6. Explorer (version 8)

Conclusion

Well, those icons have certainly brightened things up a bit. We've got a window title telling us what we're looking at and a spiffy new status line telling us how many items we have selected and, with filtering and sorting separating the files and folders neatly, it's beginning to look like a real file explorer.

It may be starting to look pretty, but it isn't doing much right now. In Part 3 of this three-part "Using the Eclipse GUI outside the Eclipse Workbench" series, we fix that by adding menus and actions. We'll see how to create bar menus, toolbars, and pop-up menus. We develop some examples where those menus use some neat JFace utilities to launch programs and access the system clipboard, and we show how to use listeners to make the menu items context-sensitive.


Resources

Learn

Get products and technologies

Discuss

  • The Eclipse Platform newsgroups should be your first stop to discuss questions regarding Eclipse. (Selecting this will launch your default Usenet news reader application and open eclipse.platform.)

  • The Eclipse newsgroups has many resources for people interested in using and extending Eclipse.

  • Participate in developerWorks blogs and get involved in the developerWorks community.

About the author

Photo of author

A. O. Van Emmenis is an independent consultant, specializing in Java/J2EE training and consulting, based in Cambridge, UK. Van has worked in the software industry for 20 years or so. He first started working with objects using Smalltalk in the CAD industry and now works mainly in Java. He is particularly interested in Agile methods and GUI design. You can contact Van at van@vanemmenis.com.

Comments



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source, Java technology
ArticleID=10755
ArticleTitle=Using the Eclipse GUI outside the Eclipse Workbench, Part 2: Using the JFace image registry
publish-date=02142003
author1-email=van@vanemmenis.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers