Wizard API updated!
Tim Boudreau has released a new version of the Swing Wizard library (version 0.997) that fixes the WizardException bug reported in JavaWorld's recent Open Source Java Project profile. The article's examples have been reworked to test out the new, improved WizardException. Thanks, Tim, for this helpful fix!
Open Source Java Projects: The Wizard API

Newsletter sign-up

Sign up for our technology specific newsletters.

Enterprise Java
View all newsletters

Email Address:

Draw the world: Create networked whiteboards with Java 1.1

Learn how to easily write a whiteboard using the new features of JDK 1.1

A whiteboard is a simple drawing utility, commonly supplied as part of a collaboration framework to allow distributed users to share a common drawing space. In this article, we will examine how JDK 1.1 simplifies the implementation of such an application.

Lightweight components, introduced by JDK 1.1, offer several important advantages over traditional JDK 1.0-style components. For one, they are much more efficient than traditional components because they do not make use of native GUI peers, being instead a pure-Java kluge on top of the old AWT. More importantly for this particular application, however, is that these components are transparent. This means that a lightweight component can easily overlay information on top of another component.

We will use this transparency feature to implement our whiteboard; when the user selects a particular drawing tool then a lightweight component will be overlaid on the whiteboard. The drawing tool can then use this component to annotate the whiteboard display and collect GUI events.

The whiteboard

Our whiteboard is perforce simple; the constraint of understandability requires that it be of fairly limited expanse. Hopefully, however, the techniques I demonstrate will be of fairly broad use, and will allow you to easily modify the application to suit your own needs.

The whiteboard in action

The following classes comprise the whiteboard:

  • WBLauncher -- A simple applet that launches the whiteboard when clicked.

  • WB -- The main whiteboard class, which is a standalone Frame containing all of the whiteboard components.

  • Tool -- An interface that provides access to all the whiteboard's drawing tools.

  • Element -- An interface that describes the elements of a whiteboard drawing.

  • WBContainer -- A lightweight container that displays the contents of the whiteboard.

  • Rect -- A drawing tool that allows rectangles of various colors to be placed on the whiteboard.

  • Select -- A drawing tool that allows existing elements of the whiteboard to be moved.

  • ObservableList -- A class that maintains the list of elements comprising a whiteboard drawing. It is very similar to a Vector except that it notifies listeners whenever a change is made to the list.

  • UpdateListener -- An interface that must be implemented by classes wishing to be notified when an ObservableList is modified.

  • UpdateEvent -- An event that notifies of a change to an ObservableList; it is delivered through the ObservableList interface.

  • LWContainer -- A generic lightweight container; a lightweight equivalent to the Panel class.

  • LWImageButton -- A generic lightweight image button.


This may seem like a large number of classes, but the core of the whiteboard is, in fact, surprisingly small. The WB class contains the controlling logic of the whiteboard; the WBContainer class contains the display code; and the Tool and Element interfaces describe the various tools and elements.

Stepping through the classes

For the sake of brevity, I will discuss the classes in general terms only, allowing you to read the source for a greater understanding.

Class WBLauncher
WBLauncher is a simple applet that provides a simple introductory interface to the whiteboard, delaying the download of the whiteboard's classes until the user chooses to launch it.

The whiteboard launcher

Because we are using the JDK 1.1 AWT, we must explicitly declare interest in GUI events in order to receive them. For this reason, we call the enableEvents() method in order to receive mouse events:

enableEvents (AWTEvent.MOUSE_EVENT_MASK);


These events are delivered through the new method hierarchy to our processEvent() method that can process the event as necessary:

protected void processMouseEvent (MouseEvent e) ...


In this next method, we use a Class.forName().newInstance() sequence to create an instance of the whiteboard. Using this mechanism, the whiteboard classes are only downloaded when the user actually clicks on the launcher:

Class theClass = Class.forName ("org.merlin.step.nov.WB");
wb = (Frame) theClass.newInstance ();


To initialize the whiteboard, we pass a dummy ActionEvent to its dispatchEvent() method with this as the source of the event; the whiteboard will then make use of our Applet methods. In particular, it can call getCodeBase() to determine its origin and can then download its configuration and image files:

wb.dispatchEvent (new ActionEvent (this, AWTEvent.RESERVED_ID_MAX + 1, ""));


Class WB
The WB class is a Frame subclass that sets up and controls the whiteboard. Under JDK 1.1, it is no longer necessary to subclass Frame in order to implement a freestanding application; however, you may find this approach to be convenient in some cases.

The whiteboard layout

As a component subclass, we must again call enableEvents() in order to receive events; in this case, window events:

enableEvents (AWTEvent.WINDOW_EVENT_MASK);


These window events will be passed to the processWindowEvent() method where we can handle the user closing the window:

protected void processWindowEvent (WindowEvent e) {
  super.processWindowEvent (e);
  if (e.getID () == WindowEvent.WINDOW_CLOSING)
    setVisible (false);
}


To trap the ActionEvent that the WBLauncher passes to initialize the whiteboard, we must also override the processEvent() method:

protected void processEvent (AWTEvent e) {
  if ((e instanceof ActionEvent) && (e.getSource () instanceof Applet))
    init ((Applet) e.getSource ());
  else
    super.processEvent (e);
}


When the whiteboard is initialized, it downloads a simple configuration file. To parse this text file, we use the new Reader classes of JDK 1.1, which are more efficient, correct, and generalized for reading text than the InputStream classes. We use the character encoding "latin1", which stands for ISO Latin 1.

URL u = new URL (parent.getCodeBase (), "config.txt");
InputStreamReader i = new InputStreamReader (u.openStream (), "latin1");
BufferedReader r = new BufferedReader (i);
String line;
while ((line = r.readLine ()) != null) ...


The rest of the initialization simply lays out the user interface, which includes a row of tool icons on the left of the frame, a tool control panel at the bottom, and a whiteboard in the remaining space.

1 | 2 |  Next >
Resources
  • Rich Kadel explains how to subclass an AWT component so you can build an image button for toolbars, palettes, and other "icon-friendly" environments http://www.javaworld.com/javaworld/jw-03-1997/jw-03-imagebutton.html
  • Todd Sundsted's "Examining HotSpot, an object-oriented drawing program" (JavaWorld December, 1996) provides information a JDK 1.0.2 drawing tool that uses some techniques similar to the ones we discuss http://www.javaworld.com/javaworld/jw-12-1996/jw-12-howto.html
  • Todd's "Build dynamically extensible applications" (JavaWorld September, 1997) explains how to use Class.forName().newInstance() to load classes at runtime http://www.javaworld.com/javaworld/jw-09-1997/jw-09-howto.html
  • My own "Moving to JDK 1.1" (JavaWorld May, 1997)
  • details how to create custom components in JDK 1.1 http://www.javaworld.com/javaworld/jw-05-1997/jw-05-step.html
  • Download this article and the complete source code a gzipped tar file http://www.javaworld.com/javaworld/jw-11-1997/step/jw-11-step.tar.gz
  • Download this article and the complete source code a zip file http://www.javaworld.com/javaworld/jw-11-1997/step/jw-11-step.zip
  • Previous Step by Step articles