5.1 The TuttleButton class

The TuttleButton class will supply a button which implements push-button behaviour and is capable of displaying an image, instead of the text label of the pre-supplied Java AWT Button class. The state transition diagram of the behaviour of a push-button is presented in Figure 5.1 and Table 5.1.

Figure 5.1 The TuttleButton State Transition Diagram

Table 5.1 State table for the TuttleButton STD in Figure 5.1.

The behaviour indicates that a button becomes primed when the mouse button is pressed whilst the mouse pointer is inside the button; this is confirmed to the user by a visual feedback which shows the button as if it were depressed. If the button is released whilst the mouse pointer is still inside the button the button is raised and an action event is generated, as indicated by the dashed arrow leaving transition 4. However, if the mouse pointer is moved outside the button after being primed, the button will be un-primed and raised, and when the mouse pointer is subsequently released no action event will be generated.

Figure 5.2 Highlighting of buttons, (the reversal of the icon is not significant).

The mechanism for showing a raised and depressed button is illustrated in Figure 5.2. The button is surrounded by a pseudo three dimensional border, which has been emphasised in this illustration. It is imagined that the button is illuminated by a light shining from the top left. In the left hand illustration the button is raised so the right and bottom of the button are in shade and are shown in a darker hue. In the right hand illustration the button is depressed so the left and top of the button are in the shade and are shown in a darker hue.

The class diagram for the TuttleButton class is given in Figure 5.3.

Figure 5.3 The TuttleButton class diagram.

The first constructor requires the identity of the Applet and the source of the image to be used by the button to be specified and will use a default, grey, border colour. The second constructor allows the colour to be used for the bordering to be explicitly specified, as well as the Applet and the source for the image. The addNotify() action is used to initialise the button after its peer has been constructed. The getMinimumSize(), getPreferredSize(), update() and paint() actions are used by the button to interact with layout negotations and to display itself, using the techniques which have previously been described. The processMouseEvent() action will implement the behaviour shown in the STD in Figure 5.1, and is supported by the setActionCommand(), getActionCommand(), addActionListener() and removeActionListener() actions. These are provided to allow instances of this class to interact with other Compoenents in a manner directly comparable to the AWT Button class, as described in the previous chapter. The implementation of this class, as far as the end of the constructors, is as follows.

0001  // Filename TuttleButton.java. 0002  // Contains an extended Canvas component to supply 0003  // button behaviour with an image as its label. 0004  // 0005  // Written for the Java Interface book, Chapter 5. 0006  // Fintan Culwin, v 0.2, August 1997. 0007   0008  package Tuttles; 0009   0010  import java.awt.*; 0011  import java.awt.event.*; 0012  import java.applet.*; 0013  import java.awt.image.*; 0014   0015   0016  public class TuttleButton extends Canvas {  0017   0018  private static final int     BORDER_WIDTH         = 2; 0019  private static final Color   DEFAULT_BORDER_COLOR =  0020                                        new Color( 0x80, 0x80, 0x80);  0021   0022  private Image           buttonImage   = null; 0023  private String          imageSource   = null; 0024  private int             buttonWidth   = -1; 0025  private int             buttonHeight  = -1; 0026  private boolean         pressed       = false; 0027  private Color           borderColour;  0028  private String          actionCommand = null; 0029  private ActionListener  itsListener   = null; 0030  private Applet          itsApplet; 0031   0032   0033     public TuttleButton( String theSource,  0034                          Applet applet) {  0035        this( theSource, applet,  DEFAULT_BORDER_COLOR);  0036     } // End TuttleButton constructor. 0037   0038     public TuttleButton( String theSource, 0039                          Applet applet, 0040                          Color  colorForBorder) {  0041        super(); 0042        imageSource = new String( "Tuttles/Images/" + theSource);        0043        itsApplet = applet; 0044        this.setForeground( colorForBorder);        0045        this.enableEvents( AWTEvent.MOUSE_EVENT_MASK);  0046     } // End TuttleButton constructor.

The class wide constant attributes, BORDER_WIDTH and DEFAULT_BORDER_COLOR, determine the size of the pseudo three dimensional border and the default border colour, if no colour is explicitly specified as the button is constructed. The instance attributes indicate the Image which is to be used and theSource filename where it can be obtained from, the overall buttonWidth and buttonHeight, the pressed state of the button, its borderColor of the button, the commandString and itsListener object, and finally the identity of itsApplet.

The first constructor indirects to the second, passing the DEFAULT_BORDER_COLOR as the Color argument. The second constructor calls the super constructor and then, on line 0042, catenates the theSource argument, identifying the file which contains the image for this button with a path string which indicates that it is expected to be found in the Images sub-directory of the Tuttles package directory. The full pathname and filename string is stored in the imageSource instance attribute. The applet argument is stored in the itsApplet attribute, on line 0043, and colorForBorder is passed as an argument to the inherited setForeground() action. The final step of the constructor is to enable mouse events on the TuttleButton in order that its processMouseEvent() action will be called every time the mouse is used within its extent. The addNotify() action will complete the initialisation of the TuttleButton, and is implemented as follows.

0050     public void addNotify() {  0051      0052     MediaTracker aTracker; 0053   0054        super.addNotify();      0055          0056        buttonImage = ( itsApplet.getImage(  0057                        itsApplet.getCodeBase(), imageSource));          0058        aTracker = new MediaTracker( this); 0059        aTracker.addImage( buttonImage, 0); 0060        try {  0061            aTracker.waitForID( 0); 0062        } catch ( InterruptedException exception) {        0063           // Do nothing!                                 0064        } // End try/ catch.  0065    0066        if ( buttonImage == null              || 0067             buttonImage.getWidth( this)  < 1 || 0068             buttonImage.getHeight( this) < 1 ){  0069           System.err.println( "The image " + imageSource +  0070                               "\nCould not be loaded, abending ...");  0071           System.exit( -1);   0072        } // End if.           0073        buttonWidth  = buttonImage.getWidth( this) + BORDER_WIDTH *2; 0074        buttonHeight = buttonImage.getHeight(this) + BORDER_WIDTH *2;  0075        this.setSize( buttonWidth, buttonHeight);     0076     } // End addNotify;   

The first part of this action, which loads the image from a file on the server which provided the applet, is essentially identical to the loading of an image in the previous chapter. It uses the itsApplet attribute as an argument in the getImage() call on line 0056, to load the file into the buttonImage Image attribute. Should the image not load successfully, for example if the file cannot be found, then the condition, on lines 0066 to 0068, will evaluate true and the program will abend. If the image is loaded successfully then the buttonWidth and buttonHeight attributes are initialised to the width and height of the image, plus an allowance for the border at each edge, and, on line 0075, the size of the Component is established based upon these dimensions. These attributes are used by the getMinimumSize() and getPreferredSize() actions, as follows.

0081     public Dimension getMinimumSize() { 0082        return( new Dimension( buttonWidth, buttonHeight)); 0083     } // End getMinimumSize. 0084   0085     public Dimension getPreferredSize() { 0086       return this.getMinimumSize(); 0087     } // End getPreferredSize.

The implementation of the update() and paint() actions are as follows.

0090     public void update( Graphics systemContext) { 0091        this.paint( systemContext); 0092     } // End update.  0093     0094     public void paint( Graphics systemContext) {  0095      0096     int index; 0097   0098        systemContext.drawImage( buttonImage, BORDER_WIDTH, BORDER_WIDTH, this); 0099        for ( index=0; index < BORDER_WIDTH; index++) {  0100           systemContext.draw3DRect( index, index, 0101                                     buttonWidth  - index -1, 0102                                     buttonHeight - index -1, !pressed);                                   0103        } // End for. 0104     } // End paint.

The update() action indirects to the paint() action for the reasons explained in the last chapter. The paint() action commences, on line 0098, by drawing the buttonImage into the button's window offset from the top left of the window by the width of the border. Once this has happened the border is drawn around the image as a sequence of concentric draw3Drect() calls. The last argument of a draw3Drect() call is a boolean which if true causes the rectangle to be drawn as if raised, and if false depressed. Those parts of the border shown as dark grey in Figure 5.2 are drawn using a darker hue of the component's foreground drawing colour, those parts of the border shown as light grey are drawn using a lighter hue. The negated value of the pressed attribute is used to supply this last argument and its value is controlled by the processMouseEvent(), as follows.

0107     protected void processMouseEvent( MouseEvent event) {         0108        switch ( event.getID()) {  0109                     0110        case MouseEvent.MOUSE_EXITED:  0111           pressed = false; 0112           repaint(); 0113           break; 0114            0115        case MouseEvent.MOUSE_PRESSED:  0116           pressed = true; 0117           repaint();         0118           break; 0119            0120        case MouseEvent.MOUSE_RELEASED:  0121           if ( (pressed)             &&  0122                (itsListener != null) ){ 0123             itsListener.actionPerformed( new ActionEvent( this,  0124                                              ActionEvent.ACTION_PERFORMED, 0125                                              this.getActionCommand()));                                                    0126           }  // End if. 0127           pressed = false; 0128           repaint();        0129           break;                       0130        } // End switch.      0131     } // End processMouseEvent.

The MOUSE_EXITED branch of the switch structure ensures that the pressed attribute is false and calls the repaint() action to, indirectly, cause the paint() action to be called. As pressed is false the last argument of the draw3Drect() action will be true and so the button will be drawn in a raised fashion. The MOUSE_PRESSED branch primes the button by setting the pressed attribute true and calls repaint() which will draw the button in a depressed state. Finally the MOUSE_RELEASED action, if the pressed attribute is true and an ActionListener has been registered, calls the actionPerformed() action of itsListener passing as an argument a new ActionEvent containing the TuttleButton's actionCommand. This branch concludes by setting the pressed attribute false and repainting the button. The list of ActionListeners in itsListeners and the commandString attribute are maintained by the remaining four actions, whose implementations are directly comparable with those in the DatePanel class from the previous chapters, as follows.

0134     public void setActionCommand( String command) { 0135         actionCommand = command; 0136     } // End setActionCommand. 0137       0138     public String getActionCommand() { 0139        if ( actionCommand == null) {  0140           return "Tuttle Button"; 0141        } else {  0142           return actionCommand; 0143        } // End if.    0144     } // End getActionCommand. 0145   0146   0147     public void addActionListener( ActionListener listener) { 0148        itsListener = AWTEventMulticaster.add( itsListener, listener); 0149     } // End addActionListener. 0150   0151     public void removeActionListener( ActionListener listener) { 0152        itsListener = AWTEventMulticaster.remove( itsListener, listener); 0153     } // End removeActionListener.

Before using instances of the TuttleButton in the semi-direct interface it should be demonstrated to be working correctly; a demonstration harness might be as follows.

0001  // Filename TuttleButtonDemonstration.java. 0002  // A demonstration test harness for the TuttleButton class. 0003  //  0004  // Written for the Java Interface book, Chapter 5. 0005  // Fintan Culwin, v 0.2, August 1997. 0006   0007   0008  import java.awt.*; 0009  import java.applet.*; 0010  import java.awt.event.*; 0011 0012  import Tuttles.TuttleButton; 0013   0014  public class TuttleButtonDemonstration extends    Applet  0015                                         implements ActionListener { 0016                                                               0017    public void init() { 0018   0019    TuttleButton leftTuttleButton; 0020    TuttleButton rightTuttleButton; 0021   0022       this.setBackground( Color.white); 0023        0024       leftTuttleButton  = new TuttleButton( "greyltutt.gif", this); 0025       leftTuttleButton.setActionCommand( "Left  button"); 0026       leftTuttleButton.addActionListener( this); 0027       this.add( leftTuttleButton); 0028        0029       rightTuttleButton = new TuttleButton( "greyrtutt.gif", this, Color.red); 0030       rightTuttleButton.setActionCommand( "Right button"); 0031       rightTuttleButton.addActionListener( this);      0032       this.add( rightTuttleButton);          0033    } // End init. 0034   0035   0036    public  void actionPerformed( ActionEvent event) {  0037       System.out.println( event.getActionCommand() + " pressed.");                      0038    } // End actionPerformed.                           0039  } // End class TuttleButtonDemonstration.

The init() action constructs two TuttleButtons, one initialised to display a grey left facing tuttle image ( greyltutt.gif ) with the default grey border and the other a grey right facing tuttle image ( greyrtutt.gif ) with a red border. These two GIF files are stored in the Images sub-directory of the directory on the server which contains the Tuttles package. Figure 5.2 illustrates the appearance of this demonstration harness.

The TuttleButtonDemonstration class implements the ActionListener interface and registers itself as the listener object for both buttons. Its actionPerformed() action will display a message on the console, containing the button's actionCommand, when each of the buttons is pressed. This indicates that the TuttleButtons are calling their registered listener object's actionPerformed() action when they are pressed by the user and that the action can determine which button was pressed. This client class does not have a main() action as the loading of the images from the server relies upon the top level window being an Applet, consequently the demonstration can only be run from a Web browser or an appletviewer utility.





5.2 The SemiDirectTuttle interface

Chapter 5 Semi-direct user interfaces

A Java GUI programmer's primer
Java GUI Programmers Primer, A
ISBN: 0139088490
EAN: 2147483647
Year: 1998
Pages: 85
Authors: Fintan Culwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net