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.
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.
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.
DSMultiSelectList.DS_INT_ARRAY
or DSMultiSelectList.DS_STRING_ARRAY, to change the return
type. The default is DS_INT_ARRAY.
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.
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.
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.
![]() |