blob: 57bf6910cdea449c7d29d48f29a890b075aef5b7 [file] [log] [blame]
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Rich Client Platform</title>
<link href="default_style.css" rel=stylesheet>
</head>
<body>
<div align="right">
<font face="Times New Roman, Times, serif" size="2">Copyright © 2004 Ed
Burnette.</font>
<table border="0" cellspacing="0" cellpadding="2" width="100%">
<tbody>
<tr>
<td align="left" valign="top" colspan="2" bgcolor="#0080c0"><b><font face="Arial,Helvetica"><font color="#ffffff">Eclipse
Article</font></font></b></td>
</tr>
</tbody>
</table>
</div>
<div align="left">
<h1 title="RCP Tutorial"><img src="images/Idea.jpg" align="middle" width="120" height="86"></h1>
</div>
<h1 align="center">Rich Client Tutorial Part 2</h1>
<p class="summary">
The Rich Client Platform (RCP)
allows you to
build Java applications that can compete with native
applications on any platform.
Part 1 of the tutorial introduced you to the platform
and the steps used to build the smallest possible RCP program.
In part 2 we'll look at what we did in more detail
and introduce some of the configuration classes
that let you take control of much of the layout and
functionality of an RCP application.
</p>
<p><b>By Ed Burnette, SAS</b><br>
<font size="-1">August 9, 2004</font></p>
<hr width="100%">
<h2>Introduction</h2>
<p>
In Eclipse 2.1, many functions of the Eclipse IDE were hard-wired
into the code.
These included the name and location of the File menu,
the title of the Workbench Window, and the existence of
the status bar.
This was fine for the IDE but when people started to
use Eclipse as a basis for non-IDE programs, sometimes
these things didn't make sense.
Although all the source code was provided, it was
inconvenient to find the right places that had to be
changed.
So for Eclipse 3, the designers refactored the API to
make these and other hard-wired aspects of the user
interface controllable through public API.
</p>
<p>
To this end, Eclipse 3 introduces a brand new
<code>WorkbenchAdvisor</code> class and a set of
<code>*Configurer</code> interfaces.
The most important class for an RCP developer to understand is
<code>WorkbenchAdvisor</code>.
You extend the base version of the <code>WorkbenchAdvisor</code> class
in your RCP application and override one or more of the methods
to set whatever options you want.
In part 1, we implemented one of the simpler methods,
<code>getInitialWindowPerspectiveId</code>,
in order to return the one and only perspective (set of views, editors, and menus)
for the application.
</p>
<p>
In the next section we'll take a look at the other advisor methods
to get a quick overview of what is possible with them.
If you want to cut to the chase and look at or download
the code for this part you can view the
<a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.tutorials.rcp.part2">Eclipse project here</a>.
</p>
<p>
Otherwise, let's start by clearing up
some possible confusion about
the relationship between
Applications, Workbenches, and Workbench Windows.
</p>
<h2>Applications, Workbenches, and Workbench Windows</h2>
<p>
The Application is a class you create that acts as your RCP program's main routine.
You can think of it as the controller for the program.
Just like the controller in a Model2 architecture, it is short and sweet
and doesn't change significantly for different projects.
All it does is create a Workbench and attach a Workbench Advisor to it.
</p>
<p>
The Workbench is declared and maintained for you as part of the RCP framework.
There is only one Workbench but it can have more than one visible top-level
Workbench Window.
For example, in the Eclipse IDE, when you first start Eclipse you will see
one Workbench Window, but if you select <b>Window &gt; New Window</b> a second
window pops up --
two Workbench Windows, but only one Workbench.
</p>
<p>
Figure 1 shows the relationship between your Application, the Workbench, and
Workbench Windows.
</p>
<img src="images/Application.png">
<p><b>Figure 1. An RCP program has one Application class that you provide, and one
Workbench class provided by the framework.
Typically there is only one Workbench Window but
the framework supports having more than one.
</b></p>
<p>
Ok, now that that's out of the way, let's get our feet wet with
changing how the Workbench Window looks.
</p>
<h2>Customizing the Workbench Window</h2>
<p>
The simple example in part 1 left room for a status line, tool bar, and
other visual elements even though they weren't used.
We could argue that those should be off by default, but
for some reason they're not so if you don't want them
then you have to turn them off yourself.
Also the window didn't have a title and it
opened much larger than it needed to be.
A good place to take care of all
this is the <code>preWindowOpen</code> method
of <code>WorkbenchAdvisor</code>,
as shown in listing 1.
</p>
<p><b>Listing 1. preWindowOpen example.
</b></p>
<pre>
public void preWindowOpen(IWorkbenchWindowConfigurer configurer) {
super.preWindowOpen(configurer);
configurer.setInitialSize(new Point(400, 300));
configurer.setShowCoolBar(false);
configurer.setShowStatusLine(false);
configurer.setTitle("Hello, RCP");
}
</pre>
<p>
Notice how a Configurer interface (in this case, <code>IWorkbenchWindowConfigurer</code>)
is passed in to the advisor method.
You call methods on the Configurer interfaces to actually
change the options.
These interfaces are not covered in any detail in this tutorial
but you can refer to their Javadoc for more information.
See figure 2 for the final result.
</p>
<img src="images/hellotitle.png">
<p><b>
Figure 2. Workbench window with a title.
Unused controls are turned off and a title is added using the
<code>preWindowOpen</code> event of
<code>WorkbenchAdvisor</code>.
</b></p>
<h2>Digging into the <code>WorkbenchAdvisor</code> class</h2>
<p>
Since RCP is new and documentation is sparse,
we're going to depart from our tutorial format at this point to
examine some background information about the
<code>WorkbenchAdvisor</code> class.
Methods in this class are called from the platform to
notify you at every point in the lifecycle of the Workbench
and top level Workbench Windows.
They also provide a way to handle exceptions in the
event loop,
and provide important parameters to the Workbench such as
the default perspective.
First let's take a look at the lifecycle events.
</p>
<p>
<img src="images/note.gif" alt="Note: " width="62" height="13">
Why not an interface?
You'll notice that <code>WorkbenchAdvisor</code> is an abstract class.
For the most part, Eclipse APIs shun abstract classes in favor of interfaces and
base classes that implement those interfaces.
In this case, the designers intend for <code>WorkbenchAdvisor</code>
to <i>always</i> be implemented
by the RCP application, the base class doesn't contain any significant functionality,
and it is very likely that new methods will need to be added to the base class in the
future (something that is difficult using interfaces).
So this was a deliberate design choice.
See the article on evolving Java APIs in the reference section
for more tips.
</p>
<h3>Lifecycle events</h3>
<p>
There is only one Workbench, and
typically there is also only one Workbench Window, though you can open
more than one if you like (on different monitors for example).
See figure 3 for the most important events in the lifetime of
Workbenches and their windows.
Table 1 shows all the methods that you can define in your
own subclass of <code>WorkbenchAdvisor</code> to let you
hook into lifecycle events for the Workbench.
Table 2 shows the corresponding methods for
Workbench Windows.
</p>
<img src="images/Lifecycle.png">
<p><b>Figure 3. Important events in the lifecycle of the Workbench and Workbench Window(s).
These correspond to methods in <code>WorkbenchAdvisor</code> that are called by the Platform
to let you perform custom code at these points in time.
</b></p>
<p><b>Table 1.
Workbench lifecycle hooks provided by <code>org.eclipse.ui.application.WorkbenchAdvisor</code>.
</b></p>
<table border="1">
<tr><th>Method
<th>Description
<th>Parameter(s)
<tr><td>initialize
<td>Called first to perform any setup such as
parsing the command line, registering adapters, declaring images, etc..
<td><code>IWorkbenchConfigurer<code>
<tr><td>preStartup
<td>Called after initialization but before the first window is opened.
May be used to set options affecting which editors and views
are initially opened.
<td>&nbsp;
<tr><td>postStartup
<td>Called after all windows have been opened or restored,
but before the event loop starts.
It can be used to start automatic processes and to
open tips or other windows.
<td>&nbsp;
<tr><td>preShutdown
<td>Called after the event loop has terminated but before any
windows have been closed.
<td>&nbsp;
<tr><td>postShutdown
<td>Called after all windows are closed during Workbench shutdown.
This can be used to save the current application state and
clean up anything created
by <code>initialize</code>.
<td>&nbsp;
</table>
<p><b>Table 2.
Workbench window lifecycle hooks provided by <code>org.eclipse.ui.application.WorkbenchAdvisor</code>.
</b></p>
<table border="1">
<tr><th>Method
<th>Description
<th>Parameter(s)
<tr><td>preWindowOpen
<td>Called in the constructor of the Workbench Window.
Use this method to set options such as whether or not the window will have
a menu bar.
However none of the window's widgets have been created yet so they
can't be referenced in this method.
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>fillActionBars
<td>Called right after <code>preWindowOpen</code>.
This is where you set up any hard-wired menus and toolbars.
This is probably the most complicated method here because of the flags
it is passed.
Note: takes a Workbench window, not a configurer.
<td><code>IWorkbenchWindow</code>,
<code>IActionBarConfigurer</code>, flags
<tr><td>postWindowRestore
<td>Optionally called for cases when a window has been restored
from saved state but before it is opened.
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>postWindowCreate
<td>Called after a window has been restored
from saved state or created from scratch
but before it is opened.
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>openIntro
<td>Called immediately before a window is opened
in order to create the Intro component (if any).
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>postWindowOpen
<td>Called right after the Workbench window is opened.
Can be used to tweak any of the window's widgets, for example
to set a title or change its size.
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>preWindowShellClose
<td>Called before the Workbench window is closed
(technically, before its shell is closed).
This is the only function that can veto the close, so it's
a good place for an "Are you sure" kind of dialog.
<td><code>IWorkbenchWindowConfigurer</code>
<tr><td>postWindowClose
<td>Called after the Workbench window is closed
Can be used to clean up anything created by
<code>preWindowOpen</code>.
<td><code>IWorkbenchWindowConfigurer</code>
</table>
<h3>Event loop hooks</h3>
<p>
The event loop is the code that is running most of the time during
the life of the Workbench.
It handles all user inputs and dispatches them to the right listeners.
RCP provides a couple of hooks to handle crashes and perform work during idle time
(see table 3).
</p>
<p><b>Table 3.
Event loop hooks provided by <code>org.eclipse.ui.application.WorkbenchAdvisor</code>.
</b></p>
<table border="1">
<tr><th>Method
<th>Description
<th>Parameter(s)
<tr><td>eventLoopException
<td>Called if there is an unhandled exception in the event loop.
The default implementation will log the error.
<td><code>Throwable</code>
<tr><td>eventLoopIdle
<td>Called when the event loop has nothing to do.
<td><code>Display</code>
</table>
<h3>Information getting hooks</h3>
<p>
Next, there are few methods you can implement that
the platform will call to get information about your application
(see table 4).
The most important one (and the only one that is not optional)
is <code>getInitialWindowPerspectiveId</code>.
We used this in part 1 to return the id of the starting (and only)
perspective in RcpTest.
</p>
<p><b>Table 4.
Information requests provided by <code>org.eclipse.ui.application.WorkbenchAdvisor</code>.
</b></p>
<table border="1">
<tr><th>Method
<th>Description
<th>Parameter(s)
<tr><td>getDefaultPageInput
<td>Return the default input for new workbench pages.
Defaults to null.
<td>&nbsp;
<tr><td>getInitialWindowPerspectiveId
<td>Return the initial perspective used for new workbench windows.
This is a required function that has no default.
<td>&nbsp;
<tr><td>getMainPreferencePageId
<td>Return the preference page that should be displayed first.
Defaults to null, meaning the pages should be arranged alphabetically.
<td>&nbsp;
<tr><td>isApplicationMenu
<td>Return true if the menu is one of yours.
OLE specific; see Javadoc for details.
<td><code>IWorkbenchWindowConfigurer</code>,
<code>String</code>
</table>
<h3>Advanced configuration</h3>
<p>
The <code>WorkbenchAdvisor</code> events above should be
sufficient for most applications, but just in case,
RCP provides two more methods to take complete control
of how your application windows and controls are created.
They're listed in table 5 for completeness but I
don't expect many programs will need them.
</p>
<p><b>Table 5.
Advanced methods in <code>org.eclipse.ui.application.WorkbenchAdvisor</code>.
</b></p>
<table border="1">
<tr><th>Method
<th>Description
<th>Parameter(s)
<tr><td>createWindowContents
<td>Creates the contents of one window.
Override this method to define custom contents and layout.
<td><code>IWorkbenchWindowConfigurer</code>,
<code>Shell</code>
<tr><td>openWindows
<td>Open all Workbench windows on startup.
The default implementation tries to restore the previously
saved workbench state.
<td>&nbsp;
</table>
<h2>Preparing for internationalization</h2>
<p>
Internationalization (i18n for short) opens
up your application to a much wider market.
The first step is simply to pull all your human readable text messages out of
your code and into a standard format properties file.
Even if you are not planning to make your code available in multiple languages,
separating the messages makes it much easier for you to spell check them and
check them for consistent grammar and word usage.
</p>
<p>
There's nothing magic about messages in Eclipse - you just
use the plain old Java resource bundle mechanism that you may
already be familiar with.
The Eclipse IDE provides a nice Externalization wizard to make
this less of a chore.
See the references section below for a link to an article
that describes how to use it in detail.
</p>
<p>
Briefly, when you're writing a new section of code
you will often hard-code strings just to get something going.
For example, in listing 1 we used:
</p>
<pre>
configurer.setTitle("Hello, RCP");
</pre>
<p>
Once you have a section of code working, though, you should get into the habit
of invoking the Externalization wizard to pull these strings out.
Simply right click on the project and select
<b>Source > Find Strings to Externalize...</b>.
Any source files that need attention will be listed.
Pick one and press <b>Externalize...</b> to open the Externalization wizard.
Follow the directions there to have the wizard convert your code to
use a resource bundle reference.
Alternatively you could right click on a single source file and select
<b>Source > Externalize Strings...</b>.
When you're done your source code will look something like this:
</p>
<pre>
configurer.setTitle(Messages.getString("Hello_RCP")); //$NON-NLS-1$
</pre>
<p>
The string <code>$NON-NLS-1$</code> is a hint for both the compiler and
the Externalization wizard that the first character string on this line
is a tag or keyword of some sort and should not be localized.
</p>
<p>
Also you will have a standard format .properties file containing
the keys and values for all your messages.
In the example code you will find a file called RcpTutorial.properties
that contains:
</p>
<pre>
Hello_RCP=Hello, RCP
</pre>
<p>
Finally, the wizard will create a class that wraps a Java resource bundle
to load and find things in the .properties file.
</p>
<p>
<img src="images/tip.gif" alt="Tip: " width="62" height="13">
To perform substitutions use the standard
<code>java.text.MessageFormat</code> class.
The <code>format()</code> method is somewhat similar
to the C routine <code>sprintf</code>, except
instead of taking format specifiers starting with percent signs,
<code>format()</code> uses numbered parameters in curly braces.
Here's an example from the <code>XMLStructureCreator</code> class in the
compare example plug-in (split onto multiple lines for readability):
<pre>
bodynode.setName(MessageFormat.format("{0} ({1})",
new String[] {XMLCompareMessages.getString("XMLStructureCreator.body"),
Integer.toString(fcurrentParent.bodies)})); //$NON-NLS-2$ //$NON-NLS-1$
</pre>
<p>
This isn't a good example, though, because typically the format string
itself should be in a message file too.
However, messages intended to be read by another program
(commands, keywords, scripts, and so forth) should
<b>not</b> be put in a message file.
</p>
<p>
To keep these lines from getting incredibly long you will probably want to
create helper methods.
For examples of helper methods see <code>org.eclipse.internal.runtime.Policy</code>.
</p>
<h2>Conclusion</h2>
<p>
In part 2 of this tutorial, we looked at some of the newly refactored API
of the Rich Client Platform that allows you to develop customized
native-looking client-side Java programs.
The next part will delve into defining and populating menus and toolbars.
All the sample code for this part may be viewed at the
<a href="http://dev.eclipse.org/viewcvs/index.cgi/org.eclipse.ui.tutorials.rcp.part2">Eclipse project here</a>.
You can use
<a href="http://dev.eclipse.org/cvshowto.html">Eclipse's built-in CVS client</a>
to download the source to your workspace.
</p>
<h2>References</h2>
<p>
<a href="http://eclipse.org/articles/Article-RCP-1/tutorial1.html">
RCP Tutorial Part 1</a><br>
<a href="http://eclipse.org/articles/Article-RCP-3/tutorial3.html">
RCP Tutorial Part 3</a><br>
<a href="http://dev.eclipse.org/viewcvs/index.cgi/%7Echeckout%7E/platform-ui-home/rcp/index.html">
Eclipse Rich Client Platform</a><br>
<a href="http://www.eclipse.org/articles/Article-Internationalization/how2I18n.html">
How to Internationalize your Eclipse Plug-in</a><br>
<a href="http://www.fawcette.com/javapro/2002_06/online/servlets_06_11_02/">Almost All Java Web Apps Need Model 2
(introduction to the Model 2 architecture)</a><br>
<a href="http://www.eclipse.org/eclipse/development/java-api-evolution.html">
Evolving Java-based APIs</a><br>
<a href="http://www.eclipsepowered.org">
Eclipse Powered (rich client plug-ins and resources)</a><br>
</p>
<p><small>IBM is trademark of International Business Machines Corporation in the
United States, other countries, or both.</small></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>
<p><small>Microsoft and Windows are trademarks of Microsoft Corporation in the
United States, other countries, or both.</small></p>
<p><small>Other company, product, and service names may be trademarks or service
marks of others.</small></p>
</body>
</html>