Events and actions

In the previous chapter about the component builder tag library we explained how different types of UI controls can be constructed and combined to graphical user interfaces. Now it is time to fill these user interfaces with functionality. Many applications require that manipulations on controls can be tracked so that they can react accordingly. Examples of such manipulations are a button that is pressed, a change in the selection of a list box, or text being entered into a text field. The UI logic of an application often demands that such manipulations affect other parts of the UI. Maybe the enabled state of some controls depends on the data entered into other controls, or detail views have to change so that they display the data of a selected table element.

In this chapter we discuss how these use cases are addressed by the JGUIraffe library. One key component in this area is another Jelly tag library that provides tags for registering listeners at components and for defining actions to be executed in reaction of changes on controls: the action builder tag library. Using this tag library it is also possible to define menus and tool bars that can be associated with windows. We will discuss these topics in the following sub sections.

Basics of the event system

Most UI toolkits for Java use the classic observer pattern for dealing with manipulations on UI components: In order to be notified about such manipluations, client code has to implement specific listener interfaces and to register itself at the components of interest. Whenever a certain change in the status of a monitored component occurs, all registered listeners are called. Typically the methods of the listener interface expect a specific parameter object that contains information about the manipulation. These parameter objects are often called events, and the listeners registered at UI components are called event listeners.

Toolkits like Swing and SWT follow this approach. They define a multitude of event listener interfaces and corresponding event objects to allow an application to keep track of the several UI components it is comprised of. JGUIraffe also uses the concept of event listeners, but in contrast to the toolkits mentioned above, only a very limited number of events is supported. The philosophy is to abstract from low-level events; rather, only a few logic event types are defined. To make this clearer have a look at the many different event types Swing uses to indicate that a component has been changed in some way: a change of a text component is treated differently than a change of the selection of a list, a tree, or a table. For all these kinds of changes there are different event listener interfaces and event objects. JGUIraffe uses a single logic change event for all these components. This simplifies programming and should be sufficient in most cases. Typically the many different event listener interfaces do not provide any additional value because it is still required to query the affected component to find out the details about the change.

Here is an overview over the fundamental event types supported by the JGUIraffe library for graphical input elements (there are a couple of other event types related to special controls and windows which will be covered later).

Listener interface Event object Description
FormActionListener FormActionEvent An action event typically means that the user wants the application to do something. Events of this type are issued for instance by buttons or menu items. In reaction of the event the listener has to initiate the operation that corresponds to the UI element that fired the event.
FormChangeListener FormChangeEvent Change events have already been mentioned above. They are fired for all kinds of changes on different component types. Typically an application only has to be notified that something has changed. The exact nature of the change can then be determined by inspecting the affected component. One use case for change events is the implementation of dynamic UIs that change according to user actions. As an example consider a directory browser application: When the user selects another directory, the table showing the content of the current directory has to be refreshed.
FormFocusListener FormFocusEvent Events of this type are fired when the user changes the keyboard focus from one component to another. This means that the user's interaction with one component has finished (and maybe a new interaction with another component starts). Focus events provide a good opportunity for validating the data the user has entered.
FormMouseListener FormMouseEvent With this event type an application can react on mouse clicks or several other mouse gestures.

While action and change events are more abstract and high-level events, focus and mouse events are more on a technical layer. They are needed to implement specific functionality around UI components. For instance, it is not necessary to register a mouse listener at a button component to find out whether the button is pressed by the user. The action event is used for this purpose. However, mouse listeners are required if an application wants to react on a double-click.

All event classes shown in the table above are derived from the base class FormEvent. From this base class they inherit some properties which are very useful during event processing:

  • The name of the component that is affected by the event.
  • The ComponentHandler of this component. Through this object access to the component is possible. For instance, its current data can be inspected.
  • The source of the event. This is the original event object fired by the underlying UI toolkit. JGUIraffe catches this event and wraps it into one of its event classes. Through this property it is available to an application.

So in order to retrieve events from UI components an application has to implement the corresponding event listener interfaces. But how does it register event listeners at the desired components? For this purpose JGUIraffe provides the FormEventManager class. The current instance of this class can be obtained from the ComponentBuilderData object which holds central information about the builder operation currently in progress. (Refer to the section Fundamental classes and interfaces for more information about this class and its role.) A reference to the ComponentBuilderData object is available to all form controllers automatically; other components can be injected a reference by means of the dependency injection builder.

FormEventManager provides methods that allow registering and unregistering event listeners of all supported types for specific or all components of the current form. For instance, there are two overloaded variants of the addActionListener() method: one expects the name of a component and a FormActionListener object, the other one only takes a FormActionListener parameter. The former method registers the given action listener only at the specified component. The latter one registers the listener at all components that support action listeners. This is an extension to the functionality provided by most of the typical UI frameworks where only the registration at single components is supported. With this feature it is very easy to register "global" event listeners which allow a very generic event processing. JGUIraffe itself uses this feature for instance to implement a generic validation listener that is triggered whenever a component loses focus.

As an example we show how a change listener can be added to a checkbox component named cbxFilter. We assume that the ComponentBuilderData is already available in the variable builderData. Then the code for looking up the event manager and registering the listener would look as follows:

FormEventManager eventManager = builderData.getEventManager();
eventManager.addChangeListener("cbxFilter", new FormChangeListener() {
    ...
});
    

The major part of the components supported by JGUIraffe only deals with standard events (action, change, focus, and mouse). However, there are some exceptions. For instance, the TreeHandler interface allows adding further tree-specific event listeners. To deal with such components, in addition to the methods operating on standard event handlers, FormEventManager provides a generic method for adding event handlers of an arbitrary type: addEventHandler(). This method expects the type of the event listener to be added as a string. This can be the name of a constant of the FormListenerType enumeration class which defines all standard event listener types. In this case the addEventListener() methods behaves exactly as the corresponding specific addXXXListener() methods.

However, if the passed in listener type does not match one of the standard event types, the method tries to find a corresponding add method using reflection. This is based on a simple naming convention: if the string Foo is passed as event listener type, the corresponding method for registering event listeners must be called addFooListener(). (This corresponds to the naming conventions used within the JDK.) Using this mechanism it is possible to add arbitrary event listeners to components. The following example shows how a TreeExpansionListener object is registered at a TreeHandler:

FormEventManager eventManager = builderData.getEventManager();
TreeExpansionListener listener = new TreeExpansionListener() { ... };
eventManager.addEventListener("tree", "Expansion", listener);
    

Here tree is the name of the tree component. addEventListener() searches for a ComponentHandler object with the specified name. If it finds one, it calls the method for adding the event listener according to the described naming convention. In this example it would call the addExpansionListener() method. Analogously to the other methods for adding specific event listeners it is possible to pass in null for the component name. Then addEventListener() iterates over all component handler objects available and tries to find a corresponding method for adding the event listener. All handlers supporting such a method are called - the others are ignored. Using this mechanism it is possible for instance to add a TreeExpansionListener at all existing tree components in a single method call.

Working directly with FormEventManager is a programmatic approach to the registration of event listeners. The corresponding code can be placed for instance in initialization methods of Form controllers. Later in this document we will see, that event listeners can also be registered using specialized tags in builder scripts. This is then a declarative approach. But before we come to this topic, we first have to discuss actions - the components that gave the action builder tag library its name.

Actions

Actions are a well-established concept in many UI libraries. For instance, they are supported by Swing and SWT. An action connects a graphical element in the user interface of an application to the operation the application should execute when this element is triggered by the user. Often the same could be achieved by event listeners registered directly at the corresponding UI elements. However, actions are concepts on a higher abstraction level and provide some advantages over plain event listeners. The main advantage is probably that actions are independent on specific UI elements; actually they can be associated with multiple UI elements at once. This is especially useful when an application allows its users to execute a command in various ways. This pattern is used frequently, for instance, a certain operation can be accessed as a menu item or as a tool bar button. If this operation is realized as an action, the logic for executing this operation can be implemented in a central place. So actions provide a sophisticated programming model that supports the separation between UI and business logic.

Another advantage of actions is that they also influence the UI elements associated with them. For instance, if an action is temporarily not available (e.g. due to restrictions in the business logic), it can be disabled. Then all associated UI elements are automatically disabled - the developer does not have to remember all components affected by the action and disable them manually.

Because of their mediator role between the UI and application logic actions are twofold: They have a set of properties related to their visual representation and also refer to the logic to be executed when they are triggered. The visual properties come into play when an action is associated with a UI element like a tool bar button; then the UI element initializes some of its properties (e.g. label, icon, accelerator, etc.) from the data stored by the action. The action is also automatically registered as event listener at the associated UI element. When the element fires an action event, the action is notified and ensures that the logic is executed.

The actions provided by the JGUIraffe library comply to this generic description in all points. Programmatically they are represented by the FormAction interface. This interface is a bit minimalistic; it only provides access to properties related to the execution of an action. No visual properties can be queried here. This is due to the fact that for the action framework such visual properties are of less importance. They are evaluated by code that is specific to the underlying UI platform (e.g. Swing or SWT) when the action is associated with a specific UI element. After that the framework only has to ensure that the action is correctly executed when it is triggered. This also conforms to the declarative approach taken by JGUIraffe: Actions are defined in builder scripts (as we will see soon). When client code obtains an action object it has already been fully initialized and can now be used to control its execution.

FormAction provides access to the following properties:

  • Actions are assigned a name at creation time that cannot be changed later. The name can be used for instance to lookup the action in a bean context.
  • The enabled flag: Using this property actions can be disabled and enabled. A disabled action cannot be executed. The enabled status is also reflected by UI elements associated with the action; for instance, a button is grayed out if its action is disabled.
  • The checked flag: Some actions do not execute code when they are triggered, but only changed their checked status. Such actions behave similar to checkboxes. They are often used to represent a global status of an application. Graphically they are represented by checkable menu items or toggle buttons in the tool bar. Using the checked property the action's checked status can be queried and set.
  • FormAction objects are associated with a task that is executed when the action is triggered. This is a difference to the action implementations in typical UI frameworks like Swing or SWT where actions directly implement the logic they represent. The possibility to dynamically change the task of an action allows for very flexible applications. As task of an action an object can be set that implements one of the following interfaces:
    • java.lang.Runnable: This is the standard interface used by the JDK to represent an executable task. Its simple run() method does not support any parameters or exceptions.
    • ActionTask is a slightly enhanced version of the Runnable interface. It also defines a run() method, but this variant expects some more parameters which can be evaluated by an implementation. For instance, a reference to the action that is currently executed is passed.
  • In addition to these properties FormAction defines an execute() method which triggers the action.

In many cases client code does not have to deal with FormAction objects directly. They are declared and initialized in a builder script and then work automatically, i.e. they are executed when the user interacts with the associated UI elements without intervention of the application. So how are actions created? Take a look at the following fragment of a builder script for some example action declarations:

  <!-- File new action -->
  <a:action name="fileNewAction" textres="act_filenew_text"
    tooltipres="act_filenew_tip" mnemonicres="act_filenew_mnemo"
    acceleratorDef="CONTROL F1" taskBean="openNewFileDialogTask">
    <f:icon resource="new.gif"/>
  </a:action>

  <!-- File open action -->
  <a:action name="fileOpenAction" textres="act_fileopen_text"
    tooltipres="act_fileopen_tip" mnemonicres="act_fileopen_mnemo"
    group="SINGLE_FILE" taskBean="dummyTask">
    <f:icon resource="open.gif"/>
  </a:action>

  <!-- Edit cut action -->
  <a:action name="editCutAction" textres="act_editcut_text"
    tooltipres="act_editcut_tip" mnemonicres="act_editcut_mnemo"
    group="SELECTION" acceleratorDef="control x" taskBean="dummyTask"/>
    

This script defines three different actions. As can be seen, actions are created using the <a:action> tag which is implemented by the ActionTag class. (We assume that the action builder tag library is associated with the namespace prefix "a". This means that the Jelly script must contain the attribute declaration xmlns:a="actionBuilder" in its root element.) Using this tag the following properties of an action can be set:

  • The name of the action which is mandatory.
  • The action can be assigned a (display) text. It can be set either directly or through a resource ID in the usual way.
  • Actions can also have a tool tip. Again the text of the tool tip can be specified directly or as a resource ID.
  • The definition of a mnemonic allows for better keyboard support. For instance, if the action is associated with a menu item, the mnemonic is an underlined character in the text of the menu item. By typing this character the menu item is executed.
  • An accelerator also associates the action with a keyboard action. However, in contrast to a mnemonic an accelerator is globally available; it is a direct keyborad shortcut for invoking the action. Refer to the Accelerator class for a detailed description of accelerators and the syntax used to define them.
  • An icon for the action can be defined by a nested <f:icon> tag.
  • Actions can be added to groups. This is convenient for actions that are logically related. Action groups allow some operations on the elements they contain; for instance, it is possible to disable all actions in a group at once.
  • Finally, the task of the action can be specified. The name passed to the taskBean attribute is looked up in the current bean context. It may have been created using the dependency injection framework.

After these declarations fully initialized FormAction objects exist and are available in the current context. In the next section we will see how we can reuse these objects in order to create corresponding UI elements.

Main menus and tool bars

The action builder tag library provides a set of tags for the creation of menus and tool bars. Because menu items and tool bar buttons are the default controls to be associated with actions it is reasonable to let these components be handled by the same part of the framework. Menus and tool bars should be familiar to most developers so there is not much to say about them. Let's dive into an example of a menu definition directly:

  <a:menubar name="mainMenu">
    <a:menu textres="menu_file_text" mnemonicres="menu_file_mnemo">
      <a:menuitem actionName="fileNewAction"/>
      <a:menuitem actionName="fileOpenAction"/>
      <a:menuitem actionName="fileEditAction"/>
      <a:menuitem actionName="filePrintAction"/>
      <a:separator/>
      <a:menuitem actionName="exitAction"/>
    </a:menu>
    <a:menu textres="menu_edit_text" mnemonicres="menu_edit_mnemo">
      <a:menuitem actionName="editCutAction"/>
      <a:menuitem actionName="editCopyAction"/>
      <a:menuitem actionName="editPasteAction"/>
      <a:separator/>
      <a:menuitem actionName="editRefreshAction"/>
      <a:separator/>
      <a:menuitem actionName="editDeleteAction"/>
    </a:menu>
    <a:menu textres="menu_extras_text" mnemonicres="menu_extras_text">
      <a:menuitem actionName="extrasViewDefAction"/>
      <a:separator/>
      <a:menuitem actionName="extrasSettingsAction"/>
      <a:menuitem actionName="extrasLongOpAction"/>
    </a:menu>
  </a:menubar>
    

This fragment of a builder script defines the main menu of an application: a menu bar with three menus. The items of the menus are defined through actions (which are specified using the actionName). Here the actions defined in the last example (and some more) are referenced. Of course, the names passed to the actionName attribute must point to valid actions that have been declared before in this builder script or are available in the parent bean context. We give a short overview over the tags involved:

Tag Implementation class Description
<a:menubar> MenuBarTag Defines a menu bar. This is the top-level element of a menu definition. It has a mandatory name attribute. The whole menu bar created by this tag is stored in the current bean context under this name. The name can then be used to reference the menu from a window.
<a:menu> MenuTag This tag adds a menu to a menu bar or another menu. It can be placed in the body of a <a:menubar> tag or another <a:menu> tag. The menu is defined by its text (which can either be set directly or through a resource ID) and an optional icon. A mnemonic can also be defined. Menus are no active elements themselves; they merely serve as containers in which other active elements - mainly menu items - are placed.
<a:menuitem> MenuItemTag The <a:menuitem> tag defines the content of a menu. Each tag creates an entry in the menu that can be selected by the user to initiate an operation. We will discuss the different options supported by this tag below.
<a:separator> SeparatorTag This tag can be used to group menu items. It creates a thin line in a menu to separate groups of menu items. It has no further functionality.

This example script fragment shows that a declarative approach for defining menus works pretty well. The hierarchical structure of XML fits well to the organization of a menu bar with menus and sub menus. The same menu setup in Java code as a series of constructor calls is by far less readable.

All menu items defined by the script are associated with an action whose name is passed to the actionName attribute. Under the hood, this causes a menu item to be created whose properties are initialized from the properties set for the associated action. Constructing menu items from actions is the preferred option. It allows using all of the advantages of actions. Also, there is no need to manually register an event listener because the action is automatically connected with the menu item and gets triggered when the user selects the item.

Alternatively the <a:menuitem> tag supports a similar set of attributes as used by the <a:action> tag for setting the properties of an action (text, mnemonic, accelerator and so on). With these attributes the appearance of the menu item can be defined manually. If this approach is used, the developer has to add an event listener explicitly. In order to do so, the menu item must be assigned a name through the name attribute. In this case a ComponentHandler is created for the menu item and stored in the current context. The FormEventManager class can then be used to register an action listener as discussed in the section Basics of the event system. It is also possible to use the declarative approach for registering an event listener that will be discussed below.

So much for menus, we now move on to tool bars. The declarations in a builder script that define a tool bar look similar to menu definitions as demonstrated by the following snippet:

  <w:frame titleres="main_title" menu="mainMenu" autoClose="false">
    <f:panel>
      <f:borderlayout/>
      <!-- A tool bar at the top of the window's content -->
      <a:toolbar>
        <f:borderconstr name="NORTH"/>
        <a:toolbutton actionName="fileNewAction"/>
        <a:separator/>
        <a:toolbutton actionName="fileOpenAction"/>
        <a:toolbutton actionName="fileEditAction"/>
        <a:toolbutton actionName="filePrintAction"/>
        <a:separator/>
        <a:toolbutton actionName="editRefreshAction"/>
      </a:toolbar>
      ...
    </f:panel>
  </w:frame>
    

There is one obvious difference between menu bars and tool bars: Menus are declared as stand-alone components while tool bars are really integrated in the UI of the window they belong to. This example defines a frame window (the tags for constructing windows are subject of the next chapter). The content of the window consists of a panel with a BorderLayout. The tool bar is added to the north of the frame's content panel. This is the typical pattern for defining tool bars. All tags involved in the definition of a tool bar are listed in the following table:

Tag Implementation class Description
<a:toolbar> ToolbarTag The top-level tag for defining a tool bar. The tag does not have any special attributes other than the ones inherited by the super class.
<a:toolbutton> ToolButtonTag With this tag a single tool bar button can be added. So a sequence of these tags populates the tool bar.
<a:separator> SeparatorTag The <a:separator> tag has already been introduced in the discussion about menus. It can be used for tool bars, too, to logically group single buttons. This time it produces a gap between buttons.

Most comments we made for menu items also apply to tool bar buttons. Again a button can either be defined by associating it with an action or by manually setting the corresponding properties. Note that partly the same actions were used for menu items as for tool bar buttons. In this case both elements trigger the same action.

Popup menus

The menus discussed in the previous section provide commands available for the whole application - they are application-global. Popup menus in constrast are associated with a single component of the application and allow access to functionality specific for this component. The user clicks on a specific location on the screen and is displayed a menu with options related to this position. Therefore menus of this type are also refered to as context menus.

While global menus are pretty static, popup menus are often more dynamic in nature. Their content may vary depending on the status of the component they are associated with. Consider a table listing the files and sub directories of a specific directory. A popup menu for this table can show different items depending on the current selection: if a file is selected, other options may be available than for a sub directory; other options may be always visible.

The action builder tag library supports assigning popup menus to specific components. It pays special attention to the dynamic nature of such menus because they are not specified directly, but a class has to be provided which is able to create the menu on demand. For this purpose the PopupMenuHandler interface exists. Internally, JGUIraffe installs the appropriate event listeners listening for a gesture that should bring up a popup menu - for most platforms this is a click of the right mouse button. If a fitting event is detected, the PopupMenuHandler is invoked. It can then create a menu based on arbitrary criteria.

PopupMenuHandler is a simple interface defining only a single method: constructPopup(). This method is passed a PopupMenuBuilder object which can be used to define the content of the menu. The PopupMenuBuilder interface defines several addXXX() methods for adding different types of objects to the popup menu under construction: actions, sub menus, or separators. There is also a create() method that has to be called after the menu's content has been defined. It causes the menu to be created and displayed. Actually, the PopupMenuHandler can decide not to call the create() method - in this case no menu will be shown.

Let's come to an example: We stay with the table showing the content of a directory. To this table a context menu is to be added. The menu should offer operations on the files or directories currently selected. However, some operations are only available if the selection contains a single file object only. The corresponding actions are grouped together in an action group called SINGLE_FILE. The implementation of the PopupMenuHandler interface obtains all actions belonging to this group and adds them to the menu only if they are enabled. After that it adds some actions that are always active.

package net.sf.jguiraffe.examples.tutorial.mainwnd;

import net.sf.jguiraffe.gui.builder.action.ActionBuilder;
import net.sf.jguiraffe.gui.builder.action.ActionStore;
import net.sf.jguiraffe.gui.builder.action.FormAction;
import net.sf.jguiraffe.gui.builder.action.FormActionException;
import net.sf.jguiraffe.gui.builder.action.PopupMenuBuilder;
import net.sf.jguiraffe.gui.builder.action.PopupMenuHandler;
import net.sf.jguiraffe.gui.builder.components.ComponentBuilderData;

public class TablePopupHandler implements PopupMenuHandler
{
    /** An array with the names of actions that are always present. */
    private static final String[] ACTIVE_ACTIONS = {
            "editDeleteAction", "editRefreshAction"
    };

    @Override
    public void constructPopup(PopupMenuBuilder builder,
            ComponentBuilderData compData) throws FormActionException
    {
        ActionStore as = (ActionStore) compData.getBeanContext().getBean(
                ActionBuilder.KEY_ACTION_STORE);

        // Handle group with actions for a single file selection
        boolean enabled = false;
        for (String name : as.getActionNamesForGroup("SINGLE_FILE"))
        {
            FormAction action = as.getAction(name);
            if (action.isEnabled())
            {
                builder.addAction(action);
                enabled = true;
            }
        }

        if (enabled)
        {
            builder.addSeparator();
        }

        // Actions that are always contained in the menu
        for (String name : ACTIVE_ACTIONS)
        {
            builder.addAction(as.getAction(name));
        }

        // Display the menu
        builder.create();
    }
}
    

This listing also demonstrates the usage of some of the helper classes related to action management. The ActionStore class manages all action objects and action groups defined in the current builder script and allows access to them by name. The current instance of this class can be queried from the bean context as shown in the example code. Using the ActionStore object the names of the actions in the SINGLE_FILE action group can be obtained. The code iterates over this set and calls the popup builder's addAction() method only if the action is enabled. This assumes that there are other components in place which control the enabled status of the actions in the group based on the table's current selection. This is probably handled by a change event listener registered at the table. If at least one action of the group was enabled, a separator is added to the popup menu, so the actions of the group are visually separated from the other ones. Finally the actions that should always be visible are added to the menu. As the very last operation the popup handler calls the builder's create() method to actually display the menu.

Now that the logic for creating the menu is available, how can it be connected to the table component? For this purpose the action builder tag library provides the <a:popup> tag which is implemented by the PopupHandlerTag class. The tag has to be placed in the body of the input component tag for the element the menu is to be added to:

  <f:table name="table" model="tableModel" multiSelection="true">
    ...
    <!-- Add a context menu to the table -->
    <a:popup class="net.sf.jguiraffe.examples.tutorial.mainwnd.TablePopupHandler"/>
  </f:table>
    

The example showed how the content of the popup menu can vary depending on certain criteria: some actions were only added to the builder if they were enabled. If you have a popup menu that should always display the same menu items, you probably do not want to implement a separate PopupMenuHandler class for it - especially as it would be pretty trivial. JGUIraffe provides a default implementation of the interface that can be used in such cases: SimplePopupMenuHandler. When an instance of this class is created it is passed a collection with the content of the popup menu to be constructed. This collection can contain objects of the following types:

  • FormAction objects are added to the builder using its addAction() method. They become menu items in the generated menu.
  • Other SimplePopupMenuHandler objects are used to create sub menus. In this case the popup menu has a sub menu whose content is defined by the SimplePopupMenuHandler object. This can be nested to an arbitrary depth.
  • A null reference in the collection causes a separator to be added to the popup menu.

Using features of the dependency injection framework it is possible to define SimplePopupMenuHandler instances completely in builder scripts. That way static popup menus can be created and assigned to components without programming. We present an example that is about a text area control. The text area allows the user to insert several pre-defined text fragments and to clear its content. These operations should be made available through a popup menu. The popup menu consists of a sub menu with the several insert operations and a menu item for clearing the text. So two SimplePopupMenuHandler definitions are needed: one for the sub menu and one for the main popup menu. The definitions can look as follows (the actions referred to have been dropped):

  <!-- Action data definition for the append sub menu in the popup menu -->
  <a:actionData textres="newfile_men_append_text"
    mnemonicres="newfile_men_append_mnemo" var="menuAppend"/>

  <!-- Popup menu handler for the append sub menu -->
  <di:bean name="appendMenu"
    beanClass="net.sf.jguiraffe.gui.builder.action.SimplePopupMenuHandler">
    <di:constructor>
      <di:param>
        <di:list>
          <di:element refName="action:addContentLoremIpsumAction"/>
          <di:element refName="action:addContentXMLAction"/>
          <di:element refName="action:addContentHTMLAction"/>
        </di:list>
      </di:param>
    </di:constructor>
    <di:setProperty property="actionData" refName="menuAppend"/>
  </di:bean>

  <!-- Popup menu handler for the text area's content menu -->
  <di:bean name="fileContentPopupHandler"
    beanClass="net.sf.jguiraffe.gui.builder.action.SimplePopupMenuHandler">
    <di:constructor>
      <di:param>
        <di:list>
          <di:element refName="appendMenu"/>
          <di:element refName="action:clearContentAction"/>
        </di:list>
      </di:param>
    </di:constructor>
  </di:bean>
    

This example uses the <di:list> tag of the dependency injection framework to create the lists that have to be passed to the SimplePopupMenuHandler objects. In most cases the list elements are actions obtained from the current bean context. Note how the reserved prefix action: is used to reference the desired actions. The first SimplePopupMenuHandler declaration represents the sub menu. It is referenced by the second one. The visual properties of the sub menu - e.g. its text, mnemonic, or icon - are obtained from the SimplePopupMenuHandler instance. In order to initialize them, the definition contains a <di:setProperty> tag for the ActionData property. Using this property all visual properties related to actions can be set at once. With the <a:actionData> tag it is possible to create a corresponding data object for these properties. The example fragment demonstrates how this tag is used. The data object created by the tag is stored under the name determined by the var attribute and is later referenced by the <di:setProperty> tag. More information can be found at the ActionDataTag class. The following fragment shows how the main SimplePopupMenuHandler bean is associated with the text area control:

  <f:textarea name="fileContent" displayNameres="newfile_disp_content">
    ...
    <a:popup beanName="fileContentPopupHandler"/>
  </f:textarea>
    

Result is a popup menu with a nested sub menu:

Popup menu

Declarative event listener registration

In the section Basics of the event system we have used the FormEventManager class to register event listeners at input elements. While this programmatic registration surely makes sense in some situations it is not really compatible with the declarative approach of declaring UIs: after the builder script was run you still have to do some tasks related to the initialization of the UI. It would be nice if event listeners could be directly registered in the script, too.

Well, the action builder tag library provides such functionality. It offers some tags that allow the registration of event listeners at components defined in the builder script. There are even two different approaches: a simple one that simulates the direct usage of the FormEventManager class as described in the section Basics of the event system, and a more advanced one that is based on actions. We start with the simple approach.

Normal registrations of event listeners can be done using the <a:eventListener> tag which is implemented by the EventRegistrationTag class. Basically, this tag operates like the generic addEventHandler() method of FormEventManager: it locates the component of interest, evaluates the listener type (which can be either a standard type like action, change, or focus or a custom type), and registers the specified listener. We present an example in which a change listener is registered at a checkbox:

<!-- The event listener bean -->
<di:bean name="cbxListener" beanClass="...">
  ...
</di:bean>

<a:eventListener component="cbx" eventType="CHANGE" beanName="cbxListener"/>
    

The eventType attribute defines the type of listener to register. In this case, with the type change (case does not matter) a default listener type is used. For non-standard types the same naming conventions apply as discussed for the addEventHandler() method of FormEventManager. For instance, to register an expansion listener for a tree, the event type would be Expansion.

In this example the event listener to register is specified through the beanName attribute which references a bean defined by tags of the dependency injection builder library. It is also possible to specify the class attribute and set it to the fully qualified name of the listener class. Then a new instance of this class would be created. However, the approach with the beanName attribute is more powerful because the whole feature set of bean declarations can be used.

Finally, the component to which the listener should be added has to be provided. For this purpose there are multiple options:

  • With the component attribute the name of an input component can be specified. The tag searches for a ComponentHandler with this name in the current context and tries to add the listener to this handler. This is probably the most basic use case when dealing with event listeners.
  • Alternatively it is possible to place the <a:eventListener> tag in the body of another input component tag. Then the listener will be registered at this component.
  • With the targetBean attribute an arbitrary object can be defined as target of the registration. The name specified here is looked up in the current bean context. The resulting bean need not be a component handler; registration is performed through reflection, so the only requirement is that a method exists whose name corresponds to the listener type. This also allows adding event listeners to native UI elements.
  • There is another option available for registering the listener at multiple components: if the multiple attribute is set to true, the tag searches for all ComponentHandler objects in the current form that are compatible with the specified listener type. The specified event listener is registered at all matching handlers. This corresponds to the functionality of FormEventManager to register "global" event listeners. When using the multiple attribute there must not be a component name nor a target bean name.

As another example we show how a menu item can be declared without the reference to an action and then be connected to an event listener. As stated in the section Main menus and tool bars, it is recommended to use actions for the definition of menu items and tool bar buttons because this approach is more flexible. However, if a menu item is more or less static, and the functionality it represents is only located in the menu and not in other places, it is easier to declare it directly. The following fragment from a builder script shows an excerpt from a menu definition followed by a tag which assigns an event listener to the Cut item from the Edit menu (the declaration of the event listener bean has been omitted):

    ...
    <a:menu text="Edit" mnemonic="E">
      <a:menuitem name="editCut" text="Cut" mnemonic="C" acceleratorDef="CONTROL X"/>
      ...
    </a:menu>
    ...
  <a:eventListener component="editCut" eventType="ACTION" beanName="cutListener"/>
    

Note the following points:

  • The menu item is assigned a name via the name attribute. This causes it to be stored as a UI component so that it can be referenced later on.
  • The <eventListener> tag uses the same name in its component attribute.
  • The event type is action; the referenced listener bean must implement the FormActionListener interface.

So using the <a:eventListener> tag the same functionality is available as when using FormEventManager directly. Before we end this discussion, we want to add some words about the creation of event listeners:

In typical UI programming it is often the case that anonymous inner classes are used as event listeners. They are declared in the code that constructs the UI and directly added to the components they should monitor. This is convenient and has the advantage that the listener can access all members of the enclosing class - typically an event listener needs access to other components, e.g. to manipulate the state of input elements or to change properties of beans. However, the big disadvantage of this approach is that logic is cluttered and mixed with code for constructing the UI.

Using the declarative approach for registering event listeners together with the features of the dependency injection framework does not have this drawback. Event listener classes can be declared as regular top-level classes, maybe in a separate package for classes that deal with view logic. If a listener class needs references to other objects (graphical components or other beans like controllers), these dependencies can be automatically injected when the listener bean is created. To make this clearer we provide an example: An event listener is to be registered at a checkbox. When the checkbox is clicked by the user, the listener has to update the enabled state of an action and of a text field, depending on the checked state of the checkbox. The event listener class could be implemented as follows:

package com.mypackage;

public class CbxChangeListener implements ChangeListener
{
    /** The action to be enabled/disabled. */
    private final FormAction actionToModify;

    /** The component handler to enable/disable. */
    private final ComponentHandler<?> handlerToModify;

    /**
     * Creates an instance of CbxChangeListener and initializes it with the
     * components to be manipulated.
     *
     * @param action the action to enable/disable
     * @param handler the component handler to enable/disable
     */
    public CbxChangeListener(FormAction action, ComponentHandler<?> handler)
    {
        actionToModify = action;
        handlerToModify = handler;
    }

    /**
     * The value of the monitored checkbox has changed.
     *
     * @param e the change event
     */
    public void elementChanged(FormChangeEvent e)
    {
        // obtain the current value of the checkbox
        // (we can suppress the warning because we know that it is a checkbox)
        @SuppressWarning("unchecked")
        ComponentHandler<Boolean> cbxHandler =
            (ComponentHandler<Boolean>) e.getHandler();
        Boolean value = cbxHandler.getData();

        // now modify the components
        actionToModify.setEnabled(value);
        handlerToModify.setEnabled(value);
    }
}
    

This is pretty straightforward. The class has two member fields for the objects (an action and a component handler) it should manipulate. They are initialized by the constructor. Provided that an instance is properly registered at the checkbox component, the elementChanged() method is called every time the value of the checkbox changes. The event passed to the method also contains the ComponentHandler of the input element which caused the event - in our case this is the checkbox. So we just have to query the current value of the checkbox through its ComponentHandler and can then set the enabled state of the watched components accordingly. This was the Java part. The following fragment shows how an instance of this class is created in a builder script and associated with the checkbox:

<!-- The event listener bean -->
<di:bean name="cbxListener" beanClass="com.mypackage.CbxChangeListener">
  <di:constructor>
    <di:param refName="action:myaction"/>
    <di:param refName="comp:txtData"/>
  </di:constructor>
</di:bean>

<a:eventListener component="cbx" eventType="CHANGE" beanName="cbxListener"/>
    

The use of the <a:eventListener> tag should be clear. The interesting part is the creation of the listener object. Here standard functionality of the dependency injection builder is used to invoke the constructor of the listener class and pass the expected dependencies. The prefixes passed to the refName attributes of the <di:param> tags indicate that special elements should be looked up: an action and a component handler. These special prefixes are supported by the BeanContext that is available in the builder script. You can find a documentation about them in the Javadocs of the classes ComponentBuilderData, ActionBuilder, and WindowBuilderData. Hopefully, this example demonstrates that it is easy to use the declarative approach for registering event listeners. The combination of the dependency injection framework and the <a:eventListener> tag provides a powerful means to separate logic from UI initialization.

Mapping events to actions

The <a:eventListener> tag provides all functionality needed for dealing with event listener registrations. So what else has to be said about event listeners? JGUIraffe provides another feature related to the handling of event listeners: events fired by UI input elements can be mapped to actions.

One advantage of actions is that they can be shared between multiple components, e.g. a menu item and a tool bar button. The logic behind the action has to be implemented once and can then be directly reused by all components that are associated with the action. Many input components however, cannot be associated with actions; they only support event listeners. Now there are some use cases where an event generated by an input component should have the same effect as an action. One example is a table listing the files of a directory. Through a menu item and a tool bar button it is possible to open the file currently selected in the table. The logic for opening the file is implemented centrally by an action. To improve usability it would be desirable if a double-click on a file would also cause the file to be opened.

The problem could be solved by externalizing the code for opening a file into a helper class. Then we can write an event listener for mouse events and register it at the table that calls the helper class. Analogously, the file open action would delegate to this class. This would work without major code duplication, but it is still inconvenient and involves creating an almost trivial event listener class. JGUIraffe solves the problem by providing tags that register special event listeners at components. Whenever specific events are received from the component the event listener delegates to an action. This happens automatically without intervention of the developer. Take a look at the code fragment below:

<f:table name="fileTable" model="dirModel">
  ...
  <a:mouseEvent actionName="fileOpenAction">
  </a:mouseEvent>
</f:table>
    

This example shows how the <a:mouseEvent> tag (which is implemented by the MouseListenerTag class) is used to map mouse events to the action with the name fileOpenAction. The action must be available in the current context - typically it has been declared before in the same builder script. The tag is placed in the body of the input element tag defining the element the event handler is to be registered at. This works not only for mouse events, but for all standard event types supported by the event system of JGUIraffe. Event custom events are supported. The following table lists the tags for the different event types:

Tag Implementation class Event type
<a:actionEvent> ActionListenerTag Action events
<a:changeEvent> ChangeListenerTag Change events
<a:focusEvent> FocusListenerTag Focus events
<a:mouseEvent> MouseListenerTag Mouse events
<a:customEvent> FormEventListenerTag Custom events

So the <a:mouseEvent> tag in the body of the <f:table> tag basically means that whenever the table fires a mouse event the specified action is to be executed. But wait! There are several types of mouse events (e.g. mouse pressed, mouse released, mouse double-clicked, etc.), but we only want the action to be triggered for double-click events. This can be achieved by nesting another tag in the body of <a:mouseEvent> which defines a specific event filter:

<f:table name="fileTable" model="dirModel">
  ...
  <a:mouseEvent actionName="fileOpenAction">
    <a:eventFilter eventType="MOUSE_DOUBLE_CLICKED"
      class="net.sf.jguiraffe.gui.builder.event.filter.TypeEventFilter"/>
  </a:mouseEvent>
</f:table>
    

Using the <a:eventFilter> tag (implemented by the EventFilterTag class) a filter can be added to the automatically generated event listener which constraints the execution of the associated action: only if a specified condition is matched by the event, the action gets fired. In this example the filter accepts only events with the type MOUSE_DOUBLE_CLICKED. The <a:eventFilter> tag creates an object implementing the EventFilter interface and initializes it. Here we use an instance of the TypeEventFilter class, a specialized EventFilter implementation which can check for an event's type property. The net.sf.jguiraffe.gui.builder.event.filter package contains various implementations of event filters. It is also possible to combine multiple filters using boolean logic (and and or). In the following example a filter is defined which accepts single or double-click mouse events:

<a:mouseEvent actionName="fileOpenAction">
  <a:orEventFilter>
    <a:eventFilter eventType="MOUSE_DOUBLE_CLICKED"
      class="net.sf.jguiraffe.gui.builder.event.filter.TypeEventFilter"/>
    <a:eventFilter eventType="MOUSE_CLICKED"
      class="net.sf.jguiraffe.gui.builder.event.filter.TypeEventFilter"/>
  </a:orEventFilter>
</a:mouseEvent>
    

This fragment creates two instances of TypeEventFilter with different values of the type property and combines them using an <a:orEventFilter> tag. That way complex conditional logic can be specified in a declarative way.

In addition to the standard events supported by JGUIraffe's event system, it is also possible to map custom events to actions. For this purpose the generic <a:customEvent> tag exists. The actual event type is defined by a nested <a:listenerType> tag. This tag is implemented by the EventListenerTypeTag class. It expects the event listener type (as required by the FormEventManager class) and the class of the event listener interface as attributes. The latter is needed only for non-standard event types. The next example maps the tree expansion event of a tree component to an action:

<f:tree name="testTree" model="treeModel">
  <a:customEvent actionName="expansionAction">
    <a:listenerType type="Expansion"
      listenerClass="net.sf.jguiraffe.gui.builder.components.model.TreeExpansionListener"/>
  </a:customEvent>
</f:tree>
    

It should now be clear how to map arbitrary events generated by input components to actions. One last hint: The enabled property of a FormAction determines whether a mapped action is invoked or not: When an event arrives (which is accepted by a possibly installed event filter) the automatically generated event listener checks whether the action is enabled. Only then it executes the action. This is another advantage over the standard approach with event listeners: a typical event listener has no built-in mechanism to temporarily disable event processing.

Actions and commands

In the chapter about Commands we already pointed out that the programming model around command objects enforces a centralization of business logic which increases the maintainability of an application. Now about Actions we claimed something similar. How do these concept relate together?

To answer this question it makes sense to first emphasize the main difference between actions and commands: Actions are directly invoked by the thread that manages the UI - the event dispatching thread. Commands in contrast are intended to be executed by a separate worker thread. This allows the UI to stay responsive while the application is working in the background.

Because of their different execution modes actions and commands are taylored to different kinds of operations. If an operation runs very fast - maybe just an update of the UI -, an action is appropriate. However, all longer running operations should be implemented as commands running in a background thread.

In order to create and execute a command usually some glue code is required. A typical use case is that a menu item or a tool bar button should trigger an operation which may take a while. The graphical elements (the menu item or the tool bar button) are associated with an action. In the task of this action the logic is placed that handles the execution of the command. Here lies the typical relation between actions and commands: actions act as starters for command objects. They bridge between the UI thread and the worker thread for background execution.

Because starting a command from an action is a standard pattern in UI programming JGUIraffe provides some special support for it. With the CommandActionTask class there is a specialized task implementation that executes a configurable command. An instance of CommandActionTask can be fully defined in a builder script. It is assigned the command to be executed. This command can also be defined in the builder script. An example should make this clearer. Imagine we have written a command class for opening a dialog with view settings. This is a simple command class which does not expect any configuration parameters. So its definition in a builder script is straightforward:

<di:bean name="openViewDefsDialogCommand"
  beanClass="net.sf.jguiraffe.examples.tutorial.viewset.OpenViewSettingsDlgCommand">
</di:bean>
    

This declaration just defines an instance of the OpenViewSettingsDlgCommand class and makes it available in the current bean context under the name openViewDefsDialogCommand. The class implements the Command interface. Now we define an instance of CommandActionTask and associate it with the command bean:

<di:bean name="openViewDefsDialogTask"
  beanClass="net.sf.jguiraffe.gui.app.CommandActionTask">
  <di:setProperty property="commandBeanName" value="openViewDefsDialogCommand"/>
</di:bean>
    

The property commandBeanName connects the task implementation to the command to be executed. The last step is to assign this task to an action:

<a:action name="extrasViewDefAction" textres="act_extrviewdef_text"
  tooltipres="act_extrviewdef_tip" mnemonicres="act_extrviewdef_mnemo"
  taskBean="openViewDefsDialogTask">
  <f:icon resource="view.gif"/>
</a:action>
    

This is a complete action definition. Of special importance is the taskBean attribute referencing the task we setup in the previous step.

So CommandActionTask can be used out of the box to associate actions with long-running background tasks. The class provides another feature which is useful in this context: it allows disabling or enabling specific UI elements while the background processing is running. Often there are some restrictions while an operation runs in a background task. For instance, some other operations cannot be started until results of the current operation are available. Another typical use case is that it is not allowed to start an operation again while it is still running. To achieve this, a CommandActionTask instance can be associated with an ElementEnabler. An ElementEnabler is a generic object that knows how to disable or enable certain UI elements, e.g. actions, action groups, or input components. Have a look at the following example which is a slight extension of the previous one:

<di:bean name="openViewDefsDialogTask"
  beanClass="net.sf.jguiraffe.gui.app.CommandActionTask">
  <di:setProperty property="commandBeanName" value="openViewDefsDialogCommand"/>
  <di:setProperty property="beforeEnabler" value="action:extrasViewDefAction"/>
</di:bean>
    

Here again the task for the action for opening the dialog with view settings is defined, but this time the additional beforeEnabler property is set. If you look at the Javadocs, you will see that the beforeEnabler property actually expects an object implementing the ElementEnabler interface. However, when executing a builder script a specialized data type converter for element enablers is active. The converter is able to transform a textual representation of an element enabler to a concrete implementation object. This is done with the help of the EnablerBuilder class. In this example fragment the value action:extrasViewDefAction means that during the execution of the associated background command the action with the name extrasViewDefAction (this is the action itself) should be disabled. So the user cannot accidently open multiple dialog windows by issuing multiple clicks. There is also a property named afterEnabler which allows setting an enabler to be invoked after the command was executed. If it is not defined, an enabler is set which does exactly the opposite of the before enabler; therefore all elements that have been disabled when the execution of the command started are enabled again when it finishes. If you want to change the states of elements in a non-symmetric way, you can specify a different object for the afterEnabler property.

Action enablers are a pretty neat feature that supports dealing with concurrency issues that might arise when working with background tasks. Have a look at the Javadocs for the CommandActionTask and EnablerBuilder classes for more details about the enabler mechanism.

One special case of longer-running tasks is the processing of builder scripts. Well, actually typical builder scripts are executed pretty fast, but nevertheless this operation involves I/O activities, so it will not do any harm to put it in a background thread. JGUIraffe provides a pre-defined Command implementation that processes a builder script and opens the main window defined by the script. This command can be used for instance everytime an application needs to open a dialog window. The following code fragment shows an example:

<di:bean name="openNewFileDialogCommand"
  beanClass="net.sf.jguiraffe.gui.app.OpenWindowCommand">
  <di:constructor>
    <di:param>
      <di:bean beanClass="net.sf.jguiraffe.locators.ClassPathLocator">
        <di:factory>
          <di:methodInvocation method="getInstance"
            targetClass="net.sf.jguiraffe.locators.ClassPathLocator">
            <di:param value="newfile.jelly"/>
          </di:methodInvocation>
        </di:factory>
      </di:bean>
    </di:param>
  </di:constructor>
</di:bean>
    

OpenWindowCommand implements the Command interface in a way that it executes the builder script defined by the Locator object passed to the constructor. After the script has been evaluated the window it defines is opened. The bean definition in the example fragment declares an instance of OpenWindowCommand and also defines the Locator object to be passed to the constructor - a ClassPathLocator in this case. The major part of the bean definition deals with the creation of the ClassPathLocator object. This is because instances of this class are created using a static factory method which complicates the definition. Fortunately, there is a simpler way to write this declaration because the dependency injection framework provides a specialized data type converter for Locator objects: LocatorConverter. The Javadocs of this class describe the syntax understood by this converter. Using this mechanism the declaration of the command can be abbreviated as follows:

<di:bean name="openNewFileDialogCommand"
  beanClass="net.sf.jguiraffe.gui.app.OpenWindowCommand">
  <di:constructor>
    <di:param value="classpath:newfile.jelly"/>
  </di:constructor>
</di:bean>
    

This is much easier, isn't it? Now this command bean can be assigned a CommandActionTask bean, and then it is directly available to an action:

<di:bean name="openNewFileDialogTask"
  beanClass="net.sf.jguiraffe.gui.app.CommandActionTask">
  <di:setProperty property="commandBeanName" value="openNewFileDialogCommand"/>
  <di:setProperty property="enablerSpecification" value="action:fileNewAction"/>
</di:bean>
    

Because the builder script newfile.jelly is read the dialog window created by the command is proably about creating a new file. So the corresponding task can be referenced by the fileNewAction. Note that this action also appears in the enablerSpecification property of the task bean. So while the builder script is processed, the user cannot trigger the action again to open multiple copies of the dialog. When the dialog is open it is modal, so actions in the parent window are not available per se.

These examples show that actions for opening dialog windows that are defined by builder scripts can be completely defined in a declarative way. No programming is required unless there are special requirements related to the initialization of the builder. For instance, if some application-specific properties have to be passed to the builder script, a custom class has to be created. This class can be derived from OpenWindowCommand; it can override the prepareBuilderData() method to perform additional initialization before the builder is invoked.