blob: 3e78a2bbf4484788ec27d482e9095f12cbfdf676 [file] [log] [blame]
<!doctype html public "-//w3c//dtd html 4.0 transitional//en">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">
<meta name="GENERATOR" content="Mozilla/4.76 [en] (Windows NT 5.0; U) [Netscape]">
<meta name="Author" content="Balaji Krish-Sampath">
<title>Understanding Decorators in Eclipse</title>
<link rel="stylesheet" href="../../default_style.css">
</head>
<body bgcolor="#FFFFFF" link="#0000FF" vlink="#800080">
<div align=right><font face="Times New Roman, Times, serif"><font size=-1>Copyright
&copy; 2003 International Business Machines Corp.</font></font></div>
<table border=0 cellspacing=0 cellpadding=2 width="100%">
<tr>
<td align=LEFT valign=TOP colspan="2" bgcolor="#0080C0"><b><font face="Arial,Helvetica" color="#FFFFFF">&nbsp;Eclipse
Corner Article</font></b></td>
</tr>
</table>
</div>
<div align="left">
<h1><img src="Idea.jpg" height=86 width=120 align=CENTER></h1>
</div>
<p>&nbsp;</p>
<h1 ALIGN="CENTER">Understanding Decorators in Eclipse</h1>
<blockquote>
<b>Summary</b>
<br>
Decorators, as the name suggests, are used for adorning/annotating resources
with useful information. Decorators can be used by plug-ins to convey more information
about a resource and other objects displayed in different workbench views.&nbsp;
This article, with the help of a simple plug-in example, will illustrate the
steps involved in decorating resources, along with some best practice approaches
for decorating resources. Finally, we will discuss performance issues that may
arise when enabling decorators, and briefly go over the new <a href="#New">Lightweight
decorators</a> found in Eclipse 2.1.
<p>We assume the reader already has a basic understanding
of Eclipse and knows how to create simple plug-ins.
<p><b>Balaji Krish-Sampath, IBM</b>
<br>
<font size=-1>January 16, 2003</font></blockquote>
</blockquote>
<hr width="100%"/>
<h2>
What are Decorators?</h2>
Decorators are visual cues that convey useful state information associated
with objects or resources displayed in Eclipse views. Many of the standard
workbench views participate in showing decorations, <font color="#000000">for
example, the navigator view, the package explorer view and the outline
view.</font> With decorators, users can get valuable information about
the resources in a particular view. The following figure illustrates a
simple custom decoration.
<center>
<p><img SRC="SimpleDecoration.gif" height=224 width=375>
<br><b><font size=-1>Fig. 1: Simple Decorator Example</font></b></center>
<p>In the Fig. 1, a lock icon is superimposed on the Java icon image (<img SRC="tag_1.gif" height=13 width=24 align=CENTER>)
of the file ImageDecoration.java. A prefix and a suffix label are added for
the file TextDecoration.java (<img SRC="tag_3.gif" height=13 width=24 align=CENTER>).
The file NoDecoration.java does not have any custom decoration (<img SRC="tag_2.gif" height=13 width=24 align=CENTER>).
<p>With the help of a simple example, this article will provide a step by step
approach to decorating resources. Since the performance of the UI is affected
by the speed with which decorations are performed, we will concentrate on the
approaches that should be followed to make decoration efficient. Decoration
can be performed on resources and other objects displayed in Eclipse views.
Although the article illustrates most of the concepts with reference to decorating
a resource, decorations are not limited to resources and same technique can
be applied to decorate all the objects displayed in Eclipse views. <br>
&nbsp;
<h2>
Decorators are Everywhere</h2>
To better understand decorators, it is important<font color="#000000">
to be able to spot some of the ba</font>sic decorations provided by Eclipse.
The introductory sentences about decorators and figure shown above (Fig. 1) might have given you the impression that decorators are just the
custom images annotating the resources with more valuable information.
However, some of the basic images that users see, like the problem marker
that is shown to alert the user about compilation errors is a good example
of basic decoration provided by Eclipse.
<p>Now that we know some of the basic decorations provided by Eclipse,
let's revisit the package explorer view.
<center>
<p><img SRC="BasicDecorations.gif" height=283 width=372>
<br><b><font size=-1>Fig. 2: Basic Decorations</font></b></center>
<p>The figure shown above clearly illustrates the wealth of information that Eclipse
provides on resources and other objects contained in the workspace.&nbsp; This
ranges from information regarding the type of resource (a file or a folder or
a project), type of file (a Java file or a text file) and various Java elements.
In the figure shown above, a problem marker is superimposed on the compilation
unit (<img SRC="tag_1.gif" height=13 width=24 align=CENTER>), Java class element
(<img SRC="tag_2.gif" height=13 width=24 align=CENTER>) and method (<img SRC="tag_3.gif" height=13 width=24 align=CENTER>).
The problem marker decoration on the compilation unit indicates that the compilation
unit named ProblemMarkerDecoration.java has compilation errors. The problem
marker decoration on the compilationErrorMethod() method (<img SRC="tag_3.gif" height=13 width=24 align=CENTER>)
indicates the method failed to compile. <font color="#000000">By viewing the
Package Explorer view with basic decorations provided by Eclipse, users can
get details like reasons for compilation errors in a Java file.</font>
<h2>
<b><font size=+2>Types of Decorations</font></b></h2>
The display of a resource in a view has two components, the label (the
name of the resource) and an image denoting the type of resource and possibly
other state information. Using the Eclipse extension mechanism, we can
<font color="#000000">change
the label of the resource and the image of the resource. Hence, we have
two different types of decorations:</font>
<ul>
<li>
Text Label Decorators</li>
<li>
Image Decorators</li>
</ul>
As the name suggests, text label decorators are decorations on resource labels
and image decorators are decorations on icon images. The resource name is the
base label provided by Eclipse. This is important because this is the base value
to be decorated and the decorator developer can assume that it will at least be
part of the input.&nbsp; Plug-in developers can augment the base resource label
with additional information.
<p>Let's revisit the Package Explorer view to understand more on text and
label decorators.
<center>
<p><img SRC="ImageAndTextDecorations.gif" height=271 width=417>
<br><b><font size=-1>Fig. 3: Image and Text Decorators</font></b></center>
<p><font color="#000000">In the figure shown above, custom decorations are applied
to the base decorations provided by Eclipse</font><font color="#FF6666">.</font><font color="#000000">
The file NoDecoration.java does not contain any contain any decoration (<img SRC="tag_1.gif" height=13 width=24>).
The file ImageDecoration.java has a lock icon (image decoration) superimposed
on the Java icon image (<img SRC="tag_2.gif" height=13 width=24>). The files
PrefixAdded.java and PrefixAndSuffixAdded.java have text decorations added to
their labels (<img SRC="tag_3.gif" height=13 width=24><img SRC="tag_4.gif" height=13 width=24>).
The file ImageAndTextDecorations.java has both image decoration (a lock icon
superimposed on the compilation unit) and text decorations (a prefix and a suffix
added to base label) (<img SRC="tag_5.gif" height=13 width=24>). The CustomDecorationsWithProblemMarker.java
has a problem marker decoration (basic decoration provided by Eclipse) as well
as custom image and text decorations (<img SRC="tag_6.gif" height=13 width=24>)</font>
<p><font color="#000000">So, how did these decorations get there? Let's
start our quest for creating custom decorators.</font>
<br>&nbsp;
<h2>
<b><font color="#000000"><font size=+2>Defining Decorators in plugin.xml</font></font></b></h2>
<font color="#000000">The first step in providing custom decoration is
to contribute to the org.eclipse.ui.decorators extension point.</font><a NAME="pluginXmlFile"></a>
<br>&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt><font color="#000000">&lt;extension point="org.eclipse.ui.decorators"></font></tt>
<br><tt><font color="#000000">&lt;decorator</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; id="com.ibm.decoratordemo.ui.decorator.demodecorator"</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; label="Decorator Example"</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; state="false"</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; class= "com.ibm.DecoratorDemo.ui.decorators.DemoDecorator"</font></tt>
<br><img SRC="tag_1.gif" height=13 width=24><tt><font color="#000000">&nbsp;
objectClass="org.eclipse.core.resources.IResource"</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; adaptable="true"></font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; &lt;description></font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; This is an
example to illustrate the use of decorators</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; &lt;/description></font></tt>
<br><tt><font color="#000000">&nbsp; &lt;/decorator></font></tt>
<br><tt><font color="#000000">&lt;/extension></font></tt></td>
</tr>
</table>
<center>
<b><font size=-1>Fig. 4: Plug-in manifest info</font></b> (<b><font size=-1>plugin.xml)</font></b>
</center>
<ul>
<li>
id - defines the id of our decorator. There may be many decorators contributed
via the decorator extension point. The decorator id must be unique.</li>
<li>
label - defines the label of the decorator for the workbench Label Decorations
Preference Page (shown in Fig. 5).</li>
<li>
state - defines the default state of the decorator. The decorator can be
enabled or disabled by default.</li>
<li>
class - class that implements the decorator. The class must implement org.eclipse.jface.viewers.ILabelDecorator
and is responsible for decorating the original label's image and text with
our own annotations.</li>
<li>
objectClass - objectClass indicates the class of resources that need to
be decorated. If the user requires decoration only on files, the objectClass
element can be changed to denote that the decoration should be performed
only on files. <font color="#000000">To provide decoration only on files,&nbsp;<img SRC="tag_1.gif" height=13 width=24>
should be changed as objectClass="org.eclipse.core.resources.IFile"</font></li>
<li>
adaptable -&nbsp; <font color="#000000">Indicates whether classes that
adapt to the resources should also be decorated. The adaptable flag holds
significance when decorations need to be performed on different elements
that conform to IResource. A detailed discussion on the use of adaptable
flag is provided later.</font></li>
<li> description - an optional sub-element which contains text providing a short
description of the decorator. This will be shown in the Label Decorations
preference page as the description of the custom decorator.</li>
</ul>
<p><br><b><font size=+2>Enabling / Disabling Custom Decorators</font></b>
<p>Individual decorators can be turned on and off from the Label Decorations preference
page. Users can access this page by selecting Window->Preferences->Workbench->Label
Decorations (<img SRC="tag_1.gif" height=13 width=24>).
<center>
<p><img SRC="LabelDecorationsPage.gif" height=360 width=533>
<br><b><font size=-1>Fig. 5: Label Decorations Preference Page</font></b></center>
<p>The name of the decorator is the value of the <i>label attribute </i>of the
decorator extension (Fig. 4) while the description is the text contained in
the <i>description</i> sub-element. In the figure shown above, the "Decorator
Example"<i> </i>decorator is turned on (<img SRC="tag_2.gif" height=13 width=24 align=CENTER>),
while the other decorations are turned off. Although the above-mentioned scenario
might be typical when the project is not shared with CVS repository, enabling
and disabling the decorators are extremely useful when the decorations performed
by two or more decorators conflict with each other. For example, the CVS plug-in
might decorate the base image by superimposing the base image with a custom
image while the "Decorate Example" plug-in might superimpose a different custom
image at the same position thereby conflicting with the CVS plug-in decoration.
<font color="#000000">If the decoration performed by two different decorators
on the same resource conflict, users should appropriately enable / disable different
decorators to get the required decoration.</font>
<p><img SRC="tip.gif" height=11 width=56> It is very important to design
custom decorators that don't conflict with basic decorations provided by
different Eclipse views. For example, the package explorer view decorates
Java files with problem markers (a problem marker is placed at the bottom
left hand corner) if there are compilation errors. It is a bad practice
to decorate resources with custom decoration exactly at the position of
a problem marker and developers should avoid this. <font color="#000000">If
the custom decoration is performed at the bottom left corner, then custom
decoration and the problem marker decoration, if any, conflict each other
and hence users will not be able to view the decorations. The solution
to the above mentioned problem is to provide a custom image decoration
at the bottom right corner which does not conflict with the basic image
decoration provided by Eclipse. The top left corner is the second best
place although it conflicts with the binary project decorator. The bottom
left and top right should be avoided as they are decorated outside of the
decorator mechanism.</font><font color="#000000"></font>
<p>Before we proceed further, let's take a closer look at the class that
provides custom decoration. As mentioned earlier, the name of the class
should be same as the value specified in the class attribute. The class
must implement ILabelDecorator.
<br>&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>// Class extends LabelProvider because LabelProvider</tt>
<br><tt>// provides methods for getting images and text labels from objects&nbsp;</tt>
<br><tt>public class DemoDecorator extends LabelProvider&nbsp;</tt>
<br><tt>&nbsp; implements ILabelDecorator</tt>
<br><tt>{</tt>
<br><tt>&nbsp; public DemoDecorator()</tt>
<br><tt>&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp; super();</tt>
<br><tt>&nbsp; }</tt>
<br><tt>&nbsp; // Method to decorate Image&nbsp;</tt>
<br><tt>&nbsp; public Image decorateImage(Image image, Object object)</tt>
<br><tt>&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp; // Return null to specify no decoration</tt>
<br><tt>&nbsp;&nbsp;&nbsp; return null;</tt>
<br><tt>&nbsp; }</tt>
<br><tt>&nbsp; // Method to decorate Text</tt>
<br><tt>&nbsp; public String decorateText(String label, Object object)</tt>
<br><tt>&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp; // return null to specify no decoration</tt>
<br><tt>&nbsp;&nbsp;&nbsp; return null;</tt>
<br><tt>&nbsp; }</tt>
<br><tt>}</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 6: Decorator Implementation class</font></b></center>
<p>The default implementation can be used as a template to get started.
The decorateImage() and decorateText() methods are used to decorate the
image and text respectively. A detailed discussion on how to decorate a
resource using these two methods is provided later.
<p><img SRC="tryit.gif" height=13 width=61> Contribute your custom decoration
via the decorator extension point in a plugin.xml manifest file (<a href="#pluginXmlFile">use
the example shown in previous section</a> ). Create a class to implement
custom decoration (Fig. 6). Note that the Java class name should be
the same as the text value of the <i>"class"</i> attribute of the decorator
tag
in plugin.xml. Compile and run. You should be able to see your custom
decoration appear inside Window->Preferences->Workbench->Label Decorations
as shown in Fig. 5.
<br>&nbsp;
<h2>
<font size=+2>Individual Preference Pages for Label Decoration</font></h2>
The label decorations preference page<font color="#000000"> provides the user
with only two options, eith</font>er to use a decorator or not. To provide users
more control over the decoration contents and use of a subset of decorations out
of a pool of decorations provided by a particular decorator, a plug-ins can provide
an individual preference page for its label decorators. For example, the CVS plug-in
provides a preference page (Window->Preferences->Team->CVS-> Label Decorations)
that allows users to control the presentation and content of CVS decorators.
<center>
<p><img SRC="CVSLabelDecorations.gif" height=437 width=665>
<br>
<b><font size=-1>Fig. 7: CVS Decoration Preference Page</font></b>
</center>
<p>The figure shown above illustrates how plug-ins can use the individual label
decorator preference page to provide users the ultimate control over the decorations.
<font color="#000000">The CVS plug-in gives a user the control over the choice
of decorations, the richness in decorating different resources and the choice
of types of resources that need to be decorated. They can even control the look
and feel of decoration (<img SRC="tag_1.gif" height=13 width=24 align=CENTER><img SRC="tag_2.gif" height=13 width=24 align=CENTER>).</font>
<p>One of the important considerations while providing custom decorations
is the performance of UI with and without decorators. Users can effectively
use the individual decorator preference page to avoid decorations that
are expensive to compute and thus enhance the performance.
<p><img SRC="tip.gif" height=11 width=56> An individual decoration preference
page is extremely useful when resources are decorated on receiving external
events / notification. For example, a repository provider plug-in (e.g. org.eclipse.team.cvs.ui)
might be collaborating with an external server to decorate resources with a
lock icon, whenever resources are checked out by some other user. The repository
provider plug-in might be listening to thousands of events and it would be better
if the user has ultimate control on what to decorate (and what not to).
<p>Since individual decoration preference pages are preference pages that are
used to provide control to the users on decorations, they should be contributed
via the preference pages extension point. Users can refer to the articles <i><a href="../Article-Preferences/preferences.htm">Preferences
in the Eclipse Workbench UI</a></i>&nbsp; by Tod Creasey and <i><a href="../Article-Field-Editors/field_editors.html">Simplifying
Preference Pages with Field Editors</a></i> by <font color="#000000">Ryan Cooper
to learn more about preference pages and ways to create them.</font>
<h2>
<b>Example Plug-in Introduction</b></h2>
Now that we know how to declare a custom label decoration, and contribute
individual label decoration preference pages,<font color="#000000"> let's
dive into our Example Plug-in.</font>
<p>The basic idea behind providing this example is to illustrate how to
decorate images and text. To keep it as simple as possible, users are pro<font color="#000000">vided
with a custom File Property page. The file property page has a custom page
"DecoratorDemo File Property page" that provides a control for the users
to set the "Busy" property (The "Busy" property indicates that the resource
is busy and hence should not be modified)</font>. The page also provides
controls for users to set the prefix and suffix values for the resource.
<center>
<p><img SRC="CustomFilePropertyPage.gif" height=330 width=537>
<br><b><font size=-1>Fig. 8: Example Plug-in's File Property Page</font></b></center>
<p>Using <font color="#000000">the file property page (Fig. 8) (a file property
page can be opened by right-clicking the file and selecting properties in the
context menu)</font>, users can set the busy state for the file. The busy state
when set is indicated by a lock icon superimposed on the base image provided
by Eclipse. Users can set the prefix and suffix values of resource labels (<img SRC="tag_2.gif" height=13 width=24 align=CENTER><img SRC="tag_3.gif" height=13 width=24 align=CENTER>).
<p>An individual label decoration preference page is provided for users to manage
the decorations (Fig. 9). They provide users control over prefix/suffix text
decoration <img SRC="tag_2.gif" height=13 width=24 align=CENTER>) and project
label decoration (<img SRC="tag_3.gif" height=13 width=24 align=CENTER>). The
project is decorated with a default text label decoration.
<center>
<p><img SRC="IndividualLabelDecorationPreferencePage.gif" height=280 width=524>
<br><b><font size=-1>Fig. 9: Example Plug-in's Label Decoration Preference
Page</font></b></center>
<p>Now that we have been introduced to the example plug-in, let's go into
details.
<h2>
<b><font color="#000000">Beyond the Basics</font></b></h2>
<p><font color="#000000">Before delving deep into actual methods that provide
decorations, it is important to understand the concepts needed while decorating
a resource.</font></p>
<h3>Persistent Resource Properties</h3>
Decorators, as we mentioned before, are used to visually annotate resources with
state information. The state information of a resource used for decoration should
be persistent across sessions. In our example plug-in, the prefix, the suffix
and the busy state of the resource should be persisted across sessions. There
are two ways to persist resource properties. One is the traditional way of associating
resources names with their corresponding properties, and storing them in a property
file. The second preferred method is to use Eclipse API to store persistent resource
properties. </p>
<p>Resources can have properties that hold state information. The properties of
a resource are declared, accessed and maintained by various plug-ins and are
not interpreted by the platform. There are two types of properties associated
with a resource: persistent properties and session properties. Persistent properties,
as the name suggests, are persistent across sessions while session properties
are maintained in memory and are lost when the project or workspace is closed.
Resource properties are deleted when the resources are deleted.
<p>Depending on the utility of the plug-in, plug-in developers can use
persistent or session properties. Persistent properties are stored on disk
and hence are accessible across platform sessions. The example plug-in
requires the busy state of the resource to be persisted across sessions.
The persistent property resource API is used to store and retrieve property
values by key. The following figure shows how to set and get the persistent
properties.
<br>&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>// Create a qualified Name for Busy state of the resource</tt>
<br><tt>QualifiedName q1 = new QualifiedName&nbsp;</tt>
<br><tt>&nbsp;("com.ibm.Decorator.DecoratorDemo", "Busy");</tt>
<p><tt>// Create a qualified Name for Prefix&nbsp;</tt>
<br><tt>QualifiedName q2 = new QualifiedName</tt>
<br><tt>&nbsp;("com.ibm.Decorator.DecoratorDemo", "Prefix");</tt>
<p><tt>// Set the persistent properties</tt>
<br><tt>resource.setPersistentProperty (q1, "true");</tt>
<br><tt>resource.setPersistentProperty (q2, "Prefix Value");</tt>
<p><tt>// Get the value of persistent property q1... The value&nbsp;</tt>
<br><tt>// retrieved is "true"</tt>
<br><tt>String busyNature = resource.getPersistentProperty (q1);</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 10: Sample code to manage Resource properties</font></b></center>
<p>The code snippet (shown above) explains how to set / get persistent
property of resources using <i>qualified name</i>s.&nbsp; A qualified name
is analogous to a key used for accessing / storing property values. Qualified
names are composed of two-part names, a qualifier and a local name. The
local name (in the example shown above, the local names are "Busy" and
"Prefix") could be used by any decorator. So it is extremely important
to provide a unique URI value for the qualifier part. The simplest way
to ensure a unique qualifier value is to use the id of your plug-in as
the qualifier name.
<p><img SRC="tip.gif" height=13 width=62> Where does Eclipse store resource persistent
properties? Eclipse stores resource persistent properties in an internal database
at "<em>Workspace Location</em>/.metadata/.plugins/org.eclipse.core.resources/.projects/<em>Project
Name</em>/.properties".
<p><img SRC="tip.gif" height=13 width=62> Eclipse provides a mechanism
to store the sync information associated with a resource. Associating "sync
info" with a resource and decorating a resource using the sync information
is also an option plug-in developers might want to consider before proceeding
with use of persistent properties. Sync info maintains all the information
in memory and writes to disk only on save or a snapshot. Another advantage
of using sync info is that changes to sync information are reported in
the subsequent delta while it is difficult to keep track of the persistent
property resource changes. Readers can refer to "org.eclipse.core.resources.ISynchronizer"
to know more about sync info and ways to store sync information for a resource.
<h3>Overlay Images</h3>
In order to provide our custom decorations on a resource in addition to the basic
decorations provided by Eclipse, we superimpose custom images on the base image.<font color="#000000">
Eclipse provides utility methods to help with overlaying an image over another.</font>
<p><img SRC="tip.gif" height=13 width=62>&nbsp; It<font color="#000000">
is a good idea to design your decorators so that they do not overlap or
conflict with the existing platform SDK decorators.</font> For example,
Eclipse provides the problem marker decorator to alert users of compilation
problems. The custom decoration should not superimpose images at the same
position as the problem marker. <font color="#000000">The custom decoration
also should not lose the problem marker information. </font>Since different
custom decorator providers don't have prior knowledge about one another,
there is a good chance that custom decoration from two different plug-in
providers will conflict each other. The Workbench label decoration page
and the individual preference page provided by different custom decorators
provide users the control over the choice of different decorations.
<p>Eclipse provides an API for overlaying one image over other. The following
code snippet explains how to overlay an icon image over another image.
<ul>
<li> Create a class that extends org.eclipse.jface.resource.CompositeImageDescriptor.
CompositeImageDescriptor is an abstract base class that should be used by
image descriptors that synthesize an image from other images.</li>
<li>
Implement drawCompositeImage() method to superimpose a custom image on
the base image.</li>
</ul>
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>protected void drawCompositeImage(int width, int height)&nbsp;</tt>
<br><tt>{&nbsp;</tt>
<br><tt>&nbsp;&nbsp; // To draw a composite image, the base image should
be&nbsp;</tt>
<br><tt>&nbsp;&nbsp; // drawn first (first layer) and then the overlay
image&nbsp;</tt>
<br><tt>&nbsp;&nbsp; // (second layer)&nbsp;</tt>
<p><tt>&nbsp;&nbsp; // Draw the base image using the base image's image
data</tt>
<br><img SRC="tag_1.gif" height=13 width=24><tt> drawImage(baseImage_.getImageData(),
0, 0);&nbsp;</tt>
<p><tt>&nbsp;&nbsp; // Method to create the overlay image data&nbsp;</tt> <br>
<tt>&nbsp;&nbsp; // Get the image data from the Image store or by other
means</tt> <br>
<tt>&nbsp;&nbsp; ImageData overlayImageData = demoImages.getLockImageData();</tt>
<br>
<tt><br>
</tt> <tt>&nbsp;&nbsp; // Overlaying the icon in the top left corner i.e.
x and y&nbsp;</tt> <br>
<tt>&nbsp;&nbsp; // coordinates are both zero</tt> <br>
<tt>&nbsp;&nbsp; int xValue = 0;</tt> <br>
<tt>&nbsp;&nbsp; int yValue = 0;</tt> <br>
<img SRC="tag_2.gif" height=11 width=24><tt> drawImage (overlayimageData,
xValue, yValue)&nbsp;</tt> <br>
<tt>}</tt>
</td>
</tr>
</table>
<center><b><font size=-1>Fig. 11: Overlay Custom Image on the base Image</font></b></center>
<p>The base image is drawn (<img SRC="tag_1.gif" height=13 width=24>) and then
the image that needs to be superimposed is drawn at the top left corner of the
base image (see&nbsp;<img SRC="tag_2.gif" height=11 width=24>).
<font color="#000000">The
example plug-in code "OverlayImageIcon.java"&nbsp; implements superimposing
the custom images on the base image at different locations.</font>
<p><img SRC="tip.gif" height=13 width=62> It is always good to create the overlay
image icons (that need to be superimposed on the base image) once and share
the same image across different views. In the example shown below (Fig. 12),
an image descriptor for a lock icon is created and the image data is returned
when requested by the drawImage() method of OverlayImageIcon (Fig. 11). In this
way, custom images are shared among objects across different views. The <a href="#best_practice_approaches">best
practice approaches</a> section also talks about the image registry and how
it can be best used to efficiently decorate resources with custom images. <br>
&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>public class DemoImages</tt>
<br><tt>{</tt>
<br><tt>&nbsp; private static final ImageDescriptor lockDescriptor =&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; ImageDescriptor.createFromFile (DemoDecorator.class,
"lock.gif");</tt>
<p><tt>&nbsp; public ImageData getLockImageData()</tt>
<br><tt>&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; return lockDescriptor.getImageData();</tt>
<br><tt>&nbsp; }</tt>
<br><tt>}</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 12: Illustration on how images can be shared
among objects across views</font></b></center>
<p>In our example plug-in, a lock icon is superimposed on the base image if the
file has its "busy" property set. The figure shown below shows how a resource's
image icon would look in the package explorer view (Fig. 13). The lock icon
is superimposed on the compilation unit and the runtime class instance of ImageDecoration.java
(<img SRC="tag_1.gif" height=13 width=24 align=CENTER><img SRC="tag_2.gif" height=11 width=24 align=CENTER>)
<center>
<p><img SRC="ImageDecoration.gif" height=203 width=373>
<br><b><font size=-1>Fig. 13: Superimpose a lock decorator</font></b></center>
<p>
<h3>Re-Decorate</h3>
When the workbench starts, the decorator manager checks for enabled decorators
(decorators are enabled using the Workbench &gt; Label Decorations page) and decorates
the resources inside different views using the custom decorations provided by
these decorators.
<p>The properties of a resource might change at runtime, which will trigger the
need for redecoration. For example, users might change a file's busy attribute
using the file property page - and therefore the image decoration must be changed
to reflect the change.&nbsp; To re-decorate the resources, we fire a LabelProviderChangedEvent.
The fired event notifies the different workbench views that the label provider
for the resource has changed.<font color="#000000"> Eclipse calls the decorateImage()
and decorateText() methods for the resources whose label provider has changed.
A LabelProviderChangedEvent should only be fired when some aspect of the element
used to do the decoration changes. They can also be fired when the labels need
to be updated due to a change in decoration presentation (e.g. due to a change
in a preference page for the decorator). Sending these events will update all
affected views.</font> <br>
&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt><font color="#000000">public void refresh(List resourcesToBeUpdated)</font></tt>
<br><tt><font color="#000000">{</font></tt>
<br><tt><font color="#000000">&nbsp; // resourcesToBeUpdated is a list
of resources whose decorators</font></tt>
<br><tt><font color="#000000">&nbsp; // need to be changed. The persistent
property of the resources&nbsp;</font></tt>
<br><tt><font color="#000000">&nbsp; // has been changed and hence its
decorators should change</font></tt>
<p><tt>&nbsp; // Check to see whether the custom decoration is enabled&nbsp;</tt>
<br><tt>&nbsp; DemoDecorator demoDecorator = getDemoDecorator();</tt>
<br><tt>&nbsp; if (demoDecorator == null)</tt>
<br><tt>&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp; // Decorator is not enabled.. Don't decorate
the resources.</tt>
<br><tt>&nbsp;&nbsp;&nbsp; return;</tt>
<br><tt>&nbsp; }</tt>
<p><tt><font color="#000000">&nbsp; // Fire a label provider changed event
to decorate the&nbsp;</font></tt>
<br><tt><font color="#000000">&nbsp; // resources whose image needs to
be updated</font></tt>
<br>
<img SRC="tag_1.gif" height=13 width=24 align=CENTER><tt><font color="#000000">fireLabelEvent(new
LabelProviderChangedEvent(demoDecorator,</font></tt> <br>
<tt><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; resourcesToBeUpdated.toArray()));</font></tt>
<br>
<tt><font color="#000000">}</font></tt>
<p><tt><font color="#000000">private void fireLabelEvent(final LabelProviderChangedEvent
event)</font></tt>
<br><tt><font color="#000000">{&nbsp;</font></tt>
<br><tt><font color="#000000">&nbsp; // Decorate using current UI thread</font></tt>
<br><img SRC="tag_2.gif" height=11 width=24 align=CENTER><tt><font color="#000000">Display.getDefault().asyncExec(new
Runnable()</font></tt>
<br><tt><font color="#000000">&nbsp; {</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; public void run()</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; {</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Fire a
LabelProviderChangedEvent to notify eclipse views</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // that label
provider has been changed for the resources</font></tt>
<br><img SRC="tag_3.gif" height=13 width=24 align=CENTER><tt><font color="#000000">&nbsp;&nbsp;
fireLabelProviderChanged(event);</font></tt>
<br><tt><font color="#000000">&nbsp;&nbsp;&nbsp; }</font></tt>
<br><tt><font color="#000000">&nbsp; });</font></tt>
<br><tt><font color="#000000">}</font></tt></td>
</tr>
</table>
<center><b><font color="#000000"><font size=-1>Fig. 14: Re-Decorate Resources</font></font></b></center>
<p>A LabelProviderChangedEvent is triggered (<img SRC="tag_3.gif" height=13 width=24>)
to notify different views that the label provider for the resources (in the
figure shown above, the resources list is stored in resourcesToBeUpdated) has
been changed and hence they need to be re-decorated. The plug-in developers
must provide a Runnable that fires a labelProviderChanged event (<img SRC="tag_2.gif" height=11 width=24 align=CENTER>).
<p><font color="#000000">If users choose to change the decoration preference
using the individual decoration preference page, </font>all the resources
in the workspace need to be re-decorated. This could be done easily by
changing line&nbsp;<img SRC="tag_1.gif" height=13 width=24 align=CENTER>
in Fig. 14 to <i>fireLabelEvent (new LabelProviderChangedEvent (demoDecorator)).</i>
<h3>IDecoratorManager Interface</h3>
IDecoratorManager manages custom decorators contributed
via the decorator's extension point. Some of the utility methods provided
by IDecoratorManager are:</font>
<ul>
<li>
<font color="#000000"><b>ILabelDecorator getLabelDecorator(String decoratorID)</b>-
Returns the label decorator for the specified decorator if it is enabled</font></li>
<li>
<font color="#000000"><b>boolean getEnabled(String decoratorID)</b> - Returns
whether the specified custom decorator is enabled or not</font></li>
<li>
<font color="#000000"><b>void setEnabled (String decoratorID, boolean enabled)</b>
- Enable/disable the custom decorator</font></li>
</ul>
There could be many custom decorators contributed via the decorator's extension
point. The id associated with the custom decorator is unique and should be used
to<font color="#FF6666"> </font><font color="#000000">distinguish between different
custom decorators. Views that allow decoration of their elements should use the
label decorator returned by the getLabelDecorator() method (<img SRC="tag_1.gif" height=13 width=24>).
The custom decorator objects (instance of the class used for decorating resources)
can be found using the decorator id (<img SRC="tag_1.gif" height=13 width=24>).
A custom decorator can be enabled or disabled by default using the state sub-element
in the plugin.xml manifest file.</font> <br>
&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>/**</tt>
<br>
<tt>&nbsp;* Gets the custom decorator object. This method should be called
to get</tt> <br>
<tt>&nbsp;* the custom decorator object by all methods that try to
decorate resources</tt>
<br><tt>&nbsp;* @return Custom Decorator Instance if the custom decorator
is enabled</tt>
<br><tt>&nbsp;*&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; null if
the custom decorator is not enabled&nbsp;</tt>
<br><tt>&nbsp;*/</tt>
<br><tt>public static DemoDecorator getDemoDecorator()</tt>
<br><tt>{</tt>
<p><tt>&nbsp; // In Eclipse 3.5, use: PlatformUI.getWorkbench().getDecoratorManager();</tt></p>
<tt>&nbsp; IDecoratorManager decoratorManager =</tt>
<br><tt>&nbsp;&nbsp;&nbsp; DecoratorPlugin.getDefault().getWorkbench().getDecoratorManager();</tt>
<p><tt>&nbsp; // com.ibm.decoratordemo.ui.decorator.demodecorator is the
id of the&nbsp;</tt>
<br><tt>&nbsp; // custom decorator</tt>
<p><tt>&nbsp; // If the decorator is disabled, a null value is returned&nbsp;</tt>
<br><img SRC="tag_1.gif" height=13 width=24 align=CENTER><tt>return (DemoDecorator)
decoratorManager.getLabelDecorator(</tt>
<br><tt>&nbsp;&nbsp; "com.ibm.decoratordemo.ui.decorator.demodecorator");</tt>
<p><tt>}</tt></td>
</tr>
</table>
<center>
<b><font color="#000000"><font size=-1>Fig. 15: How to get custom Decorator
instance using DecoratorManager Interface</font></font></b>
</center>
<p><font color="#000000">The custom decorator class used for decorating resources
is a singleton. The decorator developers should not use the decorator object
(instance of the class used for decorating resources) when the decorator is
disabled. The decorator developers should never cache the decorator object.
The decorator object is disposed when the decorator is disabled and is recreated
when the decorator is re-enabled. The utility method getLabelDecorator() returns
a null value if the custom decorator is disabled or a custom decorator with
the given decoratorId does not exist (<img SRC="tag_1.gif" height=13 width=24 align=CENTER>).</font>
<h2>
<a NAME="best_practice_approaches"></a>Best Practice Approaches</h2>
The time taken for decoration should be as small as possible because the
performance of the UI will be adversely affected by slow decorator code.
Some of the best practice approaches that should be followed to reduce
the time involved in decorations are as follows:
<br>&nbsp;
<li>
<b>Use image descriptors to store descriptors of image rather than storing
the images</b></li>
<br>
The org.eclipse.jface.resources.ImageDescriptor class, as the name suggests, is
a lightweight descriptor for an image. It contains all the information required
to create an image. Image Descriptors do not allocate an actual platform image
unless specifically requested using the createImage() method. Use of image descriptors
is one of the best strategies that should be used when your code is structured
such that it defines all the icons in one place and allocates them when needed.
<br>
&nbsp;
<li> <b>Use org.eclipse.jface.ImageRegistry to share images across different views</b></li>
<br>
The ImageRegistry class is used to keep a list of named images. Clients can add
image descriptors or SWT images directly to the list. When an image is requested
by name from the registry, the registry will return the image if it has been created,
or create one from the descriptor. This allows clients of the registry to share
images. A well-written article on images <a href="../Article-Using%20Images%20In%20Eclipse/Using%20Images%20In%20Eclipse.html"><em>Using
Images in the Eclipse UI</em></a> by John Arthorne is a good reference for learning
how to manage images in Eclipse.
<p>Images that are added to or retrieved from the registry must not be
disposed by any client. The registry is responsible for disposing of the
image since the images are shared by multiple clients. The registry will
dispose of the images when the platform GUI system shuts down. Appropriate
use of image descriptors and the image registry is important while performing
decorations. Since many views participate in decoration, it is important
to share the images using the caching mechanism rather than creating images
from scratch.
<br>&nbsp;
<li>
<b>Use Lightweight Decorators when possible</b></li>
<br>
Eclipse 2.1 introduces a lightweight decorator that will handle the image management
issues associated with decoration. Changes to the decorator mechanism in Eclipse
2.1 and ways to create custom decorations using <a href="#New">Lightweight decorator</a>
mechanism are discussed later. <br>
&nbsp;
<h2>
Decorate Resources</h2>
Let us look at the actual methods that decorate the image or the text of
resource labels. As mentioned before, the <font color="#000000">class that
is responsible for decoration should implement the ILabelDecorator interface.</font>
<p><font color="#000000">The interface provides two utility methods to
decorate the text and image.</font>
<ul>
<li>
<b><font color="#000000">Image decorateImage(Image baseImage, Object element)</font></b></li>
<li>
<b><font color="#000000">String decorateText(String initialText, Object
element)</font></b></li>
</ul>
<font color="#000000">The decorateImage method is used to decorate the object
image with additional state information of the resource. The current image of
the object can be obtained using the getImage() method of LabelProvider. The method
returns an annotated image or a null image if the object need not be decorated.</font>
The decorateText method is used to decorate the object label. <br>
&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>public Image decorateImage(Image baseImage, Object object)</tt>
<br><tt>{</tt>
<br><tt>&nbsp;&nbsp; // This method returns an annotated image or null
if the&nbsp;</tt>
<br><tt>&nbsp;&nbsp; // image need not be decorated. Returning a null image</tt>
<br><tt>&nbsp;&nbsp; // decorates resource icon with basic decorations
provided</tt>
<br><tt>&nbsp;&nbsp; // by Eclipse</tt>
<p><tt>&nbsp;&nbsp; IResource objectResource;</tt>
<br><img SRC="tag_1.gif" height=13 width=24 align=CENTER><tt> objectResource
= (IResource) object;</tt>
<br><tt>&nbsp;&nbsp; if (objectResource == null)</tt>
<br><tt>&nbsp;&nbsp; {</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; return null;</tt>
<br><tt>&nbsp;&nbsp; }</tt>
<br>
<img SRC="tag_2.gif" height=13 width=24 align=CENTER><tt> if (objectResource.getType()
!= IResource.FILE</tt><tt>)</tt> <br>
<tt>&nbsp;&nbsp; {</tt>
<br>
<tt>&nbsp;&nbsp;&nbsp;&nbsp; // Only files are decorated</tt> <br>
<tt>&nbsp;&nbsp;&nbsp;&nbsp; return null;</tt>
<br><tt>&nbsp;&nbsp; }</tt>
<br><tt>&nbsp;&nbsp; // Overlay custom image over base image&nbsp;</tt>
<br><tt>&nbsp;&nbsp; Image image;</tt>
<br><tt>&nbsp;&nbsp; OverlayImageIcon overlayIcon = new&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; OverlayImageIcon(baseImage,&nbsp; "Lock");</tt>
<br><img SRC="tag_3.gif" height=13 width=24><tt> image = overlayIcon.getImage();</tt>
<br><tt>&nbsp;&nbsp; return image;</tt>
<p><tt>&nbsp;&nbsp; // The image should be disposed when the plug-in is&nbsp;</tt>
<br><tt>&nbsp;&nbsp; // disabled or on shutdown</tt>
<br><tt>}</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 16: DecorateImage Method to decorate images
icons</font></b></center>
<p>The decorateImage() method (shown above) decorates only a file object and does
not decorate a project or a folder (<img SRC="tag_2.gif" height=13 width=24>).
The IResource object (<img SRC="tag_1.gif" height=13 width=24>) is used for
determining whether the object under consideration is a project / folder / file.
Using the OverlayImageIcon class (not shown), a lock decorator is superimposed
on the base image (<img SRC="tag_3.gif" height=13 width=24>) <br>
&nbsp;
<br>&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>public String decorateText(String label, Object obj)</tt>
<br><tt>{</tt>
<br><tt>&nbsp; IResource objectResource;</tt>
<br><tt>&nbsp; objectResource = (IResource) object;</tt>
<p><tt>&nbsp; if (objectResource.getType() != IResource.FILE)</tt><br>
<tt>&nbsp; {</tt>
<br>
<tt>&nbsp;&nbsp;&nbsp; // Only files are decorated in this example</tt>
<br>
<tt>&nbsp;&nbsp;&nbsp; return null;</tt>
<br><tt>&nbsp; }</tt>
<p><tt>&nbsp; // Decorate the label of the resource with the admin name&nbsp;</tt>
<br><tt>&nbsp; String ownerName = System.getProperties().getProperty("user.name");</tt>
<br><img SRC="tag_1.gif" height=13 width=24 align=CENTER><tt>return label
+ " ( " + ownerName + " )";</tt>
<br><tt>}&nbsp;</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 17: DecorateText method to annotate the label
of resource</font></b></center>
<p>The decorateText() method returns null (no decoration) for non-files. It decorates
the label of a file with owner information. Returning a null value signifies
that the decorator is ignored - it does not clear out any existing decorations.
<br>
&nbsp;
<h2>
When to Decorate</h2>
Just because you know how to decorate does not mean you know when to decorate.
There are no strict rules that could be followed to find out when to decorate
and when not to. Human intuition is the best way to proceed, although there
are various factors that could be taken into consideration. Factors that
could affect the decision on decoration are as follows:
<ul>
<li> The amount of time to perform the decoration.</li>
<li> The benefit to the user of seeing this particular state information.</li>
<li>
Is it possible to provide the state information in a different way?</li>
<li> Is the decoration in any way interfering with the basic decorations provided
by Eclipse.</li>
<li>
The frequency with which the state information changes.</li>
</ul>
Let us try to understand the above mentioned factors with an example. Let
us assume an example plug-in which tries to emulate CVS behavior - i.e.
trying to provide functionality for the developers to checkin / checkout
files from repository. The plug-in collaborates with an external server
to receive notification when resources are checked out by other users (checking
out a file means someone has extracted the file and is making changes to
the file). Let us assume the time spent on computing image decoration is
0.5 seconds. The number of times the plug-in receives notification from
the external server would be huge since there are many files in the repository
and many users are working concurrently.
<p>In our example, it is advantageous for the users to know about the files
that are changed by others. But can they afford to lose 0.5 seconds for
every decoration?&nbsp; No.&nbsp; So the plug-in developers, rather than
decorating the image icon, can present the information to the user in a
different way, for example, the file properties view. Improper use of decorators
can lead to poor performance and will ultimately lead to plug-in decorators
becoming useless.
<br>&nbsp;
<h2>
<a NAME="Caveat"></a>Caveat Lector (Reader Beware)</h2>
We saw some of the best practice approaches that could be used to reduce
the time taken to decorate image icons. Every image in Eclipse uses operating
system resources. We don't want to create 1,000 copies of the same image.
It would be nice to use some of the features in Eclipse to cache decorated
images and use them for generating similar images. Let us assume we want
to superimpose a lock icon on three text files. There are two different
ways to do this using the methods described earlier to superimpose a lock
icon on the base image. The lock icon should be superimposed on the base
image for all three text files. It would be better to decorate the first
text file, cache the resultant image in the image registry and use the
cached image to decorate the remaining files. In this way, image caching
can be used to avoid superimposing the same custom image over the same
base image every time you perform decoration. Image caching would be advantageous
when the frequency of decoration is huge and the amount of time spent on
calculating the superimposed image is high. Image caching should be used
if the developers have a prior knowledge about images that might appear
multiple times.
<p><img SRC="tip.gif" height=13 width=62> Decorations are performed when
workbench starts initially. It is called when users open a resource, close
a resource, expand the resource tree etc.
<p>Image caching, although a good technique to reduce the time involved
in decorating resources is not without problems. Some of the inherent problems
associated with decoration using image caching approach are as follows:
<ul>
<li>
<b>Plug-in developers should know all the decoration on a resource: </b>To
understand the above mentioned problem, you should know how to generate
a key that would be used for retrieving images from the image cache. The
key is a string describing all the properties of the resource, for example,
the type of the resource (a file or a folder or a project), type of the
file (a Java file or a text file) and marker information associated with
the resource. There could be new decorations provided by other plug-ins
depending on a particular custom resource property. Decorators which do
not have knowledge about the custom resource property can't differentiate
between a resource that has the custom property set and resources that
do not have the custom property set. This could lead to wrong decoration.</li>
</ul>
<ul>
<li>
<b>Decorating Java files: </b>To understand the problems associated with
using image caching to decorate Java files, let's revisit the plugin.xml
file.</li>
</ul>
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>&lt;extension point="org.eclipse.ui.decorators"></tt>
<br><tt>&nbsp; &lt;decorator&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; id="YourDecorator"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; label="Decorator Label"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; state="false"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; class="YourDecorator.class"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; objectClass="org.eclipse.core.resources.IResource"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp; adaptable="true">&nbsp;</tt>
<br><tt>&nbsp; &lt;/decorator>&nbsp;</tt>
<br><tt>&lt;/extension></tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 18: Sample plugin.xml manifest file to contribute
decorators</font></b></center>
<p>The attributes that are of interest are: objectClass and adaptable.
ObjectClass indicates the class of resources that need to be decorated.
The adaptable flag indicates whether the classes that adapt to the IResource
object should also be decorated.
<p><img SRC="tip.gif" height=13 width=62> If a user tries to decorate a Java file
in a navigator view, the decorateImage() and decorateText() methods are called
on the IFile object. If the user tries to decorate the Java file in a package
explorer, the decorateImage() and decorateText() methods are called on all the
Java elements (because all Java elements are adaptable to IFile objects).
<p>Let's see what happens when a user decorates a Java file and the adaptable
attribute is set to true. The decorateImage() method is called on all the
Java elements for the resource (JavaProject,&nbsp; PackageFragmentRoot,
PackageFragment, CompilationUnit (Java file), and runtime class). If the
adaptable flag is true, the object parameter passed to the decorateImage()
and decorateText() methods is an IResource object for compilationUnit and
runtime class while a null is passed for all the other Java elements.
<p>So what's the problem? Let us assume we cache the image (lock icon superimposed
on a Java (Compilation Unit) icon) with the property "Java Lock" to denote
that it is Java file with a lock icon superimposed on the base image. You
might have cached the image when decorateImage method was called on the
compilation unit. When decorateImage() method is called on a class file,
we get the same property information using the IResource object (Fig.
16) and hence we decorate the class file with the cached copy. So instead
of getting a lock icon on top of a class icon, the class file image icon
is represented by a custom lock decorator on top of a Java icon image.
The following diagrams illustrate the aforesaid behavior.
<center>
<p><img SRC="ImageDecorationWithoutImageCaching.gif" height=202 width=371>
<br><b><font size=-1>Fig. 19: Overlaying Image without Image cache</font></b>
<br>&nbsp;
<p><img SRC="ImageDecorationWithImageCaching.gif" height=200 width=371>
<br><b><font size=-1>Fig. 20: Overlay Image using Image Caching</font></b></center>
<p>From Fig. 19 and Fig. 20, it is clear that users should be careful while using
image caching with non-resource files. When image caching is used, the runtime
class is represented by a lock icon on top of the Java icon (<img SRC="tag_2.gif" height=13 width=24>
in Fig. 20) instead of a lock icon on top of the runtime class icon. Image caching
can't be used because there was no way to distinguish between the different
Java elements using the IResource object and its associated properties. To distinguish
between the Java elements, one has to depend on JDT core and write specific
adapters for Java elements.
<p><img SRC="tryit.gif" height=13 width=61> Change plugin.xml provided
with the example plug-in such that the class that implements decoration
is DemoDecoratorWithImageCaching rather than DemoDecorator. The DemoDecorator
object instance used in the "file property page" and "individual label
decorations preference page" should be replaced with an instance of DemoDecoratorWithImageCaching.
You should be able to see decoration like the one shown in Fig. 20.
<p>Due to the above mentioned problems, image caching, although a good
technique to reduce the time involved in decorating resources should not
be used.
<br>&nbsp;
<p><a NAME="New"></a><b><font size=+2>What's new in Eclipse 2.1?</font></b>
<p>In Eclipse 2.0, plug-in developers had to programmatically overlay the
custom images on top of the base image of the objects displayed in the
Eclipse views. Eclipse 2.1 introduces a lightweight decorator that will
handle the image management issues associated with decoration. It is also
possible to declare a lightweight decorator that simply overlays an icon
when enabled that requires no implementation from the plug-in. Lightweight
decorators performs decorations in a background thread and hence the UI
thread is not blocked when the decorations are performed.
<p>Let's look at the configuration markup for decorators in Eclipse 2.1.
<br>&nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>!ELEMENT decorator >&nbsp;</tt>
<br><tt>&nbsp;&nbsp; &lt;!ATTLIST decorator&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; id&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
CDATA #REQUIRED&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; label&nbsp;&nbsp; CDATA #REQUIRED&nbsp;</tt>
<br><img SRC="tag_1.gif" height=13 width=24 align=CENTER><tt>&nbsp;&nbsp;&nbsp;&nbsp;
class&nbsp;&nbsp; CDATA #OPTIONAL&nbsp; // #REQUIRED if lightweight = false&nbsp;</tt>
<br><img SRC="tag_2.gif" height=11 width=24 align=CENTER><tt>&nbsp;&nbsp;&nbsp;&nbsp;
objectClass CDATA #REQUIRED&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
//deprecated. Make this part of the enablement&nbsp;</tt>
<br><img SRC="tag_3.gif" height=13 width=24 align=CENTER><tt>&nbsp;&nbsp;&nbsp;&nbsp;
icon&nbsp;&nbsp;&nbsp; CDATA #OPTIONAL // required if there is no class&nbsp;</tt>
<br>
<img SRC="tag_4.gif" height=13 width=24><tt>&nbsp;&nbsp;&nbsp;&nbsp; location
("TOP_LEFT" | "TOP_RIGHT" | "BOTTOM_LEFT" | "BOTTOM_RIGHT"|&nbsp;</tt> <br>
<tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
"UNDERLAY") #OPTIONAL&nbsp;</tt>
<br>
<img SRC="tag_5.gif" height=13 width=24><tt>&nbsp;&nbsp;&nbsp;&nbsp; lightweight&nbsp;
(true | false) #IMPLIED&nbsp;</tt> <br>
<tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; adaptable&nbsp;&nbsp;&nbsp;
(true | false) #IMPLIED&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; state&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
(true | false) #IMPLIED&nbsp;</tt>
<br><tt>&nbsp;&nbsp; >&nbsp;</tt>
<br><tt>&nbsp;&nbsp; &lt;!ELEMENT description (#PCDATA)>&nbsp;</tt>
<br>
<img SRC="tag_6.gif" height=13 width=24><tt> &lt;!ELEMENT enablement (#PCDATA)>&nbsp;</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 21: Configuration Markup for decorators in
Eclipse 2.1</font></b></center>
<p>As you can see from Fig. 21, there are quite a few changes in the configuration
markup for decorators in Eclipse 2.1. The class which was a required field in
Eclipse 2.0 is an optional field in Eclipse 2.1(<img SRC="tag_1.gif" height=13 width=24 align=CENTER>).
The class attribute represents a fully qualified name of a class which implements
org.eclipse.jface.viewers.ILabelDecorator if lightweight is false or org.eclipse.jface.viewers.ILightweightLabelDecorator
if lightweight is true. The default value is false. If there is no class element
it is assumed to be true. The objectClass attribute is deprecated in Eclipse
2.1 and is part of the enablement element (<img SRC="tag_2.gif" height=11 width=24 align=CENTER>).
The icon attribute is a new attribute in Eclipse 2.1 and it represents the path
to the overlay image to apply if the decorator is lightweight (<img SRC="tag_3.gif" height=13 width=24 align=CENTER>).
Location attribute represents the location to apply the decorator if the decorator
is lightweight (<img SRC="tag_4.gif" height=13 width=24>). The default value
of location is BOTTOM_RIGHT. Lightweight attribute can be used to signify whether
the decorator is lightweight or not (<img SRC="tag_5.gif" height=13 width=24>).
Enablement sub-elements represent the actionExpression used to determine enabled
state (<img SRC="tag_6.gif" height=13 width=24>).
<p>Let's look at an example to understand lightweight decorators. &nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><tt>&lt;extension point="org.eclipse.ui.decorators">&nbsp;</tt>
<br><tt>&nbsp; &lt;decorator</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; id="com.ibm.DemoLightweightDecorator"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; label="DemoLightweightDecorator"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; state="false"&nbsp;</tt>
<br><img SRC="tag_1.gif" height=13 width=24><tt>&nbsp; class="com.ibm.Demo.LightweightDecorator"&nbsp;</tt>
<br><img SRC="tag_2.gif" height=11 width=24><tt>&nbsp; lightweight="true"&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; location="TOP_LEFT"</tt>
<p><tt>&nbsp;&nbsp;&nbsp;&nbsp; &lt;enablement></tt>
<br><img SRC="tag_3.gif" height=13 width=24><tt>&nbsp;&nbsp;&nbsp;&nbsp;
&lt;objectClass="org.eclipse.core.resources.IResource"/>&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp; &lt;/enablement>&nbsp;</tt>
<br><tt>&nbsp; &lt;/decorator&nbsp;</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 22: Decorator Extension using Lightweight
Decorator</font></b></center>
<p>Since the lightweight attribute has a true value (<img SRC="tag_2.gif" height=11 width=24>),
the class com.ibm.Demo.LightweightDecorator (<img SRC="tag_1.gif" height=13 width=24>)
should implement org.eclipse.jface.viewers.ILightweightLabelDecorator. The class
com.ibm.Demo.LightweightDecorator should provide the text decoration labels
and the image descriptor and need not be concerned with the resource handling.
Another advantage of using Lightweight decorators is that the decoration work
is done in a background thread.
<p>Let's look at the ILightweightLabelDecorator interface to see how easy decorations
can be performed in Eclipse 2.1. &nbsp;
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><b><tt>org.eclipse.jface.viewers.ILightweightLabelDecorator interface:</tt></b>
<br><tt>The ILightweightLabelDecorator is a decorator that decorates using
a prefix, suffix and overlay image rather than doing all of the image and
text management itself like an ILabelDecorator.&nbsp;</tt>
<p><img SRC="tag_1.gif" height=13 width=24><tt> void <b>decorate</b>(Object
element, IDecoration decoration)&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; calculates decorations
based on element.</tt></td>
</tr>
</table>
<center><b><font size=-1>Fig. 23: ILightweightLabelDecorator interface</font></b></center>
<p>From Fig. 23, it is clear that plug-in developers should implement decorate()
method (<img SRC="tag_1.gif" height=13 width=24>) to perform both text and image
decorations. This is a big difference from Eclipse 2.0 where plug-in developers
had to implement decorateText() for performing text decorations and decorateImage()
method for performing image decorations. One added advantage of the Eclipse
2.1 lightweight decorator mechanism is that plug-in developers need not be concerned
with resource handling and need only to provide the text and image descriptors.
When a plug-in developer tries to redecorate a resource by firing a LabelProviderChanged
event, Eclipse calls the decorate() method for the object. The plug-in developers
should appropriately set the overlay image descriptors, prefix label and the
suffix label using the IDecoration object instance.
<table BORDER COLS=1 WIDTH="100%" BGCOLOR="#CCCCCC" >
<tr>
<td><b><tt>org.eclipse.jface.viewers.IDecoration interface:&nbsp;</tt></b>
<br><tt>Defines the result of decorating an element. This interface is
not meant to be implemented and will be provided to instances of ILightweightLabelDecorator.&nbsp;</tt>
<p><img SRC="tag_1.gif" height=13 width=24><tt> void <b>addOverlay </b>(ImageDescriptor
overlay)</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Adds an overlay to the
element's image.&nbsp;</tt>
<p><img SRC="tag_2.gif" height=11 width=24><tt> void <b>addPrefix </b>(String
prefix)&nbsp;</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Adds a prefix
to the element's label.&nbsp;</tt>
<p><img SRC="tag_3.gif" height=13 width=24><tt> void <b>addSuffix</b> (String
suffix)</tt>
<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Adds a suffix
to the element's label.&nbsp;</tt></td>
</tr>
</table>
<center>
<b><font size=-1>Fig. 24: IDecoration interface</font></b>
</center>
<p>For the Example Plug-in, the lock image descriptor (to signify the busy state
of a resource) can be set using the addOverlay() method (<img SRC="tag_1.gif" height=13 width=24>).
The prefix and suffix labels for an object element can be set using the addPrefix()
and addSuffix() methods (<img SRC="tag_2.gif" height=11 width=24><img SRC="tag_3.gif" height=13 width=24>).
<p>If a plug-in requires a developer to provide only image decoration and
no text decorations, then the plug-in developer could make use of a declarative
LightweightDecorator. This means that plug-in developer need not provide
a class to implement ILightweightLabelDecorator but instead provide the
path for the icon image and location where the icon needs to be placed
(TOP_LEFT | BOTTOM_LEFT | TOP_RIGHT | BOTTOM_RIGHT | UNDERLAY). Eclipse
LightweightDecorator mechanism takes care of resource handling and image
decoration.
<p>As we have seen, the new LightweightDecorator mechanism is quite powerful
and makes it easy for developers to decorate resources. The source code
for the Example Plug-in implemented using lightweight decorators (Eclipse
M4 stable version) is provided below.
<h2>
Source Code</h2>
The example plug-in uses most of the best practice approaches that should
be followed while decorating a resource. Some of the classes are as follows:
<ul>
<li> DemoDecorator - Decorates the label and image icon of the resources.</li>
<li>
DemoImages - Maintains the image descriptors of the custom decorator image
icons.</li>
<li>
DemoStore - Stores user decoration preferences.</li>
<li> DemoImageRegistry - Accesses the image registry to get the images from registry.</li>
<li> DemoLabelDecoratorPreferencePage - Individual label decoration preference
page provided by DecoratorDemo plug-in.</li>
<li>
DemoFilePropertyPage - Custom file property page.</li>
<li> OverlayImageIcon - Class to overlay images.</li>
</ul>
To run the example or view the source code for this article, unzip <a href="sourceOfDecoratorPlugin.zip">sourceCodeOfDecoratorPlugin.zip</a>
into your <i>plug-ins/ </i>subdirectory. To run view the source code for
the Example plug-in using lightweight decorators, unzip <a href="sourceOfLightweightDecorator.zip">sourceCodeOfDecoratorPluginUsingLightweightDecorators.zip</a>
into your plug-ins/ subdirectory.
<h2>
Summary</h2>
Decorators are visual cues that convey useful state information associated
with objects or resources displayed in Eclipse views. Eclipse provides
ways for users to change the image and label decorators.&nbsp; The performance
of the Eclipse UI can be affected by the efficiency with which decorations
are performed. The best practice approaches mentioned in this article can
be used to reduce the time involved in decoration. An old saying, "<i>Pictures
are worth 1000 words - but only if you know the words</i>" aptly describes
the use of decorators.
<p><b><font color="#000000"><font size=+2>Acknowledgements</font></font></b>
<p>The author would like to thank Jan J. Kratky, Patrick McCarthy, Tod Creasey,
and Nick Edgar (all at IBM) for providing constructive comments on the article.</p>
<p><small>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.</small></p>
</body>
</html>