Previous Page
Next Page

6.3. Object Actions

Suppose you want to make it easy for the user to add files and folders to the Favorites view. Object contributions are ideal for this because they appear in context menus only when the selection in the current view or editor contains an object compatible with that action (see Figure 6-8). In this manner, an object contribution is available to the user when he or she needs the action, yet not intrusive when the action does not apply.

Figure 6-8. Object action.


6.3.1. Defining an object-based action

As in Section 6.2.1, Defining a workbench window menu, on page 209 and subsequent sections, use the Extensions page of the plug-in manifest editor to create the new object contribution. Click on the Add button to add an org.eclipse.ui.popupMenus extension, then add an objectContribution with the following attributes:

adaptable "true"

Indicates that objects that adapt to IResource are acceptable targets (see Section 20.3, Adapters, on page 714).

id "com.qualityeclipse.favorites.popupMenu"

The unique identifier for this contribution.

nameFilter Leave blank

A wildcard filter specifying the names that are acceptable targets. For example, entering "*.java" would target only those files with names ending with .java. More on this in Section 6.3.2, Action filtering and enablement, on page 227.

objectClass "org.eclipse.core.resources.IResource"

The type of object that is an acceptable target. Use the Browse... button at the right of the objectClass field to select the existing org.eclipse.core.resources.IResource class. If you want to create a new class, then click the objectClass: label to the left of the objectClass field.

Next, add an action to the new objectContribution with the following attribute values, which are very similar to the action attributes covered in Section 6.2.3, Defining a menu item and toolbar button, on page 212.

class "com.qualityeclipse.favorites.actions. AddToFavoritesActionDelegate" The action delegate for the action that implements the org.eclipse.ui.IObjectActionDelegate interface (see Section 6.3.3, IObjectActionDelegate, on page 233). The class is instantiated using its no argument constructor, but can be parameterized using the IExecutableExtension interface (see Section 20.5, Types Specified in an Extension Point, on page 723). The class can be specified in one of three different ways as in Section 6.2.6, Creating an action delegate, on page 216.

enablesFor "+"

An expression indicating when the action will be enabled (see Section 6.3.2, Action filtering and enablement, on page 227).

id "com.qualityeclipse.favorites.addToFavorites"

The unique identifier for the action.

label "Add to Favorites"

The text that appears in the context menu for the action.

menubarPath "additions"

The insertion point for the action (see Section 6.2.5, Insertion points, on page 215).

tooltip "Add the selected resource(s) to the Favorites view"

The text that appears when the mouse hovers over the menu item in the context menu.

Multiple actions appear in reverse order

Buried in the org.eclipse.ui.popupMenu extension point documentation is the following nugget of information: "If two or more actions are contributed to a menu by a single extension, the actions will appear in the reverse order of how they are listed in the plugin.xml file. This behavior is admittedly unintuitive. However, it was discovered after the Eclipse Platform API was frozen. Changing the behavior now would break every plug-in that relies on the existing behavior."


Other available action attributes not used in this example include:

helpContextId The identifier for the help context associated with the action (see Chapter 15, Implementing Help).

icon The associated image (see Section 6.2.4, Action images, on page 214).

overrideActionId An optional attribute specifying the identifier for an action that the action overrides.

state For an action with either the radio or toggle style, set the initial state to true or false (see Section 6.2.3, Defining a menu item and toolbar button, on page 212).

style An attribute defining the visual form of the action. This is covered in Section 6.2.3, Defining a menu item and toolbar button, on page 212, with the exception that the pulldown style does not apply to object contributions.

6.3.2. Action filtering and enablement

In keeping with the lazy loading plug-in theme, Eclipse provides multiple declarative mechanisms for filtering actions based on the context, and enabling visible actions only when appropriate. Because they are declared in the plug-in manifest, these mechanisms have the advantage that they do not require the plug-in to be loaded for Eclipse to use them.

6.3.2.1. Basic filtering and enablement

In Section 6.3.1, Defining an object-based action, on page 224, the nameFilter and objectClass attributes are examples of filters, while the enablesFor attribute determines when an action will be enabled. When the context menu is activated, if a selection does not contain objects with names that match the wildcard nameFilter or are not of a type specified by the objectClass attribute, none of the actions defined in that object contribution will appear in the context menu. In addition, the enablesFor attribute uses the syntax in Table 6-1 to define exactly how many objects need to be selected for a particular action to be enabled:

Table 6-1. enabledFor attribute options

Syntax

Description

!

0 items selected.

?

0 or 1 items selected.

+

1 or more items selected.

multiple, 2+

2 or more items selected.

n

A precise number of items selected; for example, enablesFor="4" enables the action only when 4 are selected.

*

Any number of items selected.


The techniques listed in this table represent those most commonly used for limiting visibility and the enablement of actions; occasionally, a more refined approach is needed. The visibility and filter elements provide an additional means to limit an action's visibility, while the selection and enablement elements provide a more flexible way to specify when an action is enabled. Still further refinement of action enablement can be provided by using the selection Changed() method in the action delegate, as discussed in Section 6.2.6, Creating an action delegate, on page 216.

6.3.2.2. The visibility element

The visibility element provides an alternate and more powerful way to specify when an object contribution's actions will be available to the user as compared with the object contribution's nameFilter and objectClass. For example, an alternate way to specify filtering for the object contribution just described would be:

<objectContribution ...
  <visibility>
      <objectClass
         name="org.eclipse.core.resources.IResource"/>
  </visibility>
   ...the other stuff here...
</objectContribution>

If the action is to be visible only for resources that are not read-only, then the visibility object contribution might look like this:

<objectContribution ...>
   <visibility>
      <and>
         <objectClass
            name="org.eclipse.core.resources.IResource"/>
         <objectState name="readOnly" value="false"/>
      </and>
    </visibility>
   ... the other stuff here ...
</objectContribution>

As part of the <visibility> element declaration, you can use nested <and>, <or>, and <not> elements for logical expressions, plus the following Boolean expressions.

adapt Adapts the selected object to the specified type then uses the new object in any child expressions. For example, if you wanted to adapt the selected object (see Section 20.3, Adapters, on page 714) to a resource and then test some resource object state, the expression would look like this:

<adapt type="org.eclipse.core.resources.IResource">
   <objectState name="readOnly" value="false"/>
</adapt>

The children of an adapt expression are combined using the and operator. The expression returns EvaluationResult.NOT_LOADED if either the adapter or the type referenced isn't loaded yet. It throws an ExpressionException during evaluation if the type name doesn't exist.

and Evaluates TRue if all subelement expressions evaluate TRue.

instanceof Compares the class of the selected object against a name. This is identical to the objectClass element, except that instanceof can be combined with other elements using the and and or elements.

not Evaluates true if its subelement expressions evaluates false.

objectClass Compares the class of the selected object against a name as shown above.

objectState Compares the state of the selected object against a specified state similar to the filter element (see Section 6.3.2.3, The filter element, on page 230).

or Evaluates true if one subelement expression evaluates true.

pluginState Compares the plug-in state, indicating whether it is installed or activated. For example, an expression such as <pluginState id="org.eclipse.pde" value="installed"/> would cause an object contribution to be visible only if the org.eclipse.pde plug-in is installed, and an expression such as <pluginState id="org.eclipse.pde" value="activated"/>would cause an object contribution to be visible only if the org.eclipse.pde plug-in has been activated in some other manner.

systemProperty Compares the system property. For example, if an object contribution should only be visible when the language is English, then the expression would be: <systemProperty name="user.language" value="en"/>

systemTest Identical to the systemProperty element, except that systemTest can be combined with other elements using the and and or elements.

test Evaluate the property state of the object. For example, if an object contribution should only be visible when a resource in a Java project is selected, then the expression would be:

<test
    property="org.eclipse.debug.ui.projectNature"
    value="org.eclipse.jdt.core.javanature"/>

The test expression returns EvaluationResult.NOT_LOADED if the property tester doing the actual testing isn't loaded yet. The set of testable properties can be extended using the org.eclipse.core.expressions.propertyTesters extension point. One example of this is the org.eclipse.debug.internal.ui.ResourceExtender class.

6.3.2.3. The filter element

The filter element is a simpler form of the objectState element discussed previously. For example, if the object contribution was to be available for any file that is not read-only, then the object contribution could be expressed like this:

<objectContribution ...>
   <filter name="readOnly" value="false"/>
   ... the other stuff here ...
</objectContribution>

As with the objectState element, the filter element uses the IActionFilter interface to determine whether an object in the selection matches the criteria. Every selected object must either implement or adapt to the IActionFilter interface (more on adapters in Chapter 20, Advanced Topics) and implement the appropriate behavior in the testAttribute() method to test the specified name/value pair against the state of the specified object. For resources, Eclipse provides the following built-in state comparisons as listed in the org.eclipse.ui.IResourceActionFilter class:

name Comparison of the filename. "*" can be used at the start or end to represent "one or more characters."

extension Comparison of the file extension.

path Comparison against the file path. "*" can be used at the start or end to represent "one or more characters."

readOnly Comparison of the read-only attribute of a file.

projectNature Comparison of the project nature.

persistentProperty Comparison of a persistent property on the selected resource. If the value is a simple string, then this tests for the existence of the property on the resource. If it has the format propertyName=propertyValue, this obtains the value of the property with the specified name and tests it for equality with the specified value.

projectPersistentProperty Comparison of a persistent property on the selected resource's project with similar semantics to the persistentProperty listed above.

sessionProperty Comparison of a session property on the selected resource with similar semantics to the persistentProperty just listed.

projectSessionProperty Comparison of a session property on the selected resource's project with similar semantics to persistentProperty.

6.3.2.4. The selection element

The selection element is a technique for enabling an individual action based on its name and type, similar to the way that the nameFilter and objectClass attributes determine whether all actions in an object contribution are visible. For example, an alternate form for the object contribution using the selection element would be:

<objectContribution
      objectClass="java.lang.Object"
      id="com.qualityeclipse.favorites.popupMenu">
   <action
         label="Add to Favorites"
         tooltip="Add the selected resource(s) to the
                  Favorites view"
         class="com.qualityeclipse.favorites.actions.
                AddToFavoritesActionDelegate"
         menubarPath="additions"
         enablesFor="+"
         id="com.qualityeclipse.favorites.addToFavorites">
      <selection
            class="org.eclipse.core.resources.IResource"
            name="*.java"/>
   </action>
</objectContribution>

With this declaration, the object contribution's actions would always be visible, but the Add to Favorites action would only be enabled if the selection contained only implementers of IResource that matched the name filter *.java.

6.3.2.5. The enablement element

The enablement element is a more powerful alternative to the selection element, supporting the same complex conditional logic expressions and comparisons as the visibility element (see Section 6.3.2.2, The visibility element, on page 228). For example, an alternate object contribution declaration to the one outlined in the previous section, but that produces the same behavior would be:

<objectContribution
      objectClass="java.lang.Object"
      id="com.qualityeclipse.favorites.popupMenu">
   <action
         label="Add to Favorites"
         tooltip="Add the selected resource(s)
                  to the Favorites view"
         class="com.qualityeclipse.favorites.actions.
                AddToFavoritesActionDelegate"
         menubarPath="additions"
         enablesFor="+"
         id="com.qualityeclipse.favorites.addToFavorites">
      <enablement>
         <and>
            <objectClass
               name="org.eclipse.core.resources.IResource"/>
             <objectState name="name" value="*.java"/>
         </and>
      </enablement>
   </action>
</objectContribution>

6.3.2.6. Content-sensitive object contributions

There is a new mechanism for filtering actions based on resource content. This filtering is specified in the plug-in manifest (does not load your plug-in) and determines whether an action should be visible or enabled by inspecting a file's content. For example, the Run Ant... command is associated with resources named build.xml, but no others; what if your Ant script is located in a file called export.xml? This new mechanism can determine whether the Run Ant... command should be visible based on the first XML tag or DTD specified in the file. In this case, the org.eclipse.ant.core plug-in defines a new antBuildFile content type:

<extension point="org.eclipse.core.runtime.contentTypes">
   <content-type
      id="antBuildFile"
      name="%antBuildFileContentType.name"
      base-type="org.eclipse.core.runtime.xml"
      file-names="build.xml"
      file-extensions="macrodef,ent,xml"
      priority="normal">
      <describer
         class="org.eclipse.ant.internal.core.
            contentDescriber.AntBuildfileContentDescriber">
      </describer>
   </content-type>
</extension>

The preceding declaration associates the antBuildFile content type with the AntBuildfileContentDescriber class, which determines whether XML content is Ant content. The antBuildFile content type can then be used to specify action visibility and enablement, editor association, and more. For more about declaring and using your own content types, see the following:

  • "Content Sensitive Object Contributions" at eclipse.org > projects > The Eclipse Project > Platform > UI > Development Resources > Content Sensitive Object Contributions, or browse dev.eclipse.org/viewcvs/index.cgi/~checkout~/platform-ui-home/object-aware-contributions/objCont.htm.

  • "Content types" in the Eclipse Help System at Help > Help Contents > Platform Plug-in Developer Guide > Programmer's Guide > Runtime overview > Content types

  • "A central content type catalog for Eclipse" at dev.eclipse.org/viewcvs/index.cgi/platform-core-home/documents/content_types.html?rev=1.11

  • "Content types in Eclipse" at eclipse.org/eclipse/platform-core/planning/3.0/plan_content_types.html

6.3.3. IObjectActionDelegate

Getting back to the Favorites plug-in, the next task is to create an action delegate that implements the IObjectActionDelegate interface, which performs the operation behind the new Add to Favorites menu item. Create a new AddToFavoritesActionDelegate class as described next. Since the Favorites view is not fully functional, the action displays a message rather than adding the selected items to the view (see Section 7.3.1, Model actions, on page 283 for more implementation details).

Start by selecting the action defined in Section 6.3.1, Defining an object-based action, on page 224 and then clicking on the class: label to the left of the class field. This opens the Java Attribute Editor dialog for creating a new Java class. Fill in the package and class name fields as necessary and be sure to add IObjectActionDelegate as the interface to implement, then click Finish to generate the new class.

Next, add a new field and modify the setActivePart() method to cache the view or editor in which the action appears:

private IWorkbenchPart targetPart;

public void setActivePart(IAction action, IWorkbenchPart part) {
   this.targetPart = part;
}

Finally, modify the run() method to open a message dialog indicating that this action was successfully executed. As mentioned before, this action delegate will be fleshed out in Section 7.3.1, Model actions, on page 283.

public void run(IAction action) {
   MessageDialog.openInformation(
         targetPart.getSite().getShell(),
         "Add to Favorites",
         "Triggered the " + getClass().getName() + " action");
}

6.3.4. Creating an object-based submenu

Menus can be contributed to a context menu in a manner similar to adding actions. If three or more similar actions are contributed, then think about placing those actions in a submenu rather than in the context menu itself. The Favorites plug-in only adds one action to the context menu, but let's place the action in a submenu rather than in the context menu itself.

To create the Favorites menu, right-click on the com.qualityeclipse.- favorites.popupMenu object contribution in the Extensions page of the plug-in manifest editor, and select New > menu. Enter the following values for this new menu:

id "com.qualityeclipse.favorites.popupSubMenu"

The identifier for the submenu.

label "Favorites"

The text appearing in the context menu as the name of the submenu.

path "additions"

The insertion point that determines the location in the context menu where the submenu will appear (see Section 6.2.5, Insertion points, on page 215).

Next, add a groupMarker to the menu with the name "content" and a separator with the name "additions" (see Section 6.2.2, Groups in a menu, on page 212). Finally, modify the Add to Favorites action's attributes as follows so that the action will now be part of the new Favorites submenu:

label "Add"

The text appearing in the submenu as the name of the action.

menubarPath "com.qualityeclipse.favorites.popupSubMenu/content"

The insertion point that determines where the Favorites submenu action will appear (see Section 6.2.5, Insertion points, on page 215).

6.3.5. Manually testing the new action

When the Favorites Runtime Workbench configuration is launched (see Section 2.6, Debugging the Product, on page 88), any context menu activated on a workbench resource will contain the Favorites menu with the Add submenu item. Selecting this submenu item displays a message box notifying you that the action was indeed triggered correctly.

The Favorites action is only displayed when one or more objects are selected due to enablesFor="+" in the declaration shown in Section 6.3.2.5, The enablement element, on page 231. This means that when you test the Favorites menu, you must have at least one project created in your runtime workbench and select at least one resource in the Navigator view when you activate the context menu. If you right click without selecting anything, you will only see an abbreviated context menu that does not have the Favorites menu.

6.3.6. Adding a test for the new action

The last task is to create an automated test that triggers the action and validates the result. Because this operation displays a message rather than adding a resource to the Favorites view, the code that validates the results of this test will have to wait until the next chapter (see Section 7.6, Testing, on page 314), where the Favorites view will be more fully developed. For now, create the following new test case in the Favorites test project and then modify the Favorites test suite to include this new test (see Section 6.2.8, Adding a test for the new action, on page 220).

You can begin by creating a new AddToFavoritesTest class that extends AbstractFavoritesTest and adds it to the Favorites test suite.

package com.qualityeclipse.favorites.test;

import ...

public class AddToFavoritesTest extends AbstractFavoritesTest {

   public AddToFavoritesTest(String name) {
      super(name);
   }

Next add a field and override the setUp() method to create a temporary project called "TestProj" for the duration of this test. The tearDown() method deletes this temporary project when the test is complete.

To get these changes to properly compile, edit the Favorites test project's plug-in manifest and add the org.eclipse.core.resources plug-in to the Required Plug-ins list (see Figure 2-10 on page 73).

protected IProject project;

protected void setUp() throws Exception {
   super.setUp();
   IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
   project = root.getProject("TestProj");
   project.create(null);
   project.open(null);
}

protected void tearDown() throws Exception {
   super.tearDown();

   // Wait for a bit for the system to catch up
   // so that the delete operation does not collide
   // with any background tasks.
   delay(3000);
   waitForJobs();

   project.delete(true, true, null);
}

Finally, add the method that exercises the new menu item for adding objects to the Favorites view.

public void testAddToFavorites() throws CoreException {

    // Show the resource navigator and select the project.
   IViewPart navigator = PlatformUI.getWorkbench()
         .getActiveWorkbenchWindow().getActivePage().showView(
                "org.eclipse.ui.views.ResourceNavigator");
   StructuredSelection selection = new StructuredSelection(project);
   ((ISetSelectionTarget) navigator).selectReveal(selection);

   // Execute the action.
   final IObjectActionDelegate delegate
      = new AddToFavoritesActionDelegate();
    IAction action = new Action("Test Add to Favorites") {
       public void run() {
         delegate.run(this);
      }
   };
   delegate.setActivePart(action, navigator);
   delegate.selectionChanged(action, selection);
   action.run();

   // Add code here at a later time to verify that the
   // Add to Favorites action correctly added the
   // appropriate values to the Favorites view.
}


Previous Page
Next Page