Chapter 11

User Interface Classes


CONTENTS


tool \'tül \ n: an instrument or apparatus necessary in the practice of a vocation or profession

Introduction

Java's Abstract Windowing Toolkit (awt) package contains classes with which you can interact with the user of your programs. Though the awt classes are nice, there are no bells and whistles. This chapter is dedicated to providing you with those bells and whistles to extend the visual beauty of your intranet apps. The main thrust of this chapter is to introduce you to the interfaces and classes with which you can enhance your user interface. You will use these classes in your programs mainly to construct visual elements to enhance the look and feel of your Java programs. Before you dig into the classes, however, let's look at a little 3-D theory.

3-D Effects

Two features that I like to use in my GUI apps are 3-D panels and group boxes. They give a nice touch to any input screen and can transform a boring screen into one bubbling with personality. Core Java cannot create these hip containers, however. Enter the JifPanel class. With this class, you can create an array of interesting effects with the greatest of ease. Figures 11.1 and 11.2 show the different things a JifPanel can do. Screenshot : The versatile JifPanel. Screenshot : The JifPanel behaving like a group box.

Note
JIF stands for Java Intranet Framework. As you'll see in , "Putting Them All Together," all of the classes you've talked about, including those in this chapter, are packaged together into the Java Intranet Framework, or JIF. You will see other objects that start with JIF.

The JifPanel class is covered in greater detail later in this chapter. For now, let's discuss the interface behind the JifPanel class: Effects.

Note
Screenshot represents the output of one example program on the CD-ROM. The program is called PanelTester.

Note
Screenshot represents the output of one example program on the CD-ROM. The program is called GroupBoxTester.

The EffectsInterface

The Effects interface extends the components' look and feel. The Effects interface only consists of constants defining the styles available. Only the JifPanel class implements this interface. The Effects styles are divided into three areas:

Let's examine each set of styles individually.

Border Styles

The first set of styles is the border styles. These dictate how a component will render its border. The available styles are

Each of these styles is represented in Figure 11.1. Listing 11.1 shows the actual definitions of these styles.


Listing 11.1. The border styles available in the Effectsinterface.
public final static int NONE = 0;
public final static int FLAT = 1;
public final static int GROOVED = 2;
public final static int LOWERED = 4;
public final static int ROUNDED = 8;
public final static int RAISED = 16;
public final static int RIDGED = 32;
public final static int CAPTION = 64;

Notice that the styles are offsets in a bit-mapped field. That means that they can be added together to combine the effects. Some effects do not warrant combining and are pointless. For instance, you won't want to combine GROOVED with RIDGED, or LOWERED with RAISED. Sometimes you might want to combine RAISED and ROUNDED, however.

Text Styles

Text effects, the second set of styles defined by the Effects interface, dictate the way a component will draw the text it contains. Four styles are available:

Each of these styles is represented in Figure 11.1. Listing 11.2 shows the actual definitions of these styles.


Listing 11.2. The text styles available in the Effectsinterface.
public final static int TEXT_NORMAL = 0;
public final static int TEXT_LOWERED = 1;
public final static int TEXT_RAISED = 2;
public final static int TEXT_SHADOW = 3;

Unlike the border styles, these styles increment sequentially. They cannot be combined.

Text Placement

The third and final style set is the text placement styles. While not styles so much as locations, they indicate one aspect of the display of the text. Three styles are available:

Again, please refer to Figure 11.1. Each of the preceding styles is demonstrated in the example. Listing 11.3 shows the actual definitions of these styles.


Listing 11.3. The text placements available in the Effectsinterface.
public final static int CENTER = 0;
public final static int LEFT = 1;
public final static int RIGHT = 2;

Like the text styles, these placement styles increment sequentially. They also cannot be combined.

The JifPanelClass

The most important user interface class you'll develop is the JifPanel class, which implements the Effects interface and provides many 3-D effects along with some special features. To fully explain the JifPanel, this section discusses its design and then covers some of the constructors and the source code.

JifPanelDesign

The JifPanel extends Java's Panel class. As you know, the Panel class extends the Container class, which allows the Panel to hold or contain other components. You can assemble this panel of components, along with other panels of components, to construct a complete user interface for an app. The following is the declaration of the JifPanel class:

//****************************************************************************
//* JifPanel &n bsp; *
//****************************************************************************

public class JifPanel
extends Panel
implements Effects

The JifPanel implements the Effects interface. As the Effects interface only contains constants, it imposes no structure on the JifPanel class. All of the constants available in Effects are available to the JifPanel, however.

Constructing a JifPanel

You can construct a JifPanel in several ways. You can use it as a regular Java Panel by creating it with no arguments:

JifPanel myPanel = new JifPanel();

You can create one that has a specific type of border:

JifPanel myPanel = new JifPanel( Effects.RAISED );

You can specify a fixed size for your panel:

JifPanel myPanel = new JifPanel( Effects.RAISED, 100, 100 );

You can create one that contains a text string:

JifPanel myPanel = new JifPanel( "JIF Rules!!" );

Finally, you can create one with a cool border that is of a fixed size with text inside:

JifPanel myPanel = new JifPanel( Effects.RAISED, 100, 100, "JIF Rules!!" );

Hopefully there are enough options from which you can choose. If there aren't, you can always subclass the JifPanel and create your own extensions. Once again, refer to Figure 11.1 for a gander at the different border, text, and text placement styles available for a JifPanel.

Smoke and Mirrors

If you're at all like me, you are probably ready to look at some source code. I'm sure you're wondering how to create those cool 3-D border and text effects. It doesn't take smoke and mirrors or a degree from a clown college. It's quite simple, and I'll show you how.

Drawing 3-D Borders

A 3-D border around a component is one of the easiest and most professional looking effects to program. Drawing a 3-D border around any component requires two details: border style and border thickness. The first step is to decide on the style of the 3-D border you want to draw. The two basic styles are lowered and raised.

Lowered Borders
A lowered border is a border that looks sunken or lower than the area that contains it. Figure 11.3 illustrates the concept of a lowered border.

Screenshot : A lowered border.

To make an object appear as if it is sunken, you need to create what appears to be a shadow. To do this, the upper and left side of your component should be darker than the lower and right side. In Figure 11.3, a 10¥10 grid represents a 10¥10 grid of pixels. The dark pixels represent the area in the shadow. The clear, or white, pixels represent the area that is lit up. If you squint your eyes and look at the figure, it will look lowered into the tutorial.
Raised Borders
A raised border is a border that looks as if it is coming off of the screen. This is a common look for buttons and other clickable objects. Figure 11.4 illustrates the concept of a raised border.

Screenshot : A raised border.

To make the object appear as if it is raised, you need to create a shadow at the bottom of the object. To do this, the upper and left side of your component should be lighter than the lower and right side. In Figure 11.4, a 10¥10 grid represents a 10¥10 grid of pixels. The dark pixels represent the area in the shadow. The clear, or white, pixels represent the area that is lit up. Again, if you squint your eyes and look at the figure, it will look raised from the tutorial.
Grooved and Lowered Borders
The grooved and lowered border types are worth mentioning. You can create group boxes with these two borders. Figure 11.5 illustrates the method for creating a grooved and lowered border.

Screenshot : A grooved border.

Screenshot illustrates the method for creating a lowered border.

Screenshot : A lowered border.

Border Thickness
The last detail you need before you dig into the code is the thickness of the border. The thickness of the border is the width of the border in pixels. The default thickness for a JifPanel is two pixels.
Thicker borders look odd, and borders thinner than two pixels almost defeat the purpose of the 3-D effect.
Drawing the Border
The drawFrame() method in the JifPanel class is responsible for drawing the 3-D border. The source code for that method follows:
//****************************************************************************
//* drawFrame & nbsp; *
//****************************************************************************

public void
drawFrame( Graphics g )
{
int i, j, offset = 0, rounded = 0;
Dimension bounds = size();

if ( isStyleSet( CAPTION ) )
{
// Fix up for font...
FontMetrics fm = g.getFontMetrics();
offset = fm.getHeight() / 2;
}

for ( i = 0, j = 1; i < thickness; i++, j += 2 )
{
if ( i != 0 )
rounded = 0;
else
if ( isStyleSet( ROUNDED ) )
rounded = 1;

if ( isStyleSet( RAISED ) ||
( isStyleSet( RIDGED ) && ( j <
thickness ) ) ||
( isStyleSet( GROOVED ) && ( j >
thickness ) ) )
g.setColor( Color.white );
else
g.setColor( Color.gray );
// Draw top line...
g.fillRect( i + rounded,
i + offset,
bounds.width - ( 2 * ( i + rounded ) ),
1 );

// Draw the left side...
g.fillRect( i,
i + rounded + offset,
1,
bounds.height - ( 2 * i + 1 + rounded ) - offset );

if ( isStyleSet( RAISED ) || isStyleSet(
FLAT ) ||
( isStyleSet( RIDGED ) && ( j < thickness ) ) ||
( isStyleSet( GROOVED ) && ( j >
thickness ) ) )
g.setColor( Color.gray );
else
g.setColor( Color.white );

// Draw the bottom line...
g.fillRect( i + rounded,
bounds.height - ( i + 1 ),
bounds.width - ( 2 * ( i + rounded ) ),
1 );

// Draw the right side...
g.fillRect( bounds.width - ( i + 1 ),
i + rounded + offset,
1,
bounds.height - ( 2 * i + 1 + rounded )
- offset );
}
}
The drawFrame() method draws the border in a loop fashion. Each run through represents one pixel from the thickness of the border. If the border thickness was set to two, the default, this loop would be performed twice.
This method first draws the upper and left lines of the border. It then swaps colors and draws the lower and right lines of the border. Each time through the loop, it thickens the border by one pixel.
Ridged and grooved borders are a little bit more complex. They swap colors halfway through the loop, giving them a walled or grooved look.

Drawing 3-D Text

Drawing 3-D text isn't nearly as complicated as drawing the 3-D borders. Making the text look lowered or raised is very simple. The following source code is for the JifPanel method drawtext():

//****************************************************************************
//* drawtext &n bsp; *
//****************************************************************************

public void
drawtext( Graphics g )
{
Color oldColor = g.getColor();
int xx = 0, yy = 0;
FontMetrics fm = g.getFontMetrics();
Dimension bounds = size();

// Decide where to place the text...
if ( !isStyleSet( CAPTION ) )
{
switch ( textPlacement )
{
case CENTER:
xx = ( bounds.width / 2 ) - ( fm.stringWidth( text ) / 2 );
yy = ( bounds.height / 2 ) - ( fm.getHeight() / 2 );
break;

case LEFT:
xx = thickness + TEXT_OFFSET;
yy = ( bounds.height / 2 ) - ( fm.getHeight() / 2 );
break;

case RIGHT:
xx = bounds.width - thickness - TEXT_OFFSET -
fm.stringWidth( text );

yy = ( bounds.height / 2 ) - ( fm.getHeight() / 2 );
break;
}
}
else
{
int spacer = fm.charWidth( 'i' );

xx = thickness + TEXT_OFFSET + spacer;
yy = 0;

// Fill a rectangle in the bounding space of the string...
g.setColor( getBackground() );
g.fillRect( xx, yy,
fm.stringWidth( text ) + ( spacer * 2 ),
fm.getHeight() );

xx += spacer;
}

// Adjust for drawString weirdness...
yy += fm.getHeight() - fm.getDescent() - 1;

if ( textStyle == TEXT_LOWERED )
{
// Draw highlight to right and below text
g.setColor( Color.white );
g.drawString( text, xx + 1, yy + 1 );
}

if ( textStyle == TEXT_RAISED )
{
// Draw highlight to left and above text
g.setColor( Color.white );
g.drawString( text, xx - 1, yy - 1 );
}

if ( textStyle == TEXT_SHADOW )
{
// Draw shadow to right and below text
g.setColor( Color.gray );
g.drawString( text, xx + 1, yy + 1 );
}

g.setColor( Color.black );
g.drawString( text, xx, yy );

// Restore the old color...
g.setColor( oldColor );
}

The first half of this method sets the starting point for the text to be drawn. Based on the text placement, the x and y coordinates for drawing the text are calculated. For CENTER, the text is centered in the width of the component. For LEFT, the text is offset by five pixels. For RIGHT, the text is offset by five pixels from the right side of the component. Now, if the CAPTION style is set, you need special processing because the CAPTION style requires a background to be drawn before the text is drawn. The method calculates the size of the rectangle that will be drawn. This rectangle will be a little wider than the text itself but exactly the same height. The second half of this method draws the specialty text, which is the lowered, raised, and shadowed text styles. These effects are achieved simply by drawing a copy of the text in white and then drawing the text again in black, offset by one pixel. This offset is important. If you offset up and over one, you get that lowered look. If you offset down and to the right, you get a raised look. The shadowed text is just like the lowered text, but the background color is gray instead of white. Finally, you restore the old color to the Graphics canvas. Others may depend on this color being there. If you don't do this, subsequent drawing of other components may be colored incorrectly.

Tabbing Between Components

Another feature of the JifPanel class is that you can tab between the components contained in the panel. This behavior of tabbing from one component to another is a GUI standard that many have come to feel comfortable with. But creating the effect is quite simple as you'll see.

Note
As of the JDK v1.0.2 for Windows NT and Windows 95, this tabbing feature was not available and has been reported as a bug. I have added it here for this reason. If later versions of the JDK support tabbing, this feature of the JifPanel will no longer be necessary.

You took over the event handler in our JifPanel class and looked for the pressed Tab key. The Tab key's value is 9, and you created a constant called TAB_KEY to hold that value:

final static public int TAB_KEY = 9;

The instance variable called currentPos keeps track of the component that currently has the focus. When you press a key on the keyboard, a KEY_PRESS event is generated. Capturing keystrokes is as easy as overriding the handleEvent() method. When you press the Tab key, you check to see if the target of the key press is a component. If it is, you jump the focus to the component directly following the target in the creation order. If you hold down the Shift key when you press the Tab key, you jump backward one component. This handles the jumping from the Tab key. One last thing you need to do is keep track of mouse clicks. When the user clicks the mouse on a component, that component receives the focus and then generates a GOT_FOCUS event. You capture this event as well and set your internal pointer, currentPos, to point at this component.

Caution
As of the JDK v1.0.2 specifically on the Windows NT and Windows 95 platform, components did not properly generate the GOT_FOCUS and LOST_FOCUS events when they received and lost the focus. This appears to be a bug in the implementation, but it might turn out that some components never will generate these events.
In any case, the behavior of tabbing can be affected by the user clicking on a component out of the tab order because of this bug. You might see the Tab key cause the cursor to jump around.

The following is the source code for the handleEvent() method of JifPanel:

//****************************************************************************
//* handleEvent ; *
//****************************************************************************

public boolean
handleEvent( Event event )
{
// Look for certain events to move the focus...
if ( ( event.id == Event.GOT_FOCUS || event.id == Event.ACTION_EVENT )
&& event.target instanceof Component )
{
// Set the focus to this comoponent...
setFocus( ( Component )event.target );
}

// Handle tabs nicely...
if ( event.id == Event.KEY_PRESS && event.key == TAB_KEY )
{
if ( event.target instanceof Component )
{
if ( !event.shiftDown() )
focusForward( ( Component )event.target );
else
focusBackward( ( Component )event.target );
}

// I handled it...
return( true );
}

// I don't want this...
return( super.handleEvent( event ) );
}

The two methods, focusForward() and focusBackward(), move the focus to the next or previous component, respectively. These two methods skip Labels and disabled components. You don't want an inert object to get the focus; you'll never get the focus back.

SQL Generation

The last feature of the wonderful JifPanel is that it can generate a SQL statement suitable for sending to a database. This feature only works when the panel itself contains other components that implement the SQLFactory interface, however. (See Chapter 10, "Database Classes," for more information on SQLFactory.) When you request a JifPanel to generate a SQL statement for the components contained within, the JifPanel queries each component for some information. First and foremost, the panel needs to know if the component implements the SQLFactory interface. If it does not, there is really no way to generate SQL for that component. To see whether a class implements an interface, the operator instanceof returns true. The following code shows this in action:

// Is this a non-mundane component?
if ( cList[ i ] instanceof SQLFactory )
{
// Get the SQL statement...
String sql = ( ( SQLFactory )cList[ i ] ).generateUpdateSQL( false );
}

The cList is an array of the components contained within the panel. You check each element in the array with the instanceof operator. If the component implements the SQLFactory interface, you then can call its generateUpdateSQL() method by casting the component array variable to that of a SQLFactory object. In any case, the JifPanel will return complete INSERT or UPDATE SQL statements if filled with components that implement the SQLFactory interface. These strings are suitable for sending directly to your database.

The JifPanelDescendants

Five classes descend from the JifPanel class: CalendarPanel, ImagePanel, JifLabel, JifTabPanel, and StatusBar. Let's look at each one in detail.

The CalendarPanelClass

As the name implies, the CalendarPanel represents a calendar. (See Figure 11.7.) The calendar starts at a certain month and can be moved forward or backward one month at a time. Each day is implemented as a button that the user can press. Screenshot : The CalendarPanel as used in the CalenderTester program. When the user presses one of the days, an ACTION_EVENT is sent to the parent of the object. This event contains the date selected in a Java Date object as the argument. Listing 11.4 is from the CalendarTester program. It creates and displays a CalendarPanel object for you to play with. The complete source code is available on the CD-ROM. The output of the CalendarTester program is shown in Figure 11.8. Screenshot : The output of the CalenderTester program.


Listing 11.4. A code snippet from the CalendarTssesterprogram.
//****************************************************************************
//* CalendarTester &n bsp; *
//****************************************************************************

public
CalendarTester()
{
super( "Calendar Tester" );

// Let's use a nicer font...
setFont( new Font( "Helvetica", Font.PLAIN, 14 ) );

// Create the calendar...
myPanel = new CalendarPanel();
add( "Center", myPanel );

// Pack it up!
pack();
show();
}

//****************************************************************************
//* action &nbs p; *
//****************************************************************************

public boolean
action( Event e, Object a )
{
// Was this my boy?
if ( e.target == myPanel )
{
// Tell the user!
MessageBox mb = new MessageBox( this, "Hey!",
"You picked " + ( ( Date )a ).toString() );

mb.show();
return( true );
}

return( false );
}

}

This program simply creates a CalendarPanel object and adds it to the center of its BorderLayout. The ACTION_EVENT event tells you that someone clicked a button. So, you've added an action() method to receive these ACTION_EVENT events. When the events are received, you verify that they were generated from your CalendarPanel. If all checks out, you notify the user that a day has been picked with another cool class-the MessageBox. This class is discussed later in this chapter.

The ImagePanelClass

The ImagePanel class is an extension of the JifPanel class that loads an image and resizes itself to the size of the image. This is useful for many apps, including in the JifDialog descendant class MessageBox. Later in this chapter you'll see more about the MessageBox class. ImagePanels are constructed in the following manner:

ImagePanel ip = new ImagePanel( "c:\\image.gif", 15 );

or

ImagePanel ip = new ImagePanel( "c:\\image.gif );

Each constructor takes as the first argument the name of the graphic file to load. Whatever image types Java supports, the ImagePanel also supports. Currently, only GIF and JPEG image formats are supported. The second, optional, argument allows to you specify a pixel border between the edge of the panel and where the image is drawn. The default is five pixels.

The JifLabelClass

One shortcoming of Java's Label class is that it can only nicely display a single line of text. If you put a second line in there, separated by a line feed perhaps, you get an ugly display on the screen. The JifLabel fills this niche. It creates a multiline label in an easy manner. Another Java tutorial that includes an excellent multiline label class is on the market, so I tried to do one better. The other multiline label class depends on when you created the components and manages the size of the text. Ugh! While elegant and visually pleasing, it was too much work. I like the KISS principle of software construction: Keep It Simple, Stupid. On that note, I present the entire source code for the JifLabel:

//****************************************************************************
//* JifLabel &n bsp; *
//****************************************************************************

public class
JifLabel
extends JifPanel
{

//****************************************************************************
//* Constructors &nbs p; *
//****************************************************************************

public
JifLabel( String s )
{
// Stuff the string in a tokenizer to get at each line...
StringTokenizer st = new StringTokenizer( s, "\n" );
int tCount = st.countTokens();

// Make a grid...
setLayout( new GridLayout( tCount + 2, 1, 0, 0 ) );

// Leave some space...
add( new Label( " " ) );

// Make each line a new label and add to layout...
for ( int i = 0; i < tCount; i++ )
add( new Label( st.nextToken() ) );

// Leave some space...
add( new Label( " " ) );
}

}

The JifLabel is extremely simple. First, you break the string into bits on the new-line (\n) boundaries. Using Java's StringTokenizer makes this task very simple. Second, you store the number of lines of text in the variable tCount to create a new layout for the label. You create a grid layout that is tCount plus two rows high and one column wide. The extra two rows are spacer rows to make the JifLabel look even nicer. Now you are ready to construct the actual label. Add a spacer label at the top. Next, spin through a loop and take out each line from the tokenizer. Each line you take out is added to the layout as a new Label. Lastly, add a new spacer to the bottom. Pretty simple, eh? I think you'll like the results considering the code that created it. Figure 11.9 shows a sample JifLabel in the output of the LabelTester program. Screenshot : The output of the LabelTester program. The following code is part of the LabelTester program so you can see how easy it is to create JifLabels:

public
LabelTester()
{
super( "Label Tester" );

String labelString =
"Hi, this is the versatile JifLabel!\n" +
"It is a multi-line label that was\n" +
"very easy to program!\n\n" +
"What do you think?";

JifLabel myLabel = new JifLabel( labelString );
myLabel.setFont( new Font( "Helvetica", Font.BOLD, 14 ) );
add( "Center", myLabel );

// Pack it up!
pack();
show();
}

The JifTabPanelClass

Another shortcoming of the stock Java widgets is the lack of a tabbed panel, or folder widget. This widget presents information one sheet at a time. This new metaphor is common in Microsoft Windows and is becoming popular on other platforms as well. Oracle noted that this would catch on, so they provided the CardLayout layout manager. This layout presents a stack of components with only one component visible at a time. You must bring one card in the stack to the top to view it. The JifTabPanel class has used this layout. This class provides a simple Microsoft Windows-like tabbed panel for your apps. This simple device uses a BorderLayout for its components. The JifTabPanel class consists of a JifPanel, a row of tabs from which to select in the North layout, and a CardLayout of components in the center layout. The JifTabPanel is limited in that it only provides a single layer of tabs. (See Figure 11.10.) Screenshot : The output of the TabTester program. The following code is from the TabTester program. You can see how easy it is to use:

public
TabTester()
{
super( "Tab Tester" );

// Create a Tab panel...
JifTabPanel myPanel = new JifTabPanel();

// Add some panes to it...
myPanel.addPane( "Pane 1",
new JifPanel( JifPanel.RAISED, 275, 375, "Panel 1" ) );
myPanel.addPane( "Pane 2",
new JifPanel( JifPanel.RAISED, 275, 375, "Panel 2" ) );
myPanel.addPane( "Pane 3",
new JifPanel( JifPanel.RAISED, 275, 375, "Panel 3" ) );
myPanel.addPane( "Pane 4",
new JifPanel( JifPanel.RAISED, 275, 375, "Panel 4" ) );
myPanel.addPane( "Pane 5",
new JifPanel( JifPanel.RAISED, 275, 375, "Panel 5" ) );

// Add to the center...
add( "Center", myPanel );

// Pack it up!
pack();
show();
}

The StatusBarClass

The final JifPanel extension is the StatusBar class, the area at the bottom of our model intranet app (see Chapter 7, "A Model Intranet app," for more information). You can use the StatusBar class to display messages to the user. If you leave it at its default value, it simply says "Ready." You can set it to anything you want, however. The StatusBar class has only a single method useful to your program: the clear() method. This method sets the text to nothing so the status bar shows nothing. It relies on the setText() method of the parent class, which is used to change the text displayed in the status bar.

The JifDialogClass

The next major user interface class is the JifDialog class. This class extends the basic Java Dialog class and adds a few cool behaviors:

You would have to implement these three features for each dialog box you create, so why not put them in a centralized base class? That's what the JifDialog class is-a centralized location. A method also included in this class, center(), centers the dialog box within either the screen or the window that owns it.

Note
The Windows NT/95 JDK v1.0.2 has a bug that causes all windows to report their x,y location as 0,0. This makes it impossible to center the dialog box within the parent window. Therefore, the parent centering will not work until the bug is fixed or used on another platform.

Three classes descend from JifDialog: MessageBox, ResponseDialog, and PickList. All of these classes, except PickList, are demonstrated in the DialogTester program. The source code for this program is shown in Listing 11.5.


Listing 11.5. The DialogTesterprogram.
//****************************************************************************
//* DialogTester &nbs p; *
//****************************************************************************

public class
DialogTester
extends Frame
{

//****************************************************************************
//* Members &nb sp; *
//****************************************************************************

ResponseDialog myDialog;

//****************************************************************************
//* main *
//****************************************************************************

public static void
main( String args[] )
{
new DialogTester( args );
}

//****************************************************************************
//* Constructor ; *
//****************************************************************************

public
DialogTester( String args[] )
{
super( "Dialog Tester!" );

JifPanel p = new JifPanel( Effects.LOWERED );
p.setLayout( new FlowLayout() );

p.add( new Button( "Plain MessageBox" ) );
p.add( new Button( "Info MessageBox" ) );
p.add( new Button( "Stop MessageBox" ) );
p.add( new Button( "Exclamation MessageBox" ) );
p.add( new Button( "Question MessageBox" ) );
p.add( new Button( "ResponseDialog" ) );

// Add the timer panel to the frame...
add( "Center", p );

// Pack the panels...
pack();
show();
}

//****************************************************************************
//* action &nbs p; *
//****************************************************************************

public boolean
action( Event event, Object arg )
{
MessageBox mb = null;

if ( arg.equals( "Plain MessageBox" ) )
{
mb = new MessageBox( this, "Plain MessageBox",
"This is a plain message box" );
}
else if ( arg.equals( "Info MessageBox" ) )
{
mb = new MessageBox( this, "Info MessageBox",
"This is an info message box",
MessageBox.INFO );
}
else if ( arg.equals( "Stop MessageBox" ) )
{
mb = new MessageBox( this, "Stop MessageBox",
"This is an stop message box",
MessageBox.STOP );
}
else if ( arg.equals( "Exclamation MessageBox" ) )
{
mb = new MessageBox( this, "Exclamation MessageBox",
"This is an exclamation message box",
MessageBox.EXCLAMATION );
}
else if ( arg.equals( "Question MessageBox" ) )
{
mb = new MessageBox( this, "Question MessageBox",
"This is an question message box",
MessageBox.QUESTION );
}
else if ( arg.equals( "ResponseDialog" ) )
{
myDialog = new ResponseDialog( this, "ResponseDialog",
"This is a response dialog with three buttons",
"Yes,No,Cancel" );

myDialog.show();
return( true );
}

if ( mb != null )
{
mb.show();
return( true );
}

if ( event.target == myDialog )
{
MessageBox mb2 = new MessageBox( this, "Response Received!",
"A response was received from the response dialog!\n" +
"You pressed button #" + ( ( Integer )arg ).toString() );

mb2.show();
return( true );
}

System.out.println( event.toString() );

return( false );
}

}

The PickList dialog, an abstract class, is demonstrated in its own example program.

The MessageBoxClass

The MessageBox class displays a message to the user and has a single button along the bottom of the dialog box. This button is usually an OK button. It allows the user to dismiss the dialog. As an option, the MessageBox can show an image along the left edge of the dialog box to further enhance it. Figure 11.9, the output from LabelTester, illustrates a MessageBox without an image. Figure 11.11 shows a MessageBox with an image. Screenshot : A MessageBox with the STOP image. You can display four stock images, or icons, in your message box. Each image is represented by a constant defined in the MessageBox class. The stop sign shown in Figure 11.11 is just one of those. The other three are as follows:

The constants are defined as follows:

public static final String INFO = "Information.gif";
public static final String EXCLAMATION = "Exclamation.gif";
public static final String STOP = "Stop.gif";
public static final String QUESTION = "Question.gif";
Note
The images that come with JIF are from Windows 95. Microsoft Windows has an API function called MessageBox that generates similar output. This class closely models the Microsoft Windows message box.

The constructor for the MessageBox follows:

MessageBox( Frame parent, String title, String message
[, String imageToUse[, boolean addButtons ] )

In the preceding code:

parent is the parent frame.
title is the title of the message box.
message is the message to be displayed to the user.
imageToUse is an optional constant indicating which image to display. If not specified, it defaults to MessageBox.INFO.
addButtons is a boolean with which you can have the message box create itself without an OK button along the bottom. With this feature, you can extend its functionality.

Some examples of the MessageBox's creations follow:

MessageBox mb = new MessageBox( this, "Plain MessageBox",
"This is a plain message box" );

MessageBox mb = new MessageBox( this, "Stop MessageBox",
"This is an stop message box",
MessageBox.STOP );

MessageBox mb = new MessageBox( this, "Info MessageBox",
"This is an info message box",
MessageBox.INFO );

MessageBox mb = new MessageBox( this, "Exclamation MessageBox",
"This is an exclamation message box",
MessageBox.EXCLAMATION );

MessageBox mb = new MessageBox( this, "Question MessageBox",
"This is an question message box",
MessageBox.QUESTION );

The MessageBox is quite a versatile class. As you'll see, it can help out in almost any situation.

Tip
In order for the MessageBox to work properly, the image files must reside in the same directory as your program. Otherwise, the image loader will not be able to find them.

The ResponseDialog Class

The ResponseDialog is the only descendant of the MessageBox class. This class extends the base class and adds a user-specified number of buttons. Furthermore, the ResponseDialog returns the number of the button that was pressed via an ACTION_EVENT event. The following code is the constructor for the ResponseDialog:

public
ResponseDialog( Frame parent, String title, String message,
String buttons )

In the preceding code

parent is the parent frame.
title is the title of the message box.
message is the message to be displayed to the user.
imageToUse is an optional constant indicating which image to display. If an image is not specified, the constant defaults to MessageBox.INFO.
buttons is the comma-separated list of button names to create along the bottom of the dialog.
Tip
With this button freedom, you can create all sorts of dialog boxes: Yes/No, Yes/No/Cancel, Maybe, and so on. The choice is yours, and there is no need to create a new class. This class is completely usable as is.

To notify you of which button was pressed, the ResponseDialog generates an ACTION_EVENT event with an argument of the zero-based index of the button. For example, if you create a dialog with the buttons OK and Cancel and the user presses the OK button, the ACTION_EVENT event generated would have an argument of 0. If the user pressed the Cancel button, the argument would be 1. If the user closes the box without pressing a button, the ResponseDialog generates an argument of -1. This gives you total control to interpret the result.

The PickListClass

The last descendant from JifDialog is the PickList class. This abstract class is a framework for creating pick lists of data. Figure 11.12 shows the pick list from the employee maintenance app. Screenshot : The employee pick list. To complete the class, thus making it usable, the derived class must supply the init() method. This should initialize the List object contained within the pick list. The init() source code for the employee pick list is shown in Listing 11.6.


Listing 11.6. The init()source code for the employee pick list.
//****************************************************************************
//* init *
//****************************************************************************

public void
init()
{
int rows = retrieveEmployees();
}

//****************************************************************************
//* retrieveEmployees &n bsp; *
//****************************************************************************

int
retrieveEmployees()
{
String sql;
boolean rv = false;
int rows = 0;

sql = "select * from emp_t order by last_name, first_name";

try
{
rv = myConnection.getStatement().execute( sql );
}
catch ( SQLException e )
{
// No employees to return...
return( 0 );
}

// Is this a result set?
if ( rv )
{
try
{
ResultSet rs = myConnection.getStatement().getResultSet();

// Spin through the results and add them to the list...
while ( rs.next() )
{
EmployeeRecord er = new EmployeeRecord( rs );

// Add to list...
if ( er.emp_id != -1 )
{
myList.addItem( er.nice_name );

// Add to row mapper...
rowMap.insertElementAt( er, rows );

// Increment row counter...
rows++;
}
}
}
catch ( SQLException e )
{
// Indicate an error!
rows = -1;
}
}

// We're done!
return( rows );
}

As you can see, the init() method calls another method called retrieveEmployees(). This method retrieves a list of employees from the database and loads them into the list. The PickList generates an ACTION_EVENT event to the owner of the pick list when the user clicks on a row or presses a button.

Java TextComponentExtensions

The last two user interface classes are JifTextArea and JifTextField. These classes extend the default Java classes, TextArea and TextField, by implementing the SQLFactory interface from our database classes (see Chapter 10, "Database Classes," for more information). This interface, in conjunction with the JifPanel class, generates SQL code from a container of these text objects. The two classes also add change-detection functionality.

Change Detection

To generate correct SQL, the component needs to know whether the data that it contains has changed. If it has not changed, no SQL is needed. If the data has changed, however, a SQL statement should be generated. To handle this, the JifTextArea and JifTextField classes contain an instance variable called dataChange. This Boolean declaration follows:

protected boolean dataChange = false;

Whenever there is a change to the component's text, this variable is set to true and an ACTION_EVENT event is sent to the owner of this component. This way, the parent container can act on the notice as well. The didDataChange() method determines whether there has been a change. The source follows:

//****************************************************************************
//* didDataChange &nb sp; *
//****************************************************************************

public boolean
didDataChange()
{
return( dataChange );
}

You can call this method from anywhere.

Summary

This chapter introduced you to many of the user interface classes that you've created. You learned about many user interface classes that extend Java. These classes provide your intranet apps with some really cool widgets for which your users will thank you. With these classes you also can generate cool 3-D effects in addition to awesome Dialog extensions that really simplify a lot of coding efforts. All of these classes can be used to extend and enhance your intranet apps. In the next chapter, "Putting Them All Together," you combine all the classes you've learned about into packages. In addition, you create a new package that contains some classes that uses all of those classes.


Java ScreenshotJava ScreenshotJava ScreenshotJava Screenshot



Comments