|  | <!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="Author" content="Build"> | 
|  | <meta name="GENERATOR" content="Mozilla/4.5 [en] (WinNT; I) [Netscape]"> | 
|  | <title>Creating an Eclipse View</title> | 
|  | <link rel="stylesheet" href="../default_style.css"> | 
|  | </head> | 
|  | <body> | 
|  |  | 
|  | <div align=right>  <font face="Times New Roman, Times, serif"><font size=-1>Copyright | 
|  | © 2001 Object Technology International, Inc.</font></font></div> | 
|  |  | 
|  | <table BORDER=0 CELLSPACING=0 CELLPADDING=2 WIDTH="100%" > | 
|  | <caption><TBODY> | 
|  | <br></TBODY></caption> | 
|  |  | 
|  | <tr> | 
|  | <td ALIGN=LEFT VALIGN=TOP COLSPAN="2" BGCOLOR="#0080C0"><b><font face="Arial,Helvetica"><font color="#FFFFFF"> Eclipse | 
|  | Corner Article</font></font></b></td> | 
|  | </tr> | 
|  | </table> | 
|  |  | 
|  | <h1> | 
|  | <img SRC="Idea.jpg" height=86 width=120 align=CENTER></h1> | 
|  |  | 
|  | <center> | 
|  | <h1> | 
|  | Creating an Eclipse View</h1></center> | 
|  |  | 
|  | <blockquote> | 
|  | <b>Summary</b> | 
|  | <br> | 
|  | In the Eclipse Platform a view is typically used to navigate a hierarchy | 
|  | of information, open an editor, or display properties for the active editor.  | 
|  | In this article the design and implementation of a view will be examined | 
|  | in detail.  You'll learn how to create a simple view based on SWT, | 
|  | and a more advanced view using the JFace viewer hierarchy.  We'll | 
|  | also look at ways to achieve good integration with many of the existing | 
|  | features in the workbench, such as the window menu and toolbar, view linking, | 
|  | workbench persistence and action extension. | 
|  | <p><b>By Dave Springgay, OTI</b> | 
|  | <br>    <font size="-1">November 2, 2001</font> | 
|  |  | 
|  | </blockquote> | 
|  | <hr WIDTH="100%"> | 
|  |  | 
|  |  | 
|  | <h2> | 
|  | Introduction</h2> | 
|  | In the Eclipse Platform the workbench contains a collection of workbench | 
|  | windows.  Each workbench window contains one or more pages, and each | 
|  | page contains a collection of editors and views.  An editor is typically | 
|  | used to edit or browse a document or input object.  Modifications | 
|  | made in an editor follow an open-save-close lifecycle model.  A view | 
|  | is typically used to navigate a hierarchy of information, open an editor, | 
|  | or display properties for the active editor.  In contrast to an editor, | 
|  | modifications made in a view are saved immediately.  The layout of | 
|  | editors and views within a page is controlled by the active perspective. | 
|  | <p>The workbench contains a number of standard components which demonstrate | 
|  | the role of a view.  For instance, the Navigator view is used to display | 
|  | and navigate through the workspace.  If you select a file in the Navigator, | 
|  | you can open an editor on the contents of the file.  Once an editor | 
|  | is open, you can navigate the structure in the editor data using the Outline | 
|  | view, or edit the properties of the file contents using the Properties | 
|  | view. | 
|  | <p>In this article we'll look at how you, as a plug-in developer, can add | 
|  | new views to the workbench.  First we'll examine the process through | 
|  | the creation of a simple Label view.  This view just displays the | 
|  | words "Hello World".  Then we'll design and implement a more comprehensive | 
|  | view, called Word view, which displays a list of words which can be modified | 
|  | through addition and deletion.  And finally, we'll look at ways to | 
|  | achieve good integration with many of the existing features in the workbench. | 
|  | <h2> | 
|  | Source Code</h2> | 
|  | <p>To run the example or view the source for code for this article you can unzip | 
|  | <a href="viewArticleSrc.zip">viewArticleSrc.zip</a> into your <i>plugins/ </i>subdirectory. | 
|  | </p> | 
|  | <p>  </p> | 
|  | <h2> | 
|  | <a NAME="Adding a New Perspective"></a>Adding a New View</h2> | 
|  | A new view is added to the workbench using a simple three step process. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | Create a plug-in.</li> | 
|  |  | 
|  | <li> | 
|  | Add a view extension to the plugin.xml file.</li> | 
|  |  | 
|  | <li> | 
|  | Define a view class for the extension within the plug-in.</li> | 
|  | </ol> | 
|  |  | 
|  | <p><br>To illustrate this process we'll define a view called "Label", which | 
|  | just displays the words "Hello World". | 
|  | <h3> | 
|  | Step 1: Create a Plug-in</h3> | 
|  | In step 1 we need to create a new plug-in.  The process of plug-in | 
|  | creation is explained in detail in <a href="http://www.eclipse.org/articles/Your%20First%20Plug-in.html">Your | 
|  | First Plugin</a>, by Jim Amsden, so we won't go into the details here.  | 
|  | To be brief, we need to create a plugin project, and then define a plugin.xml | 
|  | file (shown below).  This file contains a declaration of the plug-in | 
|  | id, name, pre-requisites, etc. | 
|  | <pre><?xml version="1.0" encoding="UTF-8"?> | 
|  | <plugin | 
|  |    name="Views Plugin"                             | 
|  |    id="org.eclipse.ui.articles.views" | 
|  |    version="1.0.0" | 
|  |    provider-name="OTI"> | 
|  |  | 
|  | <requires> | 
|  |         <import plugin="org.eclipse.core.boot"/> | 
|  |         <import plugin="org.eclipse.core.runtime"/> | 
|  |         <import plugin="org.eclipse.core.resources"/> | 
|  |         <import plugin="org.eclipse.swt"/> | 
|  |         <import plugin="org.eclipse.ui"/> | 
|  | </requires> | 
|  |  | 
|  | <runtime> | 
|  |         <library name="views.jar"/> | 
|  | </runtime> | 
|  |  | 
|  | </plugin></pre> | 
|  |  | 
|  | <h3> | 
|  | Step 2: Add a View Extension to the plugin.xml file</h3> | 
|  | Once we have a plugin, we can define a view extension.  The org.eclipse.ui | 
|  | plug-in defines a single extension point for view contribution: org.eclipse.ui.views.  | 
|  | In the XML below, we use this extension point to add the Label view extension.  | 
|  | This XML is added to the plugin.xml file, after the runtime element, and | 
|  | contains the basic attributes for the view: id, name, icon and class. | 
|  | <pre><extension point="org.eclipse.ui.views"> | 
|  |         <view id="org.eclipse.ui.articles.views.labelview" | 
|  |              name="Label View" | 
|  | <img SRC="tag_a.jpg" BORDER=0 height=13 width=24>          class="org.eclipse.ui.articles.views.LabelView" | 
|  | <img SRC="tag_b.jpg" BORDER=0 height=13 width=24>          icon="icons\view.gif"/> | 
|  | </extension></pre> | 
|  | A complete description of the view extension point and the syntax are available | 
|  | in the developer documentation for <tt>org.eclipse.ui</tt>.  The attributes | 
|  | are described as follows. | 
|  | <br>  | 
|  | <ul> | 
|  | <li> | 
|  | <b>id</b> - a unique name that will be used to identify this view</li> | 
|  |  | 
|  | <li> | 
|  | <b>name</b> - a translatable name that will be used in the UI for this | 
|  | view</li> | 
|  |  | 
|  | <li> | 
|  | <b>class</b> - a fully qualified name of the class that implements <tt>org.eclipse.ui.IViewPart</tt>. | 
|  | A common practice is to subclass <tt>org.eclipse.ui.part.ViewPart</tt> | 
|  | in order to inherit the default functionality.</li> | 
|  |  | 
|  | <li> | 
|  | <b>icon</b> - a relative name of the icon that will be associated with | 
|  | the view.</li> | 
|  | </ul> | 
|  |  | 
|  | <p><br>Perhaps the most important attribute is <i>class </i>(<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | This attribute must contain the fully qualified name of a class that implements | 
|  | <tt>org.eclipse.ui.IViewPart</tt>.  | 
|  | The <tt>IViewPart</tt> interface defines the minimal responsibilities of | 
|  | the view, the chief one being to create an SWT Control within the workbench.  | 
|  | This provides the presentation for an underlying model.  In the XML | 
|  | above, the class for the Label view is defined as <tt>org.eclipse.ui.articles.views.LabelView</tt>.  | 
|  | The implementation of <tt>LabelView</tt> will be examined in a few paragraphs. | 
|  | <p>The <i>icon</i> attribute (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | contains the name of an image that will be associated with the view.  | 
|  | This image must exist within the plugin directory or one of the sub directories. | 
|  | <h3> | 
|  | Step 3: Define a View Class for the Extension within the Plug-in</h3> | 
|  | Now we need to define the view class.  To help us out, the platform | 
|  | contains an abstract class, named <tt>org.eclipse.ui.part.ViewPart</tt>, | 
|  | which implements most of the default behavior for an <tt>IViewPart</tt>.  | 
|  | By subclassing this, we inherit all of the behavior.  The result is | 
|  | LabelView (shown below).  The methods in this class implement the | 
|  | view specific presentation.  The <tt>createPartControl</tt> method | 
|  | (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>)  creates | 
|  | an SWT <tt>Label</tt> object to display the phrase "Hello World".  | 
|  | The <tt>setFocus</tt> (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | method gives focus to this control.  Both of these methods will be | 
|  | called by the platform. | 
|  | <pre>package org.eclipse.ui.articles.views; | 
|  |  | 
|  | import org.eclipse.swt.widgets.*; | 
|  | import org.eclipse.ui.part.ViewPart; | 
|  |  | 
|  | public class LabelView extends ViewPart { | 
|  |         private Label label; | 
|  |         public LabelView() { | 
|  |                 super(); | 
|  |         } | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>     public void setFocus() { | 
|  |                 label.setFocus(); | 
|  |         } | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>     public void createPartControl(Composite parent) { | 
|  |                 label = new Label(parent, 0); | 
|  |                 label.setText("Hello World"); | 
|  |         } | 
|  |  | 
|  | }</pre> | 
|  | Once the LabelView class has been declared and compiled, we can test the | 
|  | results.  To do this, invoke the Perspective > Show View > Other menu | 
|  | item.  A dialog will appear containing all of the view extensions: | 
|  | the Label View item will appear in the Other category.  If you select | 
|  | the Label View item and press OK, the view will be instantiated and opened | 
|  | in the current perspective.  Here is a screen snapshot of the Label | 
|  | view after it has been opened within the Resource perspective. | 
|  | <center> | 
|  | <p><img SRC="LabelView.jpg" height=397 width=534></center> | 
|  |  | 
|  | <h3> | 
|  | View Lifecycle</h3> | 
|  | The lifecycle of a view begins when the view is added to a workbench page.  | 
|  | This can occur during the creation of the page, or afterwards, if the user | 
|  | invokes Perspective > Show View.  In either case the following lifecycle | 
|  | is performed. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | An instance of the view class is instantiated.  If the view class | 
|  | does not implement <tt>IViewPart</tt> an <tt>PartInitException</tt> is | 
|  | thrown.</li> | 
|  |  | 
|  | <li> | 
|  | The <tt>IViewPart.init</tt> method is called to initialize the context | 
|  | for the view.  An <tt>IViewSite</tt> object is passed, and contains | 
|  | methods to get the containing page, window, and other services is passed.</li> | 
|  |  | 
|  | <li> | 
|  | The <tt>IViewPart.createPartControl</tt> method is called.  Within | 
|  | this method the view can create any number of SWT controls within a parent | 
|  | <tt>Composite</tt>.  | 
|  | These provide the presentation for some underlying, view specific model.</li> | 
|  | </ol> | 
|  |  | 
|  | <p><br>When the view is closed the lifecycle is completed. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | The parent <tt>Composite</tt> passed to <tt>createPartControl</tt> is disposed.  | 
|  | This children are also implicitly disposed.  If you wish to run any | 
|  | code at this time, you must hook the control dispose event.</li> | 
|  |  | 
|  | <li> | 
|  | The <tt>IViewPart.dispose</tt> method is called to terminate the part lifecycle.  | 
|  | This is the last method which the workbench will call on the part.  | 
|  | It is an ideal time to release any fonts, images, etc.</li> | 
|  | </ol> | 
|  |  | 
|  | <h3> | 
|  | View Position</h3> | 
|  | As we have already seen, the user can open a view by invoking Perspective | 
|  | > Show View.  In this scenario, the initial position of the new view | 
|  | is determined by the active perspective within the current page.  | 
|  | You can think of a perspective as a layout containing views, folders, and | 
|  | place holders.  A <i>folder</i> is a stack of views.  A <i>place | 
|  | holder</i> marks the desired position of a view, should the user open it.  | 
|  | When a new view is opened the platform will search the perspective for | 
|  | a place holder with the same <tt>id</tt> as the view.  If such a place | 
|  | holder is found, it will be replaced by the new view.  Otherwise, | 
|  | the new view will be placed on the right hand side of the page. | 
|  | <p>As a plugin developer, you may also define a new perspective which contains | 
|  | your view, or add your view to a perspective which has been contributed | 
|  | by another plug-in. In either case, you have greater control over view | 
|  | position.  For more information about the use of perspectives see | 
|  | <a href="http://www.eclipse.org/articles/using-perspectives/PerspectiveArticle.html">Using | 
|  | Perspectives in the Eclipse UI</a>. | 
|  | <h3> | 
|  | Can I Declare A View Through API?</h3> | 
|  | Every view within the workbench must be declared in XML.  There are | 
|  | two reasons for this. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | Within Eclipse, XML is typically used to declare the presence of an extension | 
|  | and the circumstances under which it should be loaded.  This information | 
|  | is used to load the extension and its plugin lazily for better startup | 
|  | performance.  For views in particular, the declaration is used to | 
|  | populate the Show View menu item before the plugin is loaded.</li> | 
|  |  | 
|  | <li> | 
|  | The view declaration is also used for workbench persistence.  When | 
|  | the workbench is closed, the id and position of each view within the workbench | 
|  | is saved to a file.  It is possible to save the specific view class.  | 
|  | However, the persistence of class names in general is brittle, so view | 
|  | id's are used instead.  On startup, the view id is mapped to a view | 
|  | extension within the plugin registry.  Then a new instance of the | 
|  | view class is instantiated.</li> | 
|  | </ol> | 
|  |  | 
|  | <h2> | 
|  | The Word View</h2> | 
|  | In this section we'll implement a more comprehensive view, called Word | 
|  | view, which displays a list of words which can be modified through addition | 
|  | and deletion. | 
|  | <h3> | 
|  | Adding a View Extension to the plugin.xml file</h3> | 
|  | To start out, we add another view extension to the plugin.xml (shown below).  | 
|  | This extension has the same format as the Label view: id, name, icon and | 
|  | class.  In addition, the extension contains a <i>category</i> element | 
|  | (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  A <i>category</i> | 
|  | is used to group a set of views within the Show View dialog.  A category | 
|  | must be defined using a category element.  Once this has been done, | 
|  | we can populate the category by including the category attribute in the | 
|  | Word view declaration (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The category, and the Word view within it, will both be visible in the | 
|  | Show View dialog. | 
|  | <pre><extension point="org.eclipse.ui.views"> | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>     <category  | 
|  |            id="org.eclipse.ui.article" | 
|  |            name="Article"> | 
|  |         </category> | 
|  |         <view id="org.eclipse.ui.articles.views.wordview" | 
|  |                 name="Word View" | 
|  |                 icon="icons\view.gif" | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             category="org.eclipse.ui.article" | 
|  |                 class="org.eclipse.ui.articles.views.WordView"/> | 
|  | </extension></pre> | 
|  |  | 
|  | <h3> | 
|  | Defining a View Class for the Extension within the Plug-in</h3> | 
|  | Now we need to define a view class for the Word view.  For simplicity, | 
|  | we subclass ViewPart to create a WordView class (shown below), and then | 
|  | implement <tt>setFocus</tt> and <tt>createPartControl</tt>.  The implementation | 
|  | of this class is discussed after the code. | 
|  | <pre>public class WordView extends ViewPart  | 
|  | { | 
|  |         WordFile input; | 
|  |         ListViewer viewer; | 
|  |         Action addItemAction, deleteItemAction, selectAllAction; | 
|  |         IMemento memento; | 
|  |          | 
|  |         /** | 
|  |          * Constructor | 
|  |          */ | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>     public WordView() { | 
|  |                 super(); | 
|  |                 input = new WordFile(new File("list.lst")); | 
|  |         } | 
|  |  | 
|  |         /** | 
|  |          * @see IViewPart.init(IViewSite) | 
|  |          */ | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>     public void init(IViewSite site) throws PartInitException { | 
|  |                 super.init(site); | 
|  |                 // Normally we might do other stuff here. | 
|  |         } | 
|  |  | 
|  |         /** | 
|  |          * @see IWorkbenchPart#createPartControl(Composite) | 
|  |          */ | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>     public void createPartControl(Composite parent) { | 
|  |                 // Create viewer. | 
|  | <img SRC="tag_d.jpg" height=13 width=24 align=CENTER>             viewer = new ListViewer(parent); | 
|  |                 viewer.setContentProvider(new WordContentProvider()); | 
|  |                 viewer.setLabelProvider(new LabelProvider()); | 
|  |                 viewer.setInput(input); | 
|  |  | 
|  |                 // Create menu and toolbars. | 
|  | <img SRC="tag_e.jpg" height=13 width=24 align=CENTER>             createActions(); | 
|  |                 createMenu(); | 
|  |                 createToolbar(); | 
|  |                 createContextMenu(); | 
|  |                 hookGlobalActions(); | 
|  |                  | 
|  |                 // Restore state from the previous session. | 
|  | <img SRC="tag_f.jpg" height=13 width=24 align=CENTER>             restoreState(); | 
|  |         } | 
|  |          | 
|  |         /** | 
|  |          * @see WorkbenchPart#setFocus() | 
|  |          */ | 
|  |         public void setFocus() { | 
|  |                 viewer.getControl().setFocus(); | 
|  |         }</pre> | 
|  | In the <tt>WordView</tt> constructor, the model, a <tt>WordFile</tt>, is | 
|  | created (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The actual shape of the model is somewhat irrelevant, as we wish to focus | 
|  | on the details of view creation, and the appropriate model will vary with | 
|  | the problem domain.  To be brief, a <tt>WordFile</tt> is simply a | 
|  | list of words which are stored in a file.  The storage location of | 
|  | the <tt>WordFile</tt> is passed to the constructor.  If this file | 
|  | exists the <tt>WordFile</tt> will read it.  Otherwise it will create | 
|  | the file.  The <tt>WordFile</tt> also has simple methods to add, remove, | 
|  | and get all of the words.  Each word is stored in a <tt>Word</tt> | 
|  | object, which is just a wrapper for a <tt>String</tt>.  The Word and | 
|  | WordFile classes are supplied with the source of this article. | 
|  | <p>After the view is instantiated, the <tt>IViewPart.init</tt> method is | 
|  | called with an <tt>IViewSite</tt> (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The site is the primary interface between the view part and the outside | 
|  | world.  Given the site, you can access the view menu, toolbar, status | 
|  | line, containing page, containing window, shell, etc.  In the code | 
|  | above we simply call the superclass, ViewPart, where the site is stored | 
|  | in an instance variable.  It can be retrieved at any time by calling | 
|  | getViewSite(). | 
|  | <p>The <tt>createPartControl</tt> method (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | is called to create an SWT Control for the <tt>WordFile</tt> model.  | 
|  | The creation of this presentation can be done with raw SWT widgets.  | 
|  | However, the platform UI contains a class framework, known as JFace, which | 
|  | provides a viewer hierarchy for list, tree, table, and text widgets.  | 
|  | A <i>viewer</i> is a wrapper for an SWT control, adding a model based interface | 
|  | to it.   If you need to display a simple list model, tree model, | 
|  | or table model, use a viewer.  Otherwise, it is probably easier to | 
|  | use raw SWT widgets.  In the Word view the model is a simple list | 
|  | of words, so a ListViewer is used for the presentation. | 
|  | <p>On the first line of <tt>createPartControl</tt> the <tt>ListViewer</tt> | 
|  | is created (<img SRC="tag_d.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The constructor chosen here automatically creates an SWT List control in | 
|  | the parent.  Then a <i>content provider</i> is defined.  A content | 
|  | provider is an adapter for a domain specific model, wrapping it in an abstract | 
|  | interface which the viewer invokes to get the model root and its children.  | 
|  | If the model (the <tt>WordFile</tt>) changes, the content provider will | 
|  | also refresh the state of the <tt>ListViewer</tt> to make the changes visible.  | 
|  | A <i>label provider</i> is also defined.  This serves up a label and | 
|  | image for each object which is supplied by the content provider.  | 
|  | And finally, we set the input for the <tt>ListViewer</tt>.  The input | 
|  | is just the | 
|  | <tt>WordFile</tt> created in the <tt>WordView</tt> constructor. | 
|  | <p>The <tt>createPartControl</tt> method also contains several method calls for | 
|  | the creation of a menu, toolbar, context menu, and global actions (<img SRC="tag_e.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | These methods will be examined in later sections.  For now, it is sufficient | 
|  | to say that each view has a local menu and toolbar which appear in the view | 
|  | title area.  The view itself may contribute menu and toolbar items to these | 
|  | areas.  The view may define a context menu (pop-up menu) for each control | 
|  | it creates, or hook a global action in the parent window.  A <i>global | 
|  | action</i> refers to those actions in the window menu and toolbar which are | 
|  | always visible, but delegate their implementation to the active part.  | 
|  | For instance, the Cut, Copy and Paste actions are always visible in the Edit | 
|  | menu.  If invoked, their implementation is delegated to the active part.  | 
|  | The last line (<img SRC="tag_f.jpg" height=13 width=24 align=ABSCENTER>) relates | 
|  | to state persistence between sessions.  We will examine the code for each | 
|  | of these features in later sections. | 
|  | <p>Once the Word view has been declared and compiled we can test the results.  | 
|  | To do this, invoke Perspective > Show View > Other, and then select the | 
|  | Word View in the Show View dialog.  The Word view will appear in the | 
|  | Resource perspective, as shown below. | 
|  | <center> | 
|  | <p><img SRC="WordView.jpg" height=397 width=534></center> | 
|  |  | 
|  | <h2> | 
|  | Menus and Toolbars</h2> | 
|  | As the Word view exists now it isn't very useful.  We can browse a | 
|  | list of words, but we can't modify that list.  In this section we'll | 
|  | add some menu and toolbar items to the view so the user can add and delete | 
|  | words within the list. | 
|  | <p>But first, some background information.  Each view has a local | 
|  | menu and toolbar.  The toolbar is located to the right of the view | 
|  | caption.  The menu is initially invisible, but becomes visible if | 
|  | you click on the menu button, which is just to the left of the close button.  | 
|  | For instance, here is a snapshot of the Word view with the menu and toolbar | 
|  | we are about to define. | 
|  | <center> | 
|  | <p><img SRC="menus.jpg" height=247 width=264></center> | 
|  |  | 
|  | <p>The menu and toolbar controls for a view are initially empty and invisible.  | 
|  | As the view adds items to each, the menu or toolbar will become visible.  | 
|  | A view can also contribute items to the status line which appears at the | 
|  | bottom of the parent window.  Together, the local menu, toolbar and | 
|  | status line are known as the <i>action bars</i>. | 
|  | <p>In code, access to the action bars is provided by the view site.  | 
|  | The site is the primary interface between the view part and the outside | 
|  | world.  If your view is a subclass of ViewPart, the site can be accessed | 
|  | by calling ViewPart.getViewSite.  If not, then you must manage your | 
|  | own storage and retrieval of the site, which is passed to the view in the | 
|  | IViewPart.init method.  Given the site, you can call <tt>getActionBars().getMenuManager</tt> | 
|  | to get an <tt>IMenuManager</tt>, or getActionBars().getToolBarManager() | 
|  | to get an IToolBarManager. | 
|  | <p>The IMenuManager and IToolBarManager interfaces are a JFace abstraction.  | 
|  | They wrap an SWT <tt>Menu</tt> or <tt>Toolbar</tt> object so that you, | 
|  | the developer, can think in terms of action and action contribution. An | 
|  | <i>action</i> | 
|  | represents a command which can be triggered by the user from a menu or | 
|  | toolbar.  It has a run method, plus other methods which return the | 
|  | menu or tool item presentation (text, image, etc.).  In JFace, you | 
|  | create a menu or toolbar by adding instances of org.eclipse.jface.action.IAction | 
|  | to an IMenuManager or IToolBarManager. | 
|  | <h3> | 
|  | Defining our Actions</h3> | 
|  | We will contribute an "Add..." and "Delete" action to the Word view toolbar, | 
|  | and a "Select All" action to the view menu.  In general, the initial | 
|  | actions within a view are contributed using Java™ code, and other plugin | 
|  | developers extend the menus and toolbars of a view using XML. | 
|  | <p>In the WordView class, the actions are created in the createActions | 
|  | method (shown below), which is called from createPartControl.  For | 
|  | simplicity, each action is defined as anonymous subclass (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | of <tt>org.eclipse.jface.actions.Action</tt>.  We override the run | 
|  | method (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>) to implement | 
|  | the actual behavior of the action. | 
|  | <pre>        public void createActions() { | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             addItemAction = new Action("Add...") { | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>                     public void run() {  | 
|  |                                 addItem(); | 
|  |                         } | 
|  |                 }; | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>             addItemAction.setImageDescriptor(getImageDescriptor("add.gif"));</pre> | 
|  |  | 
|  | <pre>                deleteItemAction = new Action("Delete") { | 
|  |                         public void run() { | 
|  |                                 deleteItem(); | 
|  |                         } | 
|  |                 }; | 
|  |                 deleteItemAction.setImageDescriptor(getImageDescriptor("delete.gif"));</pre> | 
|  |  | 
|  | <pre>                selectAllAction = new Action("Select All") { | 
|  |                         public void run() { | 
|  |                                 selectAll(); | 
|  |                         } | 
|  |                 }; | 
|  |                  | 
|  |                 // Add selection listener. | 
|  | <img SRC="tag_d.jpg" height=13 width=24 align=CENTER>             viewer.addSelectionChangedListener(new ISelectionChangedListener() { | 
|  |                         public void selectionChanged(SelectionChangedEvent event) { | 
|  |                                 updateActionEnablement(); | 
|  |                         } | 
|  |                 }); | 
|  |         }</pre> | 
|  | The label of each action is defined in the action constructor (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | In addition, an image is defined for the "Add..." and "Delete" actions | 
|  | (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>), since they | 
|  | will appear in the toolbar.  These images are defined by invoking | 
|  | getImageDescriptor (shown below), a method which returns an image descriptor | 
|  | for a file stored in the plugin directory.  Within this method the | 
|  | ViewsPlugin object is retrieved, the install URL (plugin directory) is | 
|  | determined, and then an ImageDescriptor is created for a path based on | 
|  | this URL. | 
|  | <pre>        /** | 
|  |          * Returns the image descriptor with the given relative path. | 
|  |          */ | 
|  |         private ImageDescriptor getImageDescriptor(String relativePath) { | 
|  |                 String iconPath = "icons/"; | 
|  |                 try { | 
|  |                         ViewsPlugin plugin = ViewsPlugin.getDefault(); | 
|  |                         URL installURL = plugin.getDescriptor().getInstallURL(); | 
|  |                         URL url = new URL(installURL, iconPath + relativePath); | 
|  |                         return ImageDescriptor.createFromURL(url); | 
|  |                 } | 
|  |                 catch (MalformedURLException e) { | 
|  |                         // should not happen | 
|  |                         return ImageDescriptor.getMissingImageDescriptor(); | 
|  |                 } | 
|  |         }</pre> | 
|  |  | 
|  | <p><br>On the last line of createActions (<img SRC="tag_d.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | a selection listener is created and added to the list viewer.  In | 
|  | general, the enablement state of a menu or tool item should reflect the | 
|  | view selection.  If a selection occurs in the Word view, the enablement | 
|  | state of each action will be updated in the updateActionEnablement method | 
|  | (shown below). | 
|  | <pre>        private void updateActionEnablement() { | 
|  |                 IStructuredSelection sel =  | 
|  |                         (IStructuredSelection)viewer.getSelection(); | 
|  |                 deleteItemAction.setEnabled(sel.size() > 0); | 
|  |         }</pre> | 
|  |  | 
|  | <p><br>The approach taken for action enablement in the Word view is just | 
|  | one way to go.  It is also possible to imagine an implementation where | 
|  | each action is a selection listener, and will enable itself to reflect | 
|  | the selection.  This approach would make the actions reusable beyond | 
|  | the context of the original view. | 
|  | <h3> | 
|  | Adding our Actions</h3> | 
|  | Once the actions have been created we can add them to the existing menu | 
|  | and toolbar for the view.  In the WordView class, this is done in | 
|  | the createMenu and createToolbar methods (shown below), which are called | 
|  | from createPartControl.  In each case the view site is used to access | 
|  | the menu and toolbar managers, and then actions are added. | 
|  | <pre>        /** | 
|  |          * Create menu. | 
|  |          */ | 
|  |         private void createMenu() { | 
|  |                 IMenuManager mgr = getViewSite().getActionBars().getMenuManager(); | 
|  |                 mgr.add(selectAllAction); | 
|  |         } | 
|  |          | 
|  |         /** | 
|  |          * Create toolbar. | 
|  |          */ | 
|  |         private void createToolbar() { | 
|  |                 IToolBarManager mgr = getViewSite().getActionBars().getToolBarManager(); | 
|  |                 mgr.add(addItemAction); | 
|  |                 mgr.add(deleteItemAction); | 
|  |         }</pre> | 
|  | At this point the definition of the menu and toolbar are complete.  | 
|  | If the Word view is opened in the workbench, the toolbar will be populated | 
|  | with the Add and Delete items.  If you click the down arrow for the | 
|  | menu, you will see the Select All item there. | 
|  | <h2> | 
|  | <a NAME="Context Menus"></a>Context Menus</h2> | 
|  | In Eclipse, it is very common to expose and invoke actions from the context | 
|  | menu.  For instance, if you press and release the right mouse button | 
|  | over an IFile in the Navigator, a context menu appears with standard file | 
|  | operations like Open, Rename, and Delete.  Experienced users come | 
|  | to expect this, so in this section we will define a context menu for the | 
|  | Word view. | 
|  | <p>But first, some background info.  One of the primary goals for | 
|  | the platform UI is extensibility.  In fact, it is this extensibility | 
|  | which gives you the freedom to add new views, editors, perspectives, actions, | 
|  | etc., to the platform.  Of course, extensibility is a two way street.  | 
|  | While you may wish to extend the platform, others may wish to extend your | 
|  | view.  It is common for one plug-in to add actions to the menu, toolbar, | 
|  | or context menu of a view from another plugin. | 
|  | <p>Support for context menu extension is accomplished by collaboration | 
|  | between the view and the platform.  In contrast to the local menu | 
|  | and toolbar for each view, which are created by the platform and populated | 
|  | by the view, each context menu within a view must be created by the view | 
|  | itself.  This is because the context menus within a view are beyond | 
|  | the scope of platform visibility, and there may be zero, one, or many context | 
|  | menus within the view.  Once a context menu has been created, the | 
|  | view must explicitly register each context menu with the platform.  | 
|  | This makes it available for extension. | 
|  | <p>A context menu can be created using the SWT | 
|  | <tt>Menu</tt> and <tt>MenuItem</tt> | 
|  | classes directly, or it can be created using the JFace | 
|  | <tt>MenuManager</tt> | 
|  | class.  If you define a context menu using the MenuManager class, | 
|  | the platform can extend this menu with actions.  However, if you define | 
|  | a context menu using the SWT Menu and MenuItem classes directly, this is | 
|  | not possible. | 
|  | <h3> | 
|  | Creating the Context Menu</h3> | 
|  | In the WordView class, the context menu is created in the createContextMenu | 
|  | method (shown below), which is called from createPartControl.   | 
|  | Within this method a MenuManager is created (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | a menu is created (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | and then the menu manager is passed to the platform for extension with | 
|  | other actions (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>). | 
|  | <pre>        private void createContextMenu() { | 
|  |                 // Create menu manager. | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             MenuManager menuMgr = new MenuManager(); | 
|  |                 menuMgr.setRemoveAllWhenShown(true); | 
|  |                 menuMgr.addMenuListener(new IMenuListener() { | 
|  |                         public void menuAboutToShow(IMenuManager mgr) { | 
|  |                                 fillContextMenu(mgr); | 
|  |                         } | 
|  |                 }); | 
|  |                  | 
|  |                 // Create menu. | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             Menu menu = menuMgr.createContextMenu(viewer.getControl()); | 
|  |                 viewer.getControl().setMenu(menu); | 
|  |                  | 
|  |                 // Register menu for extension. | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>             getSite().registerContextMenu(menuMgr, viewer); | 
|  |         }</pre> | 
|  | In the first few lines of <tt>createContextMenu</tt> a new <tt>MenuManager</tt> | 
|  | is created (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | and it is configured for dynamic action population.  In other words, | 
|  | w<font color="#000000">henever the menu is opened, the old actions will | 
|  | be removed, and new actions will be added to reflect the current selection.  | 
|  | This behavior is a requirement for action extension, and if not implemented, | 
|  | the action extensions contributed by the platform will soon flood the context | 
|  | menu.    To accomplish this, we invoke setRemoveAllWhenShown(true) | 
|  | to clear the menu when it opens.  Then we add a menu listener to populate | 
|  | the menu when it opens.  The menu listener just calls fillContextMenu, | 
|  | which will be discussed below.</font> | 
|  | <p>After creating the MenuManager, we can create the menu, and then register | 
|  | the menu manager with the site (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | Take a good look at the last line of createContextMenu.  In this statement | 
|  | we pass the menu manager and the viewer to the site.  In general, | 
|  | the platform will only add action extensions to a menu if it opens.  | 
|  | At that time, the action extensions for the menu are determined by examining | 
|  | the menu id and view selection.  So where is the menu id?  To | 
|  | answer, we need to look at the definition of IWorkbenchPartSite.registerContextMenu.  | 
|  | There are two registerContextMenu methods (shown below).  If there | 
|  | is only one context menu in the view, we should call the first.  The | 
|  | menu id will be derived from the part id to ensure consistency.  So | 
|  | in this case, our menu id is "org.eclipse.ui.articles.views.wordview".  | 
|  | If there is more than one context menu in the view, we should use the second, | 
|  | where the menu id is explicit. | 
|  | <pre>    // Defined on IWorkbenchPartSite. | 
|  |     public void registerContextMenu(MenuManager menuManager, | 
|  |         ISelectionProvider selectionProvider); | 
|  |     public void registerContextMenu(String menuId, MenuManager menuManager, | 
|  |         ISelectionProvider selectionProvider);</pre> | 
|  | If the user opens our context menu, the fillContextMenu method will be | 
|  | called.   This method adds the Add, Delete and SelectAll actions | 
|  | to the menu.  It is important to notice that a <tt>GroupMarker</tt> | 
|  | is also added to the menu for "additions" (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | This group will be used as a target for action extension by other plug-ins.  | 
|  | If it does not exist, all of the action extensions will appear at the end | 
|  | of the menu. | 
|  | <pre>        private void fillContextMenu(IMenuManager mgr) { | 
|  |                 mgr.add(addItemAction); | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             mgr.add(new GroupMarker(IWorkbenchActionConstants.MB_ADDITIONS)); | 
|  |                 mgr.add(deleteItemAction); | 
|  |                 mgr.add(new Separator()); | 
|  |                 mgr.add(selectAllAction); | 
|  |         }</pre> | 
|  |  | 
|  | <h3> | 
|  | Supporting Extension of the Context Menu</h3> | 
|  | In the last section extensibility was a prominent issue.  If we want | 
|  | to support context menu extension in the Word view, or any other view, | 
|  | the following process should be followed. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | If you create a context menu, publish the menu id in a public interface | 
|  | which other plug-in developers can reference.</li> | 
|  |  | 
|  | <li> | 
|  | Add a GroupMarker with <i>id == IWorkbenchActionConstants.MB_ADDITIONS</i> | 
|  | to each context menu.  Other plug-in developers will use this as a | 
|  | target for menu extensions.</li> | 
|  |  | 
|  | <li> | 
|  | If you have additional groups in the menu, publish each id.</li> | 
|  |  | 
|  | <li> | 
|  | Register each context menu with your IWorkbenchPartSite.  If you don't | 
|  | do this the workbench can't extend it.</li> | 
|  |  | 
|  | <li> | 
|  | Define an IActionFilter for each of the objects in your model.  Within | 
|  | this filter, publish the filter attributes.  This allows for more | 
|  | accurate targeting of menu extensions.</li> | 
|  | </ol> | 
|  |  | 
|  | <p><br>The first couple of items have already been discussed.  At | 
|  | this point we will focus on the last point: define an IActionFilter for | 
|  | each object in your model. | 
|  | <h3> | 
|  | The Simple Case of Action Extension</h3> | 
|  | An action can be added to a context menu by defining an extension for the | 
|  | <tt>org.eclipse.ui.popupMenus</tt> | 
|  | extension point in the workbench plug-in.  For instance, the following | 
|  | XML can be used to add the "Word Action" to the context menu for Word objects.  | 
|  | Without going into too many details, the target for the action is specified | 
|  | by declaring an <tt>objectContribution </tt>(<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | with an <tt>objectClass</tt> of <tt>org.eclipse.ui.articles.views.Word | 
|  | </tt>(<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | Once this declaration is made, the action will appear within the context | 
|  | menu for any Word. | 
|  | <pre><extension point="org.eclipse.ui.popupMenus"> | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>     <objectContribution | 
|  |                 id="org.eclipse.ui.articles.pm1" | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             objectClass="org.eclipse.ui.articles.views.Word"> | 
|  |                 <action  | 
|  |                         id="org.eclipse.ui.articles.a1" | 
|  |                         label="Word Action"  | 
|  |                         menubarPath="additions"  | 
|  |                         class="org.eclipse.ui.articles.views.WordActionDelegate"/> | 
|  |         </objectContribution> | 
|  | </extension></pre> | 
|  | The target for an <tt>objectContribution</tt> can be defined using the | 
|  | <tt>objectClass</tt> | 
|  | attribute and an optional <tt>nameFilter</tt> attribute.  The <tt>nameFilter</tt> | 
|  | is used to specify objects with a specific label.  Together, these | 
|  | attributes provide reasonable accuracy when targeting objects. | 
|  | <h3> | 
|  | The Problem Case of Action Extension</h3> | 
|  | In some situations the <tt>objectClass</tt> and <tt>nameFilter</tt> attributes | 
|  | are not enough to describe the intended target object.  For instance, | 
|  | what if we wanted to add an action to a .java file which is read only, | 
|  | or a project which has the java nature?  Luckily, the platform can | 
|  | help. | 
|  | <p>Within an <tt>objectContribution</tt>, a plug-in developer may define | 
|  | <tt>filter</tt> | 
|  | sub elements. Each <tt>filter</tt> describes one attribute of the target | 
|  | object using a name value pair.  For instance, the following XML can | 
|  | be used to target an action at any Word object with <i>name = "Blue"</i>.  | 
|  | Notice how the <tt>filter</tt> sub element (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | is used to define an attribute value pair. | 
|  | <pre><extension point="org.eclipse.ui.popupMenus"> | 
|  |         <objectContribution | 
|  |                 id="org.eclipse.ui.articles.pm2" | 
|  |                 objectClass="org.eclipse.ui.articles.views.Word"> | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             <filter name="name" value="Blue"/> | 
|  |                 <action  | 
|  |                         id="org.eclipse.ui.articles.a2" | 
|  |                         label="Blue Action"  | 
|  |                         menubarPath="additions"  | 
|  |                         class="org.eclipse.ui.articles.views.BlueActionDelegate"/> | 
|  |         </objectContribution> | 
|  | </extension></pre> | 
|  | Now let's look at the implementation of the <tt>filter</tt> sub element.  | 
|  | The attributes for an object are type specific, and beyond the domain of | 
|  | the workbench itself, so the workbench will delegate filtering at this | 
|  | level to the selection itself.  To do this, the platform will test | 
|  | to see if the selected object implements an <tt>org.eclipse.ui.IActionFilter</tt>.  | 
|  | This is a type specific filtering strategy.  If the selection does | 
|  | not implement <tt>IActionFilter</tt>, the platform will ask the selection | 
|  | for a filter using the IAdaptable mechanism defined in org.eclipse.core.runtime.  | 
|  | If a filter is found, the platform will pass each name value pair to the | 
|  | filter to determine if it matches the state of the selected object.  | 
|  | If so, or there is no IActionFilter, the action will be added to the context | 
|  | menu for the object. | 
|  | <p>In order to support the XML above, we need to define an IActionFilter | 
|  | for the Word class.  To do this, we define a class called WordActionFilter | 
|  | (shown below), which simply implements the IActionFilter interface (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | This class is defined as a singleton for memory efficiency (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | It also publishes the filter attributes for the Word class (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | so that they can be referenced outside the plugin. | 
|  | <pre><img SRC="tag_a.jpg" height=13 width=24 align=TEXTTOP> public class WordActionFilter implements IActionFilter { | 
|  |  | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>     public static final String NAME = "name";         | 
|  |         private static WordActionFilter singleton; | 
|  |  | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=TEXTTOP>     public static WordActionFilter getSingleton() { | 
|  |                 if (singleton == null) | 
|  |                         singleton = new WordActionFilter(); | 
|  |                 return singleton; | 
|  |         } | 
|  |                  | 
|  |         /** | 
|  |          * @see IActionFilter#testAttribute(Object, String, String) | 
|  |          */ | 
|  |         public boolean testAttribute(Object target, String name, String value) { | 
|  |                 if (name.equals(NAME)) { | 
|  |                         Word le = (Word)target; | 
|  |                         return value.equals(le.toString()); | 
|  |                 }        | 
|  |                 return false; | 
|  |         } | 
|  | }</pre> | 
|  | The action filter for the Word class will be exposed using the IAdaptable | 
|  | mechanism.  To do this, we need to implement the IAdaptable interface | 
|  | on our Word object, and return the WordActionFilter singleton if the platform | 
|  | asks for an IActionFilter.  The implementation of getAdapter is shown | 
|  | below. | 
|  | <pre>        public Object getAdapter(Class adapter) { | 
|  |                 if (adapter == IActionFilter.class) { | 
|  |                         return WordActionFilter.getSingleton(); | 
|  |                 } | 
|  |                 return null; | 
|  |         }</pre> | 
|  | At this point the WordActionFilter is complete.  If we open the Word | 
|  | view and select a Word with <i>name = Blue</i>, the "Blue Action" will | 
|  | appear in the context menu.  Stepping back from this example, the | 
|  | WordActionFilter is actually fairly useless.  However, it does illustrate | 
|  | how precise action extension targeting should be supported by a model within | 
|  | a view (or editor). | 
|  | <p>In the platform, action filters already exist for markers, resources, | 
|  | and projects.  The attribute names for each are defined in IMarkerActionFilter, | 
|  | IResourceActionFilter, and IProjectActionFilter within org.eclipse.ui, | 
|  | so other plug-in developers can reference them easily.  If we look | 
|  | at an existing, production quality filter like IResourceActionFilter we | 
|  | can see that it contains matching attributes for NAME, EXTENSION, PATH, | 
|  | READ_ONLY and PROJECT_NATURE.  You should define attributes which | 
|  | reflect your own model and permit precise action targeting. | 
|  | <h2> | 
|  | Integration with the Window Menu and Toolbar</h2> | 
|  | In the workbench window menu there are a number of pre-existing actions | 
|  | which target the active part (as indicated by a shaded title bar) .  | 
|  | For instance, if the Delete action within the Edit menu is invoked when | 
|  | the Navigator view is active, the Delete implementation is delegated to | 
|  | the Navigator view.  If the Tasks view is active the Delete implementation | 
|  | is delegated to that view.  The Delete action is just one of many | 
|  | actions, known as <i>global actions</i>, which are always visible within | 
|  | the window menu and, when invoked, delegate their implementation to the | 
|  | active part. | 
|  | <p>In the Word view there are three actions (Add, Delete and Select All).  | 
|  | The complete set of global actions is declared in IWorkbenchActionConstants.GLOBAL_ACTIONS | 
|  | (shown below).  A quick comparison will demonstrate that there is | 
|  | no semantic equivalent to Add, but the Delete (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | and Select all actions (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | are represented within the global actions.  So the Word view will | 
|  | hook both of these actions. | 
|  | <pre>        /** | 
|  |          * From IWorkbenchActionConstants.  | 
|  |          * Standard global actions in a workbench window. | 
|  |          */ | 
|  |         public static final String [] GLOBAL_ACTIONS = { | 
|  |                 UNDO, | 
|  |                 REDO, | 
|  |                 CUT, | 
|  |                 COPY, | 
|  |                 PASTE, | 
|  |                 PRINT, | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER><font color="#000000"><b>             </b>DELETE, | 
|  |                 FIND, | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             SELECT_ALL, | 
|  | </font>                BOOKMARK | 
|  |         }; | 
|  | }</pre> | 
|  | In the WordView class, the global actions are hooked within hookGlobalActions | 
|  | (shown below), which is called from createPartControl.  Within this | 
|  | method, the Word view retrieves the action bars from the workbench part | 
|  | site (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) and then | 
|  | calls setGlobalActionHandler for each action (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | This establishes a link between the global action in the window and the | 
|  | implementation within the view. | 
|  | <pre>        private void hookGlobalActions() { | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             IActionBars bars = getViewSite().getActionBars(); | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             bars.setGlobalActionHandler(IWorkbenchActionConstants.SELECT_ALL, selectAllAction); | 
|  |                 bars.setGlobalActionHandler(IWorkbenchActionConstants.DELETE, deleteItemAction); | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>             viewer.getControl().addKeyListener(new KeyAdapter() { | 
|  |                         public void keyPressed(KeyEvent event) { | 
|  |                                 if (event.character == SWT.DEL &&  | 
|  |                                         event.stateMask == 0 &&  | 
|  |                                         deleteItemAction.isEnabled())  | 
|  |                                 { | 
|  |                                         deleteItemAction.run(); | 
|  |                                 } | 
|  |                         } | 
|  |                 }); | 
|  |         }</pre> | 
|  | On the last line of hookGlobalActions (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | we add a key listener to the viewer control.  If the "delete" key | 
|  | is pressed, the Delete action should run.  In the case of every other | 
|  | action except Delete, the accelerator is defined and implemented by the | 
|  | workbench, so there is no need for a key listener.  In the case of | 
|  | Delete, however, a key listener must be defined in the part.  This | 
|  | extra work is required because the platform cannot define an accelerator | 
|  | containing the delete key.  In doing so, it would break any text editors | 
|  | where the "delete" key has two different behaviors: delete the selection, | 
|  | and delete the next character. | 
|  | <p>Registration of the global action handlers is complete.  If the | 
|  | Select All or Delete action in the window is invoked, and the Word view | 
|  | is active, the corresponding handler within the Word view will be invoked.  | 
|  | The approach taken here can be used for other actions, and you are encouraged | 
|  | to do so.  For instance, most editors provide a handler for all of | 
|  | the global actions.  The Navigator implements the Delete and Bookmark | 
|  | actions.  The Tasks view implements the Delete and Select All actions. | 
|  | <h3> | 
|  | Can I add new actions to the window menu or toolbar?</h3> | 
|  | In general all view actions should be contributed to the local menu, toolbar, | 
|  | or context menu for a view.  There is no way to add view specific | 
|  | actions to a window menu or toolbar. The justification for this is simple: | 
|  | the proximity of view actions to the view itself creates a stronger coupling | 
|  | between the two, and it also helps to reduce clutter on the window menu | 
|  | and toolbar. | 
|  | <h2> | 
|  | Integration with Other Views</h2> | 
|  | No view is an island.  In most cases, a view co-exists with other | 
|  | views and selection within one view may affect the input of another.  | 
|  | In this section we'll create a Listener view, which will listen for the | 
|  | selection of objects in the Word view.  If a Word object is selected, | 
|  | the Listener view will display the word attributes.  This behavior | 
|  | is similar to the existing Properties view in the workbench standard components. | 
|  | <p>To start out, we need to create a new Listener view.  We've already | 
|  | done this twice, so let's skip the declaration within the plugin.xml and | 
|  | concentrate on the ListenerView class (shown below).  This class is | 
|  | very similar to LabelView.  In createPartControl we create a simple | 
|  | SWT Label (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>), where | 
|  | we will display the word attributes. | 
|  | <pre>public class ListenerView extends ViewPart  | 
|  |         implements ISelectionListener | 
|  | { | 
|  |         private Label label; | 
|  |         public ListenerView() { | 
|  |                 super(); | 
|  |         } | 
|  |         public void setFocus() { | 
|  |                 label.setFocus(); | 
|  |         } | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>     public void createPartControl(Composite parent) { | 
|  |                 label = new Label(parent, 0); | 
|  |                 label.setText("Hello World"); | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>             getViewSite().getPage().addSelectionListener(this); | 
|  |         } | 
|  |  | 
|  |         /** | 
|  |          * @see ISelectionListener#selectionChanged(IWorkbenchPart, ISelection) | 
|  |          */ | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>     public void selectionChanged(IWorkbenchPart part, ISelection selection) { | 
|  |                 if (selection instanceof IStructuredSelection) { | 
|  |                         Object first = ((IStructuredSelection)selection).getFirstElement(); | 
|  |                         if (first instanceof Word) { | 
|  |                                 label.setText(((Word)first).toString()); | 
|  |                         } | 
|  |                 } | 
|  |         } | 
|  | }</pre> | 
|  | Things get interesting on the last line of createPartControl (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The Listener view exists in a page with other views.  If a selection | 
|  | is made in any of those views, the platform will forward the selection | 
|  | event to all interested parties.  We are an interested party.  | 
|  | To register our interest, we get the site, get the page, and add the ListenerView | 
|  | as a selection listener.  If a selection occurs within the page, the | 
|  | ListenerView.selectionChanged method will be called (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | Within this method, the selection is examined and, if it is a Word, the | 
|  | label text will be updated to reflect the Word name. | 
|  | <p>So far so good.  The Listener view is ready to receive selection | 
|  | events.  However, we have a problem: the Word view doesn't publish | 
|  | any selection events. | 
|  | <p>To reconcile our problem we add one line of code to the Word view (<img SRC="tag_d.jpg" height=13 width=24 align=ABSCENTER>)  | 
|  | Within the createPartControl method, a selection provider is passed to | 
|  | the site.  Fortunately, the viewer itself is an ISelectionProvider, | 
|  | so it is very easy to define the selection provider for the view.  | 
|  | When the Word view is active (as indicated by shading in the title bar) | 
|  | the platform will redirect any selection events fired from viewer to selection | 
|  | listeners within the page. | 
|  | <pre>        public void createPartControl(Composite parent) { | 
|  |                 // Create viewer. | 
|  |                 viewer = new ListViewer(parent); | 
|  |                 viewer.setContentProvider(new ListContentProvider()); | 
|  |                 viewer.setLabelProvider(new LabelProvider()); | 
|  |                 viewer.setInput(input); | 
|  | <img SRC="tag_d.jpg" height=13 width=24 align=CENTER>             getSite().setSelectionProvider(viewer);         | 
|  |  | 
|  |                 // Create menu and toolbars. | 
|  |                 createActions(); | 
|  |                 createMenu(); | 
|  |                 createToolbar(); | 
|  |                 createContextMenu(); | 
|  |                 hookGlobalActions(); | 
|  |                  | 
|  |                 // Restore state from the previous session. | 
|  |                 restoreState(); | 
|  |         }</pre> | 
|  | Now we can try out the Listener view.  Open up the Word view and the | 
|  | Listener view.  Add a word to the Word view and then select it.  | 
|  | The name of the Word will be displayed in the Listener view.  A snapshot | 
|  | of the result is shown below. | 
|  | <center> | 
|  | <p><img SRC="ListenerView.jpg" height=397 width=534></center> | 
|  |  | 
|  | <p>The linking approach demonstrated here can be described as <i>activation | 
|  | linking</i>.  In activation linking, the listener receives selection | 
|  | from the active part.  The benefit of this approach is that there | 
|  | is a loose coupling between the Listener view and other views within the | 
|  | same page.  If we were to introduce another view, similar to the Word | 
|  | view, which published selection events for objects of type Word, these | 
|  | objects would also appear in the Listener view.  This makes it very | 
|  | easy to add, remove, or replace the views in the workbench while maintaining | 
|  | good integration between views. | 
|  | <p>In contrast to activation linking, a view may also implement <i>explicit | 
|  | linking</i>.  In explicit linking, the listener tracks selection within | 
|  | a specific source view, which is usually discovered by calling <tt>IWorkbenchPage.findView(String | 
|  | id)</tt>.  There are two drawbacks to this approach.  First, | 
|  | the listener will be disabled if the specific source view is nonexistent | 
|  | or closed by the user, and second, the listener will never work with additional | 
|  | views added by the user, or another plug-in.  For this reason, the | 
|  | use of explicit linking is discouraged. | 
|  | <h2> | 
|  | Integration with the WorkbenchPage Input</h2> | 
|  | In a large, real world project the workspace may contain hundreds or thousands | 
|  | of resources.  These resources are split among many projects, containing | 
|  | many folders, containing many files.  It's a tree, and each subtree | 
|  | within the whole defines a physical subset of information.  To avoid | 
|  | the information overload which can sometimes occur by looking at the whole, | 
|  | the user can "open a perspective" on any resource subtree within the workspace.  | 
|  | This is done by selecting a resource in the Navigator view and invoking | 
|  | "Open Perspective".  The result is a new workbench page where only | 
|  | the children of the subtree root are visible.  This subtree root is | 
|  | known as the <i>input</i>. | 
|  | <p>In practice, the visibility of resources within a page is implemented | 
|  | through collaboration between the page and the views within that page.  | 
|  | The page itself is just a visual container for views and editors.  | 
|  | It doesn't provide any presentation for resources.  That is delegated | 
|  | to the parts within the page.  The hand off usually occurs during | 
|  | part creation.  In the early stages of part lifecycle a part can obtain | 
|  | a handle to the containing <tt>IWorkbenchPage,</tt> and from this it can | 
|  | call <tt>IWorkbenchPage.getInput</tt>.  The result can be used as | 
|  | the initial input for the view.  For instance, if a new perspective | 
|  | containing the Navigator is opened, the Navigator will use the page input | 
|  | as its own input.  The Packages view does the same. | 
|  | <p>The strategy should be adopted by any view which displays resources | 
|  | within the workspace.  This will ensure a consistent navigational | 
|  | paradigm for users within the platform. | 
|  | <h2> | 
|  | State Persistence</h2> | 
|  | One of the primary goals for the platform UI is to provide efficient interaction | 
|  | with the workspace.  In the platform this is promoted by saving the | 
|  | state of the workbench when a session ends.  When a new session is | 
|  | started the state of the workbench is recreated.  The state of each | 
|  | window, page, view and editor is persisted between sessions, reducing the | 
|  | time required for the user to get back to work.  In this section we'll | 
|  | examine how the state of the Word view is saved. | 
|  | <p>Within any view there are at least two elements of state: the model | 
|  | itself and model presentation (widget state). In our Word view, the model | 
|  | is stored as a file in the file system, so it is a non issue.  However, | 
|  | the widget state should be saved.  To do this we need to implement | 
|  | the IViewPart.init and IViewPart.saveState methods (shown below) in WordView. | 
|  | <pre>    public void init(IViewSite site,IMemento memento) throws PartInitException; | 
|  |     public void saveState(IMemento memento);</pre> | 
|  | But first, some background info.  When the workbench is closed, the | 
|  | IViewPart.saveState method is called on every view.  An IMemento is | 
|  | passed to the saveState method.  This is a structured, hierarchical | 
|  | object where you can store integers, floats, Strings, and other IMementos.  | 
|  | The platform will store the IMemento data in a file called <tt>workbench.xml</tt> | 
|  | within the <tt>org.eclipse.ui</tt> metadata directory.  When a new | 
|  | session is started, the platform will read this file, recreate each window, | 
|  | page, and view, and then pass an IMemento to each view in the init method.  | 
|  | The view can use this to recreate the state of the previous session. | 
|  | <p>Now we can do some coding.  We will implement the saveState method | 
|  | first (shown below).  Within this method a memento is created for | 
|  | the selection (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>) | 
|  | and then one item is added to it for every word which is selected (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | In our code, the word string is used to identify the word.  This may | 
|  | be an inaccurate way to identify words (two words may have the same string), | 
|  | but this is only an article.  You should develop a more accurate strategy | 
|  | which reflects your own model. | 
|  | <pre>        public void saveState(IMemento memento){ | 
|  |                 IStructuredSelection sel = (IStructuredSelection)viewer.getSelection(); | 
|  |                 if (sel.isEmpty()) | 
|  |                         return; | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             memento = memento.createChild("selection"); | 
|  |                 Iterator iter = sel.iterator(); | 
|  |                 while (iter.hasNext()) { | 
|  |                         Word word = (Word)iter.next(); | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>                     memento.createChild("descriptor", word.toString()); | 
|  |                 } | 
|  |         }</pre> | 
|  | Next we implement the init method.  When a new session is started, | 
|  | the init method will be called just after the part is instantiated, but | 
|  | before the createPartControl method is called.  In our code all of | 
|  | the information within the memento must be restored to the control, so | 
|  | we have no choice but to hold on to the memento until createPartControl | 
|  | is called. | 
|  | <pre>        public void init(IViewSite site,IMemento memento) throws PartInitException { | 
|  |                 init(site); | 
|  |                 this.memento = memento;  | 
|  |         }</pre> | 
|  | Within createPartControl, the restoreState method is called.  Within | 
|  | restoreState the selection memento is retrieved (<img SRC="tag_a.jpg" height=13 width=24 align=ABSCENTER>), | 
|  | and each descriptor within it is mapped to a real Word (<img SRC="tag_b.jpg" height=13 width=24 align=ABSCENTER>).  | 
|  | The resulting array of words is used to restore the selection for the viewer | 
|  | (<img SRC="tag_c.jpg" height=13 width=24 align=ABSCENTER>). | 
|  | <pre>        private void restoreState() { | 
|  |                 if (memento == null) | 
|  |                         return; | 
|  | <img SRC="tag_a.jpg" height=13 width=24 align=CENTER>             memento = memento.getChild("selection"); | 
|  |                 if (memento != null) { | 
|  | <img SRC="tag_b.jpg" height=13 width=24 align=CENTER>                     IMemento descriptors [] = memento.getChildren("descriptor"); | 
|  |                         if (descriptors.length > 0) { | 
|  |                                 ArrayList objList = new ArrayList(descriptors.length); | 
|  |                                 for (int nX = 0; nX < descriptors.length; nX ++) { | 
|  |                                         String id = descriptors[nX].getID(); | 
|  |                                         Word word = input.find(id); | 
|  |                                         if (word != null) | 
|  |                                                 objList.add(word);               | 
|  |                                 } | 
|  | <img SRC="tag_c.jpg" height=13 width=24 align=CENTER>                             viewer.setSelection(new StructuredSelection(objList)); | 
|  |                         } | 
|  |                 } | 
|  |                 memento = null; | 
|  |                 updateActionEnablement(); | 
|  |         }</pre> | 
|  | You may notice certain limitations in the IMemento interface.  For | 
|  | instance, it can only be used to store integers, floats, and Strings.  | 
|  | This limitation is a reflection of the persistence requirements in the | 
|  | platform UI. | 
|  | <br>  | 
|  | <ol> | 
|  | <li> | 
|  | Certain objects need to be saved and restored across platform sessions.</li> | 
|  |  | 
|  | <li> | 
|  | When an object is restored, an appropriate class for an object might not | 
|  | be available. It must be possible to skip an object in this case.</li> | 
|  |  | 
|  | <li> | 
|  | When an object is restored, the appropriate class for the object may be | 
|  | different from the one when the object was originally saved. If so, the | 
|  | new class should still be able to read the old form of the data.</li> | 
|  | </ol> | 
|  |  | 
|  | <p><br>Java serialization could be used for persistence.  However, | 
|  | serialization is only appropriate for RMI and light-weight persistence | 
|  | (the archival of an object for use in a later invocation of the same program).  | 
|  | It is not considered robust for long term storage, where class names and | 
|  | structure may change.  If any of these occur serialization will throw | 
|  | an exception. | 
|  | <h2> | 
|  | State Inheritance</h2> | 
|  | If we take a close look at the standard components in Eclipse an interesting | 
|  | pattern emerges.  Most of them provide some degree of customization.  | 
|  | For instance, in the Navigator view there are two actions in the menu, | 
|  | Sort and Filter, which can be used to sort or select the visible resources | 
|  | in the Navigator.  Similar functionality exists within the Tasks view.  | 
|  | In general, customization is a desirable feature.  In this section | 
|  | we're not going to look at customization itself, as customization is very | 
|  | specific to a particular view and the underlying model.  Instead, | 
|  | we'll look at some different strategies which you could use to share user | 
|  | preferences between views. | 
|  | <p>Let's start out with the assumption that the Word view has two sorting | 
|  | algorithms.  The user may select one as the preferred algorithm.  | 
|  | How do we share this preference with other views of the same type? | 
|  | <p><b>Approach #1: We don't.</b> | 
|  | <p>The initial value of the preference will be hard coded in some way, | 
|  | probably as a constant in the class declaration.  If the user changes | 
|  | this preference in one view, it will not be reflected in other views.  | 
|  | For instance, the Navigator preference for "Sort" is a local preference, | 
|  | and is not shared with other Navigator views. | 
|  | <p><b>Approach #2: We Use a Preference Page</b> | 
|  | <p>The initial value of the preference will be exposed in a preference | 
|  | page in the Workbench Preferences.  If the user changes this preference, | 
|  | the new preference will apply to all instances of the view.  For instance, | 
|  | the Navigator preference for "Link Navigator selection to active editor" | 
|  | is controlled by a check box in the Workbench Preferences.  This option | 
|  | applies to all Navigator views. | 
|  | <p><b>Approach #3: Inheritance.</b> | 
|  | <p>The initial value of the preference will be defined in the plugin metadata.  | 
|  | If a view is opened, it will read the plugin metadata to determine the | 
|  | preference.  If the user changes the preference in one view, it will | 
|  | not affect other views, but it will be saved to the plugin metadata, so | 
|  | that any view created in the future will inherit the preference.  | 
|  | For instance, if the user changes the sort preference in one Word view, | 
|  | it will have no affect on other Word views.  However, if the user | 
|  | opens a new Word view it will inherit the most recent preference. | 
|  | <p>While each of these is a valid approach, the correct choice should reflect | 
|  | your own view and the underlying model. | 
|  | <h2> Summary</h2> | 
|  | In this article we have examined the design and implementation of two views, | 
|  | a simple Label view and a more comprehensive Word view.  The features | 
|  | of your own view will probably vary greatly from what we have created.  | 
|  | However, the integration of your view with the workbench will follow the | 
|  | same pattern demonstrated by these views. Further information on view implementation | 
|  | is available in the Platform Plug-in Developer Guide and in the javadoc | 
|  | for <tt>org.eclipse.ui</tt>. | 
|  |  | 
|  | <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> |