Richard G Baldwin (512) 223-4758, baldwin@austin.cc.tx.us, http://www2.austin.cc.tx.us/baldwin/

Swing, Hidden Buttons with Icons, Icon Images, Borders, Tool Tips, Nested Buttons, and Other Fun Stuff


Java Programming, Lecture Notes # 124, Revised 08/26/98.


Preface

Students in Prof. Baldwin's Advanced Java Programming classes at ACC are responsible for knowing and understanding all of the material in this lesson.
 

Introduction

As indicated in the title, this lesson deals with a lot of fun stuff in Swing.  The lesson was originally written using JDK 1.1.6 and Swing 1.0.1.  Much that you will see in this lesson is not documented in Swing 1.0.1 (at least if it is documented, I was unable to find it).  Rather, much of what you see here was created by examining the source code for the examples that came with the Swing download and inferring the behavior of various methods on the basis of that source code.

What is a hidden button?  If you use Netscape Communicator 4.x, then you are familiar with the type of buttons that I refer to as hidden buttons.

For example, the "Back" button in the Netscape browser in Communicator 4.x is a button of this type.  Its normal appearance is to display the word Back and a little green icon but there are no borders to cause it to look like a button.  However, when you touch it with the mouse, it suddenly displays borders that make it look like a button. The new capability of Swing to deal with borders makes it possible to create buttons that behave like this in Java.

Swing also makes it possible to place images on buttons (or on just about any Swing component for that matter).  We will use ImageIcon to create images to place on our buttons.

Not only will we have icons on the buttons, they will change as we move the mouse in the vicinity of the buttons.  This is the result of a new capability in Swing  referred to as rollover effects.

You have all seen tool tips in various applications.  Swing supports tool tips and we will demonstrate that capability in this lesson as well.

As you are already aware from a previous lesson, Swing supports Pluggable Look and Feel (PL&F).  Also, as you are already aware from a previous lesson, Swing makes it possible for components to contain other components.  Just for fun, we will build a pyramid of nested JButton components and observe them for different PL&F implementations.
 
 

Sample Program

The primary purpose of this program is to demonstrate "Hidden Buttons".  These are buttons which don't look like buttons until you touch them with the mouse but which "come alive" when you touch them.

By coming alive, they take on the appearance of a button according to the current look and feel.  This behavior is implemented using the new border capability of Swing. Also, the icon on the button changes to a different image when you touch the button with the mouse. This illustrates the new ImageIcon capability of Swing and several related behaviors (such as rollover effects).

A PL&F control panel is provided so that you can change the L&F during runtime.  Thus, the program also illustrates the appearance of Hidden Buttons under different look and feel implementations.

As mentioned earlier, the program also illustrates the use of tooltips.

And just for fun, the program illustrates that every Swing component is a container that can contain other components.  This capability is illustrated by building a pyramid of nested JButton objects where each object contains the objects above it.

It is very interesting to observe the appearance of this pyramid under different L&F implementations. For example, under the JavaSoft Metal L&F, it doesn't have a three-dimensional pyramid look.  However, under the Windows and Motif L&F, the three-dimensional appearance is very pronounced.

This program requires that several gif image files be placed in the directory that contains the class files. Otherwise, you won't be able to compile and run the program.

It doesn't matter what images the gif files contain.  Of course, to fit in the allotted space, they should be small images.  To be directly compatible with this program, the names of the gif files must be bulb1, bulb2, and bulb3. Obviously, you could modify the program to accommodate gif files with other names as well.

These gif files are used to change the icons on the buttons to illustrate the different states of the buttons.

When the program starts, two Hidden Buttons and a pyramid of nested JButton objects are displayed in a JFrame object on the screen.

In addition, a separate JFrame object is displayed containing buttons that can be used to select any of the different L&F implementations currently installed on the system. When you select an L&F implementation on this JFrame, it is applied to all of the components on both JFrame objects.

One of the Hidden Buttons is a JButton.  The other is a JToggleButton.  They are identified by the text on the buttons as well as the tool tip that appears when you cause the mouse pointer to hover over a button for a short period of time.

In my implementation, using my three gif files, the normal state of the buttons is to display a light bulb that is not illuminated.

When you touch the button with the mouse (rollover), the borders appear and the light bulb image changes to one that is illuminated.

When you press the button, the light bulb changes to one that is blue and it remains blue until you release the mouse button. (See a later comment regarding the different rollover behavior of the JToggleButton when it is selected.)

Of course, your version will be somewhat different, depending on the three images that you select.

The buttons don't do anything when you click them because no action listeners were registered on them.

This program illustrates the JToggleButton which is new to Swing.

Whereas a normal JButton object responds and returns to its original appearance when you click it, a JToggleButton responds but does not return to its original appearance.  In other words, clicking on a JToggleButton causes it to toggle between two different appearances (and two different readable states as well).

Although it isn't demonstrated in this program, the two states of a JToggleButton are true and false.  Your program can determine the state by invoking the isSelected() method on the button. The change in appearance between being selected and not selected depends on the L&F in effect at the time.

Note that unlike the JButton object, the JToggleButton object doesn't change to the rolloverIcon when it is in the selected state.  It displays the normalIcon instead. However, like the JButton, it does display the rolloverIcon when it is touched by the mouse pointer but is not selected.

This program was tested using JDK 1.1.6 and Swing 1.0.1 under Windows 95
 
 

Interesting Code Fragments

We will begin with the opening line of the controlling class and the main() method.  As you can see, the controlling class extends JFrame so it is a JFrame object.

The main() method instantiates two objects.  The first is an object of the type of the controlling class.  The second is an object of the class named PlafPanel02.

You should already be familiar with the concept involved with this second object.  It was explained in an earlier lesson on Pluggable Look & Feel.  This version of the PlafPanel class differs from the one used in that lesson only to the extent that this is a totally stand-alone control panel for selecting the L&F of the associated GUI. A complete listing of the source code for PlafPanel02 is provided at the end of this lesson.

A GUI is associated with this L&F control panel by passing a reference to the GUI when the PlafPanel02 object is instantiated.  From that point forward, selecting a new L&F on the control panel will cause the L&F of the associated GUI to change to the selected L&F (the L&F of the control panel also changes as well).

The GUI named demoFrame is passed to the constructor for PlafPanel02 to associate the GUI with the L&F control panel in this program.
 
 
public class Swing09 extends JFrame {



  public static void main(String args[]) {

    Swing09 demoFrame = new Swing09();

    PlafPanel02 plafPanel = new PlafPanel02(demoFrame);

  }//end main()

That brings us to the constructor for the primary GUI for this program. We begin with the preliminaries by creating a title, setting the layout, and adding a JLabel object that contains some explanatory information.
 
  Swing09() {//constructor

    this.setTitle("Copyright 1998, RG Baldwin");

    this.getContentPane().setLayout(new FlowLayout());

    this.getContentPane().add(new JLabel(

       "Hidden Button Demo, Copyright 1998, R.G.Baldwin"));

 The next fragment instantiates a JPanel object containing the Hidden Buttons and adds the panel to the JFrame object that is the GUI. We will examine the class from which is panel is constructed shortly.
 
    this.getContentPane().add(new HiddenButtonPanel());

Next, just for fun, we build a pyramid of nested JButton objects so that we can observe their appearance under the different L&F implementations and set a tool tip on the top button..

Following this, we set the size of the GUI and make it visible.

Then we add an anonymous inner-class WindowListener to terminate the program when the user closes the JFrame object.

And that ends the constructor for the controlling class and also ends the controlling class definition.
 
    JButton a = new JButton();

    JButton b = new JButton();

    JButton c = new JButton();

    JButton d = new JButton();

    JButton e = new JButton();

    JButton f = new JButton("Top");

    f.setToolTipText("Nested JButton objects");

    this.getContentPane().add(a);

    a.add(b);

    b.add(c);

    c.add(d);

    d.add(e);

    e.add(f);



    //Set size and display the GUI  

    this.setSize(400,280);

    this.setVisible(true);

    

    // Create an inner class WindowAdapter to terminate the

    // program when the JFrame is closed.

    this.addWindowListener(new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);}});//end WindowListener

    

  }//end constructor  

}//end class Swing09

That brings us to the definition of the class that is used to instantiate a panel containing the two Hidden Buttons.

We begin by instantiating three different objects of type ImageIcon.  The ImageIcon class implements the Icon interface, so these objects can be used anywhere an object of type Icon is required.

In our case, we will need to provide a reference to an object of type Icon in three different statements associated with each of our Hidden Buttons.  We will discuss those statements later.

There are several different constructors that can be used to instantiate an object of type ImageIcon.  We are using a version of the constructor that requires a String specifying the name of the file containing the image.

This fragment also declares reference variables for our two Hidden Button objects.
 
class HiddenButtonPanel extends JPanel{



  ImageIcon normalIcon = new ImageIcon("bulb3.gif");

  ImageIcon rolloverIcon = new ImageIcon("bulb2.gif");

  ImageIcon pressedIcon = new ImageIcon("bulb1.gif");



  JToggleButton myJToggleButton;

  JButton myJButton;

That brings us to the constructor for our HiddenButtonPanel class. There is a lot of new material contained in this constructor.

This constructor instantiates two different Hidden Buttons;

Other than their type, the code to instantiate and prepare them for use is identical.  Therefore, this fragment will deal only with the JButton object.  You can find the code for the JToggleButton object in the complete listing of the program that follows later.

We begin by instantiating a JButton object using a constructor that allows us to specify a reference to an Icon image object that will appear on the button.  The ability to put images on a button is new to Swing.  This capability does not exist in the AWT.  In this case, we specify one of the ImageIcon objects instantiated earlier (normalIcon).

Next, we invoke the setRolloverIcon() method to specify an image that will automatically appear on the button when we touch the button with the mouse pointer.  Again, we provide a reference to one of the ImageIcon objects instantiated earlier (rolloverIcon),.

After this, we invoke the setPressedIcon() method to specify an image that will automatically appear on the button while it is pressed, passing one of our ImageIcon objects as a parameter (pressedIcon).

These are all new capabilities in Swing that do not exist in the AWT.

Finally, as the last action in this fragment, we invoke the setRolloverEnabled() method on the button to "enable the rollover effects."

As mentioned earlier, I was unable to find any explanatory information in the documentation for Swing 1.0.1 regarding the "rollover effects.".  Everything here was inferred from the very cryptic statements in the API documentation and an examination of the example programs that came with the Swing download.
 
  HiddenButtonPanel(){//constructor



    myJButton = new JButton("JButton", normalIcon);

    myJButton.setRolloverIcon(rolloverIcon);

    myJButton.setPressedIcon(pressedIcon);

    myJButton.setRolloverEnabled(true);

Tool Tips are amazingly easy to create with Swing.  The first statement in the next fragment is all that is required to associate a tool tip with an object.

After setting the text for the tool tip on the button by invoking the setToolTipText() method, we cause the borders on the button to be invisible by invoking setBorderPainted() and passing false as a parameter.

Until this is changed, the borders on the button will not be painted, and the button really won't be recognizable as a button.

We wrap up this fragment by adding the JButton object to the panel.
 
    myJButton.setToolTipText("This is a JButton");

    myJButton.setBorderPainted(false);

    this.add(myJButton);

We will wrap up this discussion with the code fragment that causes the button to appear when you touch it with the mouse pointer and causes it to disappear when you move the mouse pointer away.

This is a simple anonymous inner-class mouse listener that responds when the mouse enters and leaves the boundary of the button.  Except for the fact that it invokes the setBorderPainted() method with either true or false to expose the borders of the button or make them invisible, there is nothing new here.  You should be completely familiar with this approach to processing mouse events.

Note that in this program, we processed mouse events to cause the borders on the button to be either visible or invisible.

It is also very important to note that we weren't required to provide any special event handling to cause the icon on the button to change when the mouse pointer entered and exited the boundary of the button, and to change again when the button was pressed.  That behavior is automatic once you properly invoke the setRolloverIcon(), setPressedIcon(), and setRolloverEnabled() methods discussed earlier.

In a way, I hate to see this because it tends to break the structured nature of the API.  Normally it is necessary for us to provide event handlers to deal with these kinds of  events, but in this case, the event handling is being provided automatically.  If much of this sort of thing creeps into the API, it could lead to  confusion as to when it is and when it is not necessary to provide event handling.
 
    myJButton.addMouseListener(new MouseAdapter(){



      public void mouseEntered(MouseEvent mouseEvent){

        myJButton.setBorderPainted(true);

      }//end mouseEntered



      public void mouseExited(MouseEvent mouseEvent){

        myJButton.setBorderPainted(false);

      }//end mouseExited()



    });//end inner-class definition

.
 

Program Listing

This section contains a complete listing of the program. A listing of the source code for the utility program named PlafPanel02 is provided in the next section.
 
/*File Swing09 Copyright 1998, R.G.Baldwin

The primary purpose of this program is to demonstrate

"Hidden Buttons".  These are buttons which don't look

like buttons until you point to them with the mouse but

which "come alive" when you point to them.  By coming

alive, they take on the appearance of a button according

to the current look and feel.  This behavior is 

implemented using the new border capability of Swing.

Also, the image icon on the button changes to a different

image when you point to the button with the mouse. This

illustrates the new image icon capability of Swing.



The program illustrates the appearance of Hidden Buttons 

under different look and feel implementations.



The program also illustrates the use of tooltips.



And just for fun, the program also illustrates that every

Swing component is a container that can contain other

components.  This is done by building a pyramid of nested

JButton objects where each contains the objects above it.

It is interesting to observe the different appearance

of this pyramid under different L&F implementations.



This program requires that several icon sized gif files

be placed in the directory that contains the class

files. The names of the gif files must be bulb1, bulb2,

and bulb3.



These gif files are used to illustrate the different states

of image icons.



When the program starts, two Hidden Buttons and a pyramid

of buttons are displayed in a JFrame.  



In addition, a separate JFrame is displayed containing

buttons that can be used to select any of the different L&F

implementations currently installed on the system. When 

you select a L&F implementation on this JFrame, it is 

applied to all of the components on both JFrame objects.



One of the Hidden Buttons is a JButton.  The other is a

JToggleButton.  They are identified by the text on the

button as well as the tool tip that appears when you

pause with the mouse pointing at one of the buttons.



The buttons don't do anything when you click them because

no action listeners were registered on them.



Note that the JToggleButton doesn't respond to the 

rolloverIcon when it is in the pressed state.  It displays

the normalIcon instead.



Tested using JDK 1.1.6 and Swing 1.0.1 under Windows 95



**********************************************************/



import java.awt.*;

import java.awt.event.*;

import com.sun.java.swing.*;



// Subclass JFrame for the GUI

public class Swing09 extends JFrame {//controlling class



  public static void main(String args[]) {

    //Instantiate an object of this type

    Swing09 demoFrame = new Swing09();

    //Instantiate and link a PlafPanel to it

    PlafPanel02 plafPanel = new PlafPanel02(demoFrame);

  }//end main()

  //-----------------------------------------------------//

  

  Swing09() {//constructor

    this.setTitle("Copyright 1998, RG Baldwin");

    this.getContentPane().setLayout(new FlowLayout());

    this.getContentPane().add(new JLabel(

       "Hidden Button Demo, Copyright 1998, R.G.Baldwin"));

       

    //Instantiate and add the panel containing the hidden

    // buttons.

    this.getContentPane().add(new HiddenButtonPanel());

    

    //Just for fun, build a pyramid of nested JButton 

    // objects and observe their appearance under the 

    // different L&F implementations.

    JButton a = new JButton();

    JButton b = new JButton();

    JButton c = new JButton();

    JButton d = new JButton();

    JButton e = new JButton();

    JButton f = new JButton("Top");

    f.setToolTipText("Nested JButton objects");

    this.getContentPane().add(a);

    a.add(b);

    b.add(c);

    c.add(d);

    d.add(e);

    e.add(f);



    //Set size and display the GUI  

    this.setSize(400,280);

    this.setVisible(true);

    

    // Create an inner class WindowAdapter to terminate the

    // program when the JFrame is closed.

    this.addWindowListener(new WindowAdapter() {

      public void windowClosing(WindowEvent e) {

        System.exit(0);}});//end WindowListener

    

  }//end constructor

  //-----------------------------------------------------//

   

}//end class Swing09

//=======================================================//



//This class is used to instantiate a panel containing

// two Hidden Buttons.

class HiddenButtonPanel extends JPanel{

  //Create new image icons using gif files.  You will need

  // to provide icon sized gif files with names that match

  // the file names in the following statements to be able

  // to run this program.  These images will appear on the

  // buttons in their different states. ImageIcon

  // implements the Icon interface so it can be used 

  // wherever an Icon is required (such as instantiation

  // of a JButton with an Icon).

  ImageIcon normalIcon = new ImageIcon("bulb3.gif");

  ImageIcon rolloverIcon = new ImageIcon("bulb2.gif");

  ImageIcon pressedIcon = new ImageIcon("bulb1.gif");



  //Declare references to two buttons of two different

  // types.

  JToggleButton myJToggleButton;

  JButton myJButton;

  

  HiddenButtonPanel(){//constructor

    //Create and prep the JButton object

    //Instantiate a JButton object with a caption and

    // an icon.

    myJButton = new JButton("JButton", normalIcon);

    //Establish icon to be displayed during mouse rollover

    myJButton.setRolloverIcon(rolloverIcon);

    //Establish icon to be displayed when button is pressed

    myJButton.setPressedIcon(pressedIcon);



    //Enable the Swing rollover effects.

    myJButton.setRolloverEnabled(true);

    //Set text for the tool tip for the button.

    myJButton.setToolTipText("This is a JButton");

    //Hide the border on the button

    myJButton.setBorderPainted(false);

    //Add the button to the panel

    this.add(myJButton);

    

    //Inner class listener to display and hide borders

    // on the button when the mouse enters and exits.

    myJButton.addMouseListener(new MouseAdapter(){

      public void mouseEntered(MouseEvent mouseEvent){

        //Display border on the button

        myJButton.setBorderPainted(true);

      }//end mouseEntered

      public void mouseExited(MouseEvent mouseEvent){

        //Hide the border on the button

        myJButton.setBorderPainted(false);

      }//end mouseExited()

    });//end inner-class definition



    //Create and prep the JToggleButton object  .  See

    // explanatory comments in the code for the JButton

    //above.

    myJToggleButton = new JToggleButton(

                             "JToggleButton", normalIcon);

    myJToggleButton.setRolloverIcon(rolloverIcon);

    myJToggleButton.setPressedIcon(pressedIcon);

    

    myJToggleButton.setRolloverEnabled(true);

    myJToggleButton.setToolTipText(

                               "This is a JToggleButton");

    myJToggleButton.setBorderPainted(false);

    this.add(myJToggleButton);

    

    //Inner class listener to respond to rollover

    myJToggleButton.addMouseListener(new MouseAdapter(){

      public void mouseEntered(MouseEvent mouseEvent){

        myJToggleButton.setBorderPainted(true);

      }//end mouseEntered

      public void mouseExited(MouseEvent mouseEvent){

        myJToggleButton.setBorderPainted(false);

      }//end mouseExited()

    });//end inner-class definition    

  }//end constructor  

}//end class HiddenButtonPanel

//=======================================================//
.
 

Program Listing for PlafPanel02

This section contains a complete listing for the utility program named PlafPanel02 that was used to implement the PL&F capability in this program.
 
 
/*File PlafPanel02 Copyright 1998, R.G.Baldwin

This is a revision to PlafPanel01.  This version is 

designed to stand alone inside its own JFrame object.

To use this program, simply instantiate an object of

this type and pass a reference to the JFrame under test

as a parameter.



The purpose of this class is to construct an object that

can easily be associated with a GUI to test the GUI

for all of the Look and Feel implementations installed 

with the current JDK.



To associate this object with a GUI under test, pass

a reference to the JFrame containing the GUI as a

parameter when this object is constructed.



This class creates a JFrame. The JFrame contains one 

JButton for each L&F implementation in the currently 

installed JDK.  Clicking each JButton will cause the L&F

of the GUI to change to the L&F represented by that 

JButton.



The name of the L&F is displayed on the JButton.

  

The key statements in selecting and then implementing

the new L&F are:

  

UIManager.setLookAndFeel(plafClassName);  

  

SwingUtilities.updateComponentTreeUI(thisPlafPanel);

SwingUtilities.updateComponentTreeUI(testGui);

                         

These statements are discussed further in the comments

in the program.

                           

Tested using JDK 1.1.6 and Swing 1.0.1 under Win95.

**********************************************************/



import java.awt.event.*;

import java.awt.*;

import com.sun.java.swing.*;

import java.util.*;



public class PlafPanel02 extends JFrame {

  /*

  The following is an unusual reference variable type that

  is used to refer to an array of L&F information.  As of

  Swing 1.0.1, the document that should explain this type

  is missing from the download documentation file.

  

  The best available information seems to be the following

  statement that was extracted from the description of the

  method named getInstalledLookAndFeels() that returns an 

  array object of this type (a minor typo was corrected

  by the author in this quotation):



  "Return an array of objects that provide some 

  information about the LookAndFeel implementations

  that have been installed with this java development kit.

  The LookAndFeelInfo objects can be used by an 

  application to construct a menu of look and feel options

  for the user or to set the look and feel at start up

  time."

  */

  UIManager.LookAndFeelInfo[] plafInfoArray;

  //-----------------------------------------------------//

  JFrame testGui;//save a reference to the test GUI here

  PlafPanel02 thisPlafPanel = this;//ref to this object

  

  public PlafPanel02(JFrame testGui) {//constructor

    this.testGui = testGui;//save ref to test GUI

    this.getContentPane().add(new JLabel(

       "PL&F Selection Panel, Copyright 1998, RGBaldwin"));

  

    //Get the list of L&Fs installed with the current JDK

    //See note above regarding this method.

    plafInfoArray = UIManager.getInstalledLookAndFeels();

 

    //Create a vector of references to JButton objects 

    // with one element in the vector for each L&F

    // implementation in the current JDK. 

    Vector theButtons = new Vector(plafInfoArray.length);

    

    //Create one JButton object for each L&F implementation

    // and put its reference in the Vector.

    for(int cnt = 0; cnt < plafInfoArray.length; cnt++){

      theButtons.addElement(new JButton());

      

      //Get the name of the class for each specific L&F

      // implementation

      String theClassName = 

                         plafInfoArray[cnt].getClassName();

      

      //Extract a short name for each specific L&F 

      // implementation and use it to label the JButton 

      // corresponding to that L&F. The short name appears

      // following the last period in the String

      // representation of the class name for the 

      // L&F implementation. Note the requirement to

      // downcast the reference extracted from the Vector.

      String label = theClassName.substring(

                          theClassName.lastIndexOf(".")+1);

      ((JButton)theButtons.elementAt(cnt)).setText(label);

      

      //Add an action listener to each JButton that will

      // cause the L&F to change to the one represented by

      // that JButton whenever the JButton is clicked. Note

      // that because the references to the buttons are

      // stored in a Vector, it is necessary to downcast

      // them from Object to JButton.

      ((JButton)theButtons.elementAt(cnt)).

          addActionListener(new MyActionListener(

                                            theClassName));

      

      //Add each JButton to the JFrame.

      this.getContentPane().add((JButton)theButtons.

                                           elementAt(cnt));

    }//end for loop

    this.getContentPane().setLayout(new FlowLayout());

    this.setTitle("Copyright 1998, R.G.Baldwin");

    this.setBounds(30,300,350,150);

    this.setVisible(true);

  }//end constructor

  //=====================================================//

  

  //Inner class for action listeners

  class MyActionListener implements ActionListener{

    String plafClassName;//save name of plaf class here



    //Constructor

    MyActionListener(String plafClassName){

      //save the incoming parameter

      this.plafClassName = plafClassName;

    }//end constructor

    

    public void actionPerformed(ActionEvent e){

      //Set the current default L&F to that passed in as

      // a parameter

      try{

        UIManager.setLookAndFeel(plafClassName);

      }catch(Exception ex){System.out.println(ex);}

      

      //Now implement the current default L&F to make it

      // take effect. The description of the 

      // updateComponentTreeUI() method as extracted from

      // the documentation is as follows:

        

      //"A simple minded look and feel change: ask each 

      // node in the tree to updateUI(), i.e. to 

      // initialize its UI property with the current look

      // and feel."

      

      //Set the L&F for this PlafPanel object

      SwingUtilities.updateComponentTreeUI(thisPlafPanel);

      //Set the L&F for the test GUI object

      SwingUtilities.updateComponentTreeUI(testGui);

                                    

    }//end actionPerformed()

  }//end class MyActionListener

  

  //main method for stand-alone testing

  public static void main(String[] args){

    new PlafPanel02(new JFrame());

  }//end main()

}//end class PlafPanel02

//=======================================================//
 .
-end-


These tutorials were developed by Richard Baldwin and are the copyrighted property of Richard Baldwin. You have permission to print one copy for your own use, but may not, without written permission from Richard Baldwin, redistribute the tutorial documents.

The base material in these lessons is believed by the author to be in the public domain. If you use these lessons for any purpose, you are using them at your own risk, and this author assumes no responsibility or liability for any damages that you may incur.

Java, Sun, HotJava and various other related symbols and names are registered trademarks of Sun Microsystems, Inc. Macintosh is a registered trademark of Apple Computer, Inc. OS/2 is a registered trademark of International Business Machines Corporation. Microsoft, MS-DOS, Visual Basic, Windows, Windows NT, Internet Explorer and Visual J++ are registered trademarks of Microsoft Corporation. Netscape and JavaScript are trademarks of Netscape Communications Corporation. All other trademarks and service marks that may have been inadvertently used in these lessons without proper credit being given are the property of their respective owners. If you feel that your trademark or copyright has been compromised, please notify this author immediately, and an appropriate correction to the document will be issued.


© 1996, 1997, 1998, 1999 Richard G. Baldwin