# Actions

Every Sapphire UI part can expose operations that the user can invoke. These operations are called actions. While Sapphire does not prescribe how actions are surfaced to the user, a typical presentation would use buttons, menu items or links.

This documents covers the API for defining actions, contributing handlers and other related topics.

Table of Contents

1. Actions
2. Handlers
3. Handler Factories
4. Filters
5. Links
6. Contexts
7. Extensions

## Actions

An action is an abstract definition of an operation that a user may want to perform. It does not have a concrete implementation. That is provided by handlers that are attached separately. An action simply defines appearance, applicability and access.

An action that does not have at least one active handler will not be presented to the user. This makes it possible to define actions fairly widely and only provide handlers in situations where it makes sense. By defining the action once instead of on every use, we are guaranteed the consistency of presentation.

Example

In this example, a message box action is declared. Note that the definition includes appearance (label and image) and presentation (location hints and key binding), but no sign of the implementation. The handler would need to be contributed separately. The handler definition would reference action id.

    <property-editor>
        <action>
            <id>MessageBoxAction</id>
            <label>message</label>
            <image>org/eclipse/sapphire/samples/gallery/BalloonYellow.png</image>
            <location>after:Sapphire.Browse</location>
            <location>before:Sapphire.Create</location>
            <key-binding>SHIFT+CONTROL+ALT+m</key-binding>
        </action>
    </property-editor>

Details

Element | Cardinality | Description
--- | --- | ---
condition | 0 or 1 | A condition allows use of arbitrary logic to control whether the action is going to be available or not in a given situation. Conditions must extends SapphireCondition class.
context | 0 or more | Every UI part that supports actions will define one or more context. An action can be constrained to apply only to the specified contexts. If no context is specified, the action will be treated as applicable to all contexts.
description | 0 or 1 | Provides more information about the action than what is available in the label. The description should be in the form of properly capitalized and punctuated sentences.
group | 0 or 1 | Specifies the ID of the group that this action belongs to, if any. Groups are used to visually segregate actions. Groups are created on first use. They do not need to be explicitly defined. The exact way to segregate groups is dependent on the presentation, but typical presentations use separator lines.
hint | 0 or more |
Element | Cardinality | Description
--- | --- | ---
name | 1 |
value | 1 |
id | 1 | An action ID serves to uniquely identify an action for a variety of purposes, such as defining an action handler or controlling ordering of actions via location hints. An ID only needs to be unique in the contexts where the action is expected to be used. To help avoid conflicts with other extensions, prefix the ID with company, organization or product name.
image | 0 or more | One or more images can be associated with an action to be used by the presentation as appropriate. If an image is necessary, the presentation will look for an image of the appropriate size.
key-binding | 0 or 1 | A key binding defines how an action is accessed via the keyboard as a sequence of key presses. Separate multiple keys in a sequence with a '+' (plus) character. Example: CONTROL+SHIFT+A Legal Keys Any character from ASCII. ALT CONTROL SHIFT DEL ARROW_UP ARROW_DOWN
key-binding-behavior | 0 or 1 | A key binding can be restricted to the action's part (local) or propagated to child parts. Key bindings on local actions take priority over propagated actions.
label | 1 | An action label is used when presenting the action to the user. The exact way that the label is used is dependent on the presentation, but typical presentations use labels for menu item names and as tooltips. A label should only capitalize words that must remain capitalized regardless of context (proper nouns and acronyms). Words that are not already capitalized will be capitalized as necessary depending on how the label is used by the presentation.
tooltip | 0 or 1 |
type | 0 or 1 | An action can be a push action or a toggle action. Unlike a push action, a toggle action has distinct on/off states that are made visible by the presentation. The exact method for how a toggle is presented is dependent on the presentation, but typical presentations use check marks next to menu items or recessed state for buttons.

## Handlers

An action handler is what performs the actual work for an action. Handlers can be contributed by multiple parties and multiple handlers can be active at the same time. If multiple handlers are active, the user will typically be given a choice of which handler to invoke. Exactly how the choice is presented is dependent on the presentation layer, but a common approach is to use a cascading menu or a popup menu that is activated when user clicks on an action button.

Example

In this example, a handler is contributed for a browse action. The handler simply opens a dialog with a message.

    <property-editor>
        <action-handler>
            <action>Sapphire.Browse</action>
            <impl>MessageBoxActionHandler</impl>
        </action-handler>
    </property-editor>

    public class MessageBoxActionHandler extends SapphireActionHandler
    {
        @Override
        protected Object run( SapphireRenderingContext context )
        {
            MessageDialog.openInformation( context.getShell(), "Message", "Hello World!" );
            return null;
        }
    }

Details

Element | Cardinality | Description
--- | --- | ---
action | 1 | The ID of the action that this handler is for.
condition | 0 or 1 | A condition allows use of arbitrary logic to control whether the action handler is going to be available or not in a given situation. Conditions must extends SapphireCondition class.
context | 0 or more | Every UI part that supports actions will define one or more context. An action handler can be constrained to apply only to the specified contexts. If no context is specified, the action handler will be treated as applicable to all contexts.
description | 0 or 1 | Provides more information about the action handler than what is available in the label. The description should be in the form of properly capitalized and punctuated sentences.
id | 0 or 1 | An action handler ID serves to uniquely identify an action for a variety of purposes, such as controlling ordering of action handlers via location hints. An ID only needs to be unique in the contexts where the action handler is expected to be used. To help avoid conflicts with other extensions, prefix the ID with company, organization or product name. Note that action handler ID can also be set in the implementation.
image | 0 or more | One or more images can be associated with an action handler to be used by the presentation as appropriate. If an image is necessary, the presentation will look for an image of the appropriate size. Note that images can also be set in the implementation.
impl | 1 | The action handler implementation class. Must extend SapphireActionHandler.
label | 0 or 1 | An action handler label is used when presenting the action handler to the user. The exact way that the label is used is dependent on the presentation, but typical presentations use labels for menu item names and as tooltips. A label should only capitalize words that must remain capitalized regardless of context (proper nouns and acronyms). Words that are not already capitalized will be capitalized as necessary depending on how the label is used by the presentation. Note that action handler label can also be set in the implementation.
param | 0 or more | Parameters that can be interpreted by the action handler.
Element | Cardinality | Description
--- | --- | ---
name | 1 | The name of the parameter.
value | 1 | The value of the parameter.
tooltip | 0 or 1 |

## Handler Factories

Sometimes it is necessary to be able to create handlers dynamically based on information that isn't known until runtime. When you contribute a handler, that handler is either activated or not depending on the provided condition. When you contribute a handler factory, the factory can decide at runtime how many handlers to contribute.

Example

In this example, a handler factory is contribute for a browse action. The factory in turn contributes a handler for every file in the specified folder. Activating one of these handlers sets the property value to the absolute path of the file.

    <property-editor>
        <action-handler-factory>
            <action>Sapphire.Browse</action>
            <impl>KeyFolderBrowseHandlerFactory</impl>
            <param>
                <name>folder</name>
                <value>c:\samples</value>
            </param>
        </action-handler-factory>
    </property-editor>

    public class KeyFolderBrowseHandlerFactory extends SapphireActionHandlerFactory
    {
        private File folder;

        @Override
        public void init( SapphireAction action, ISapphireActionHandlerFactoryDef def )
        {
            super.init( action, def );
            this.folder = new File( def.getParam( "folder" ) );
        }

        @Override
        public List create()
        {
            List handlers = new ArrayList();

            for( File file : this.folder.listFiles() )
            {
                if( file.isFile() )
                {
                    handlers.add( new Handler( file ) );
                }
            }

            return handlers;
        }

        private static class Handler extends SapphireBrowseActionHandler
        {
            private final File file;

            public Handler( File file )
            {
                this.file = file;
                setLabel( this.file.getName() );
            }

            @Override
            protected String browse( Presentation context )
            {
                return this.file.getAbsolutePath();
            }
        }
    }

Details

Element | Cardinality | Description
--- | --- | ---
action | 1 | The ID of the action that this factory is to provide handlers for.
condition | 0 or 1 | A condition allows use of arbitrary logic to control whether the action handler factory is going to be available or not in a given situation. Conditions must extends SapphireCondition class.
context | 0 or more | Every UI part that supports actions will define one or more context. An action handler factory can be constrained to apply only to the specified contexts. If no context is specified, the action handler factory will be treated as applicable to all contexts.
description | 0 or 1 | Provides information about the action handler factory. The description should be in the form of properly capitalized and punctuated sentences.
impl | 1 | The action handler factory implementation class. Must extend SapphireActionHandlerFactory.
param | 0 or more | Parameters that can be interpreted by the action handler factory.
Element | Cardinality | Description
--- | --- | ---
name | 1 | The name of the parameter.
value | 1 | The value of the parameter.

## Filters

In some scenarios it is useful to be able to suppress an action handler for a particular UI part that might otherwise be relevant. The action handler in question may have been contributed globally via Sapphire's extensions mechanism, but it just doesn't make sense in a given context. The mechanism for accomplishing this is action handler filters.

Filters are specified when defining a UI part. A filter implementation must extend SapphireActionHandlerFilter class. Note that a filter returns true for what should be kept, so false should be returned for handlers that should be filtered out.

Example

In this example, a very simple filter is applied that removes all browse action handlers. Since an action will not be surfaced unless it has active handlers, this is equivalent to removing the browse action.

    <property-editor>
        <action-handler-filter>
            <impl>MyFilter</impl>
        </property-editor>
    </property-editor>

    public class MyFilter extends SapphireActionHandlerFilter
    {
        @Override
        public boolean check( SapphireActionHandler handler )
        {
            return ( ! handler.getAction().getId().equals( "Sapphire.Browse" ) );
        }
    }

Details

Element | Cardinality | Description
--- | --- | ---
context | 0 or more | Every UI part that supports actions will define one or more context. An action handler filter can be constrained to apply only to the specified contexts. If no context is specified, the action handler filter will be treated as applicable to all contexts.
impl | 1 | The action handler filter implementation class. Must extend SapphireActionHandlerFilter.

## Links

Sometimes it is useful to present an adhoc link that is wired a specific action. This can be done with an action link, which is a UI part in its own right. An action link specifies the ID of the action that should be invoked when user clicks on the link. The action and its handler can be defined locally in the action link's definition. Alternatively, the part hierarchy above the action link is searched until the action is found.

Example

In this example from the contacts sample, an "Add a contact" link is placed in the section associated with the "Contacts" node in the content outline. The Contacts node has the add action due to the way its child nodes are defined. Since the Contacts node is the nearest ancestor part to the action link that defines the add action, that is the action that ends up bound to the link.

    <action-link>
        <action-id>Sapphire.Add</action-id>
        <label>Add a contact</label>
    </action-link>

## Contexts

Every UI part that supports actions must define one or more context. Actions are contributed to a context and the UI part displays all actions contributed to a context at the appropriate location.

The following context are currently available:

Context | Typical Presentation
--- | ---
Sapphire.Actuator | A stand-alone link or button.
Sapphire.EditorPage | Buttons in the editor page header.
Sapphire.EditorPage.Outline | Menu items in the content outline's context menu.
Sapphire.EditorPage.Outline.Header | Buttons directly above the content outline.
Sapphire.EditorPage.Outline.Node | Menu items in the content outline's context menu.
Sapphire.ElementPropertyEditor | Buttons next to the property editor.
Sapphire.ListPropertyEditor | Buttons next to the property editor.
Sapphire.Section | Buttons in the section header.
Sapphire.ValuePropertyEditor | Buttons next to the property editor.
Sapphire.Diagram.Editor | Menu items in the diagram editor's context menu.
Sapphire.Diagram.Node | Menu items in the diagram node's context menu.
Sapphire.Diagram.Connection | Menu items in the diagram connection's context menu.
Sapphire.WithDirective | Key binding in the with directive.

## Extensions

While actions, handlers and handler factories can be contributed directly in a part's definition, sometimes it is more useful to contribute them in a system-wide fashion. This can be done using Sapphire's extension system.

To contribute an extension, create sapphire-extension.xml file in the META-INF folder. The extension must be located in the same classloader as Sapphire. On an OSGi system this is done by creating a fragment to the org.eclipse.sapphire.ui bundle.

The semantics and syntax of the contribution is identical to what is used in a part's definition. The biggest difference is that there is no implied scope, so it is very important to specify context and condition. If neither is specified, the contribution will be active for all UI parts, which is not very useful.

The listing of all extensions currently contributed can be found in the documentation.

Example

In this example, a message box action is contributed to all value property editors.

    <extension xmlns="http://www.eclipse.org/sapphire/xmlns/extension">
        <action>
            <id>MessageBoxAction</id>
            <label>message</label>
            <image>org/eclipse/sapphire/samples/gallery/BalloonYellow.png</image>
            <location>after:Sapphire.Browse</location>
            <location>before:Sapphire.Create</location>
            <key-binding>SHIFT+CONTROL+ALT+m</key-binding>
            <context>Sapphire.ValuePropertyEditor</context>
        </action>
    </extension>

Copyright (c) 2016 Oracle
Content made available under the terms of Eclipse Public License.