User Views

We call the user interface components that we use in D2K user views. User views are Java graphical user interface components, usually containers, which implement the UserView interface, and provide a user interface for a ViewModule. Most often, developers will not implement this interface directly, instead they will subclass existing implementations described later.

This interface has a few important methods. Most important is the compileResults() method. This method will traverse the components contained within the user view container, collecting the content where appropriate and adding the entries to a hashtable with the component name as the key. The name of the component must exactly match the value associate with it in the getFieldNameMapping() method of the module. In most cases, it will not be necessary to override this method.

It is also possible that the developer will want a menu included in the user interface. Since the module returns a user view, not a window, there is no mechanism by which the module may include a menu without creating it's own window. To circumvent this problem, we provide a method getMenu() which may return a menu object that will then be attached to whatever window it is displayed in.

There also is a method that must be implemented to initialize the view. The initView() method is called by the system after creation to create and lay out the components the user view contains. This method is frequently overridden by the subclasses to add application specific components to the user view.

UserView defines only the interface to user views, but there does exist an implementation this interface, as we will see in User Panes.

One of the tasks undertaken by the user views is the creation of a Hashtable object containing name/value pairs that represent the results of the dialog with the user. This is most easily accomplished by adding components to the user views that implement an interface known as the AddFields. For convenience, several such components have already been created.

AddFields Interface

Each component, that will allow the user to enter information that can then be passed as an output, will need to implement the AddField interface. This interface has only one method, addFields(), which is called by the system when the appropriate event occurs. There is an additional requirement of these components, they need to have some name that will serve as the key in the hashtable. Note: This string must be exactly the same as the string in the array returned from getFieldNameMappings method of the ViewModule! The position of the entry in this array is the same as the index of the output that will be associated with the components value.

Widget Implementations

We have developed several user interface components that implement the AddFields interface. All these widgets are subclasses of Java AWT components that simply add the appropriate method. This components are extremely easy to develop, so please don't hesitate to create your own. There are also Swing counterparts to each of these widgets, so pure Swing interfaces can easily be developed.

Listeners

Now we know how the information can be gotten from the user interface components, the next question we will answer is how this process of collecting users inputs and transforming them to outputs get triggered. As one might expect, user interface events trigger the collection and dismissal processes.

There are some Java event listener classes provided which will respond to various types of user interface events. The action listeners are all subclasses of DSListener class. The DSSendInputListener will respond to an action by sending all the users entries and dismissing the dialog. The DSContinueInputListener will provide the outputs without dismissing the dialog, and the DSCancelInputListener will dismiss the dialog without providing any outputs.

There is also an ItemListener called DSItemListener that will respond to item selection events by sending outputs, but not dismissing the dialog. A variant of this listener is the DSItemDoneListener, which will provide the outputs and dismiss the dialog.

These listeners actually invoke methods of the view to perform the appropriate actions, so it is quite easy to develop your own listeners to deal with any type of event.

There are three methods of the ViewModule that provide the functionality needed to manage the view. These three methods are typically invoked by the listeners. The listeners described above all call one of these methods. The viewDone() method will cause the outputs to be sent and it will dismiss the window. The method viewCancel() will simply dismiss the dialog, and none of the results will be passed on. viewContinue() will send the outputs, but it will not dismiss the dialog.

Let us look at the implementation of the DSItemListener to better understand how these listeners work:

package ncsa.d2k.controller.userviews.listeners;

import java.awt.event.*;
import ncsa.d2k.controller.userviews.widgits.*;

import ncsa.d2k.infrastructure.modules.ViewModule;

/**
When a selection is made, this listener will send a message back
to the module with the action equal that given to this guy
when it is constructed.
*/
public class DSItemListener implements ItemListener {
  ViewModule uiModule = null;
  String action = null;

  /**
  Start with the UserView and the action key.

  @param mod the user view.
  @param action the action key.
  */
  public DSItemListener( ViewModule mod, String action ) {
    this.uiModule = mod;
    this.action = action;
  }

  /**
  Item state change, do it.

  @param e the event.
  */
  public void itemStateChanged(ItemEvent e) {
    if (e.getSource () instanceof DSChoice) {
      this.uiModule.viewContinue ( this.action );
    }
  }
}

As we see here, when these listeners are created, their constructors are given the module that manages the view, and the action, or command name to be associated with the action. In the case of an ItemListener, the action method is the itemStateChanged method, which is called each time a selection is made in a list or popup menu. In this case, the viewContinue method is called. All of the outputs will be collected by the system and passed on to their respective modules. However, the view will not be dismissed.

How Results are Collected

When a user interface event occurs that invokes the appropriate methods, results are collected by examining all the subcomponents of the view to see if they implement the AddFields interface. We have already seen a list of the components that are available that implement this interface, and it is very easy to develop new ones.

As you can see, this simple interface requires only one method called addFields. This method takes a hashtable as a parameter. This hashtable actually contains the outputs. The method implementation is expected to add any necessary user input to the hashtable with the key usually being the name of the field. In this way, each component that implements this interface may contribute some output when the results are collected.

The constructors for the user interface widgets available will take an additional string. This is the field name and must be identical to the string returned by the getFieldNameMapping method of the module for the corresponding output.

When the OK button is clicked, the UserView will traverse the subcomponents, looking for any component that implements this interface. If a component does implement this interface, the addField method is called where the appropriate addition to the result hashtable is made. When the component hierarchy has been traversed, the resulting message is sent back to the module.

If the existing components we provide that implement the AddFields interface is sufficient, all the module developer need to do is arrange these components in the subclass of UserPane, and lay the container out in the constructor.

User Panes

We do provide some implementations of the UserView interface, and we will describe these here. The UserPane implements the UserView interface, and subclasses java.awt.Container. It provides the compileResults() method that is required of the UserView interface. This method is required by the system to compile all the results of the users inputs into a hashtable where the key is the name of component that implements the AddFields (described later) interface, and the value is the content of that field. This method provides the functionality that allows the system to obtain and pass on the outputs when the dialog is dismissed or just passing a set of outputs along.

The initView() method is also required of the UserView interface. This method is where view specific layout and additions to the gui are made. The UserPane class does not provide this method, as it is really an empty shell and does not include any predefined layout. Subclasses of UserPane must implement this method to layout the panel.

All subclasses of UserPane must also provide a method named setInput(). This method is called to pass the inputs received by the module to the view. Often, the inputs will be displayed as default values in the user interface, but this is not always the case. If the input is not used, the implementation should ignore it, but then why would there be such an input?

UserInputPane is a subclass of UserPane that provides a row of buttons across the bottom of the window. The buttons are labeled "OK" and "Cancel". When the OK button of this panel is clicked by the user, all the user inputs are collected and sent on to their respective outputs. The window is then dismissed. This is accomplished by simply attaching the DSSendInputListener to the OK button. If the Cancel button is clicked, the window is dismissed without providing the output. Here, we just add the DSCancelInputListener to the Cancel button.

There is also the PersistInputPane, which subclasses the UserInputPane, but adds one button labeled "Continue". When this button is clicked, the users inputs are all collected and passed as outputs, but the window is not dismissed. Can you guess which listener is attached to this button?

We also now provide swing counterparts for all these panes, they can be found the package ncsa.d2k.controller.userviews.swing, and have names JUserPane, JPersistentInputPane.