Making a VisAD widget collaborative
When writing a widget for a standalone VisAD application,
the usual procedure is to create a Swing widget and, when
that widget changes, change the Display appropriately.
For example, here's a code fragment which adds the ability
to turn the VisAD box on and off:
- JCheckBox cb = new JCheckBox("Box", true);
- cb.addItemListener(new ItemListener() {
- public void itemStateChanged(ItemEvent E){
- try {
- boolean isSelected = (E.getStateChange() == ItemEvent.SELECTED);
- display.getDisplayRenderer().setBoxOn(isSelected);
- } catch (VisADException ve) {
- System.err.println(ve.getMessage());
- } catch (RemoteException re) {
- System.err.println(re.getMessage());
- }
- }
- });
Line 1 creates a standard checkbox widget.
Line 2 tells the widget to pass any change events to
the listener method in lines 3-12.
The meat of that listener method is lines 5 and 6; the other
lines are there simply to catch exceptions.
Line 5 check to see whether the box is checked or unchecked.
Line 6 specifies that the box be turned on or off, based to
the value computed in line 5.
When writing a collaborative graphical widget, things are a bit more
complicated. If the box widget above were used in a collaborative
application, a remote change would toggle the box visibility (if the
box were initially visible, for example, and a remote user turned it
off) but the checkbox widget would remain checked.
In VisAD, Displays keep much of their state in Controls and ScalarMaps.
Applications are notified of remote changes via listener methods
on those Controls and ScalarMaps.
Box visibility is tracked by the RendererControl, so the application
needs to first fetch that Control and then add a method to listen for
changes to that Control.
The Display's RendererControl can be fetched via the getControl()
method which takes the Control's class as an argument and returns
the first (and in this case, only) Control of that type:
rendCtl = (RendererControl )dpy.getControl(RendererControl.class);
The RendererControl listener method would look like:
- rendCtl.addControlListener(new ControlListener() {
- public void controlChanged(ControlEvent evt)
- throws RemoteException, VisADException
- {
- boolean rState = rendCtl.getBoxOn();
- boolean jState = cb.isSelected();
- if (jState != rState) {
- cb.setSelected(rState);
- }
- }
- });
Line 1 tells the RendererControl to pass any change events to
the listener method in lines 2-10.
Lines 2-4 are the standard declaration for a visad.ControlListener.
Line 5 sets rState to true if the Box is currently visible.
Line 6 sets jState to true if the checkbox is currently checked.
Lines 7 and 8 set the checkbox to the proper state if it doesn't currently
reflect the box visibility.
The initial box widget creates a JCheckBox widget which is already checked,
because VisAD Displays are created with a visible box. This isn't a valid
assumption in a collaborative application, because a client can join a
session at any point, so the defaults may have been changed.
To fix this, the graphics widget should be initialized
using the current Control state as a guide. For the box widget,
the initialization code would be:
cb.setSelected(rendCtl.getBoxOn());
Adding all the previous code results in a box widget which works
the same on standalone, server and client applications and which
is initialized properly no matter when it is started. The final
(optional) step would be to move all this code into a separate
class, so it can easily be reused in other applications.
Here's the code as a BoxWidget class:
import java.awt.event.*;
import java.rmi.RemoteException;
import javax.swing.JCheckBox;
import visad.*;
public class BoxWidget
extends JCheckBox
{
RendererControl ctl;
public BoxWidget(LocalDisplay dpy)
{
super("Box", true);
ctl = (RendererControl )dpy.getControl(RendererControl.class);
setSelected(ctl.getBoxOn());
ctl.addControlListener(new ControlListener() {
public void controlChanged(ControlEvent evt)
throws RemoteException, VisADException
{
boolean drState = ctl.getBoxOn();
if (isSelected() != drState) {
setSelected(drState);
}
}
});
addItemListener(new ItemListener() {
public void itemStateChanged(ItemEvent evt){
try {
boolean jState = evt.getStateChange() == ItemEvent.SELECTED;
ctl.setBoxOn(jState);
} catch (VisADException ve) {
System.err.println(ve.getMessage());
} catch (RemoteException re) {
System.err.println(re.getMessage());
}
}
});
}
}
Last modified: Tue Jul 11 15:01:53 CDT 2000